From 7724bb88d511965308a3b953a4cfa4cfe4eb4d53 Mon Sep 17 00:00:00 2001 From: loshz Date: Wed, 10 Jan 2024 12:15:36 +0000 Subject: [PATCH] [NET-6593] agent: check for minimum RSA key size (#20112) * agent: check for minimum RSA key size * add changelog * agent: add test for RSA generated key sizes * use constants in generating priv key func * update key size error message --- .changelog/20112.txt | 3 ++ agent/auto-config/auto_encrypt_test.go | 40 ++++++++++++++++++++++++++ agent/auto-config/tls.go | 7 ++++- agent/connect/generate.go | 14 +++++++-- 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 .changelog/20112.txt diff --git a/.changelog/20112.txt b/.changelog/20112.txt new file mode 100644 index 0000000000..99a2a2ecd0 --- /dev/null +++ b/.changelog/20112.txt @@ -0,0 +1,3 @@ +```release-note:security +Update RSA key generation to use a key size of at least 2048 bits. +``` diff --git a/agent/auto-config/auto_encrypt_test.go b/agent/auto-config/auto_encrypt_test.go index 10a7c8da46..d076808024 100644 --- a/agent/auto-config/auto_encrypt_test.go +++ b/agent/auto-config/auto_encrypt_test.go @@ -7,6 +7,7 @@ import ( "context" "crypto/x509" "crypto/x509/pkix" + "encoding/pem" "fmt" "net" "net/url" @@ -107,6 +108,45 @@ func TestAutoEncrypt_generateCSR(t *testing.T) { } } +func TestAutoEncrypt_generateCSR_RSA(t *testing.T) { + testCases := []struct { + name string + keySize int + expectedKeySize int + }{ + { + name: "DefaultKeySize", + keySize: 0, + expectedKeySize: 4096, + }, + { + name: "KeySize2048", + keySize: 2048, + expectedKeySize: 2048, + }, + } + + for _, tcase := range testCases { + t.Run(tcase.name, func(t *testing.T) { + ac := AutoConfig{config: &config.RuntimeConfig{ + ConnectCAConfig: map[string]interface{}{ + "PrivateKeyType": "rsa", + "PrivateKeyBits": tcase.keySize, + }, + }} + + // Generate a private RSA key. + _, key, err := ac.generateCSR() + require.NoError(t, err) + + // Parse the private key and check it's length. + pemBlock, _ := pem.Decode([]byte(key)) + priv, _ := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + require.Equal(t, tcase.expectedKeySize, priv.N.BitLen()) + }) + } +} + func TestAutoEncrypt_hosts(t *testing.T) { type testCase struct { serverProvider ServerProvider diff --git a/agent/auto-config/tls.go b/agent/auto-config/tls.go index 8142a1eeb8..dd2d6f9e25 100644 --- a/agent/auto-config/tls.go +++ b/agent/auto-config/tls.go @@ -241,7 +241,12 @@ func (ac *AutoConfig) generateCSR() (csr string, key string, err error) { conf.PrivateKeyType = connect.DefaultPrivateKeyType } if conf.PrivateKeyBits == 0 { - conf.PrivateKeyBits = connect.DefaultPrivateKeyBits + // If using an RSA key, a key size of at least 2048 bits is recommended; 4096 bits is better. + if conf.PrivateKeyType == connect.PrivateKeyTypeRSA { + conf.PrivateKeyBits = connect.DefaultPrivateKeyBitsRSA + } else { + conf.PrivateKeyBits = connect.DefaultPrivateKeyBits + } } // Create a new private key diff --git a/agent/connect/generate.go b/agent/connect/generate.go index 84c91a2468..4679e2057b 100644 --- a/agent/connect/generate.go +++ b/agent/connect/generate.go @@ -21,6 +21,11 @@ const ( DefaultPrivateKeyType = "ec" DefaultPrivateKeyBits = 256 DefaultIntermediateCertTTL = 24 * 365 * time.Hour + + // RSA specific settings. + PrivateKeyTypeRSA = "rsa" + MinPrivateKeyBitsRSA = 2048 + DefaultPrivateKeyBitsRSA = 4096 ) func pemEncode(value []byte, blockType string) (string, error) { @@ -35,6 +40,11 @@ func pemEncode(value []byte, blockType string) (string, error) { func generateRSAKey(keyBits int) (crypto.Signer, string, error) { var pk *rsa.PrivateKey + // Check for a secure key length. + if keyBits < MinPrivateKeyBitsRSA { + return nil, "", fmt.Errorf("error generating RSA private key: invalid key size %d, must be at least %d bits", keyBits, MinPrivateKeyBitsRSA) + } + pk, err := rsa.GenerateKey(rand.Reader, keyBits) if err != nil { return nil, "", fmt.Errorf("error generating RSA private key: %s", err) @@ -87,9 +97,9 @@ func generateECDSAKey(keyBits int) (crypto.Signer, string, error) { // GeneratePrivateKey generates a new Private key func GeneratePrivateKeyWithConfig(keyType string, keyBits int) (crypto.Signer, string, error) { switch strings.ToLower(keyType) { - case "rsa": + case PrivateKeyTypeRSA: return generateRSAKey(keyBits) - case "ec": + case DefaultPrivateKeyType: return generateECDSAKey(keyBits) default: return nil, "", fmt.Errorf("unknown private key type requested: %s", keyType)