From 1c140cc6f99bdd2a072f5a58d92f9ae32cba7e8e Mon Sep 17 00:00:00 2001 From: Gabriel Santos Date: Tue, 5 Apr 2022 23:42:26 +0100 Subject: [PATCH 1/7] encrypted private key support for TLSAuth --- lib/options.go | 37 ++++++++++++-- lib/options_test.go | 119 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 3 deletions(-) diff --git a/lib/options.go b/lib/options.go index c4bbf8e24d7..f558bf9ef66 100644 --- a/lib/options.go +++ b/lib/options.go @@ -22,7 +22,9 @@ package lib import ( "crypto/tls" + "crypto/x509" "encoding/json" + "encoding/pem" "fmt" "net" "reflect" @@ -130,8 +132,9 @@ func (s *TLSCipherSuites) UnmarshalJSON(data []byte) error { // Fields for TLSAuth. Unmarshalling hack. type TLSAuthFields struct { // Certificate and key as a PEM-encoded string, including "-----BEGIN CERTIFICATE-----". - Cert string `json:"cert"` - Key string `json:"key"` + Cert string `json:"cert"` + Key string `json:"key"` + Password string `json:"password"` // Domains to present the certificate to. May contain wildcards, eg. "*.example.com". Domains []string `json:"domains"` @@ -154,8 +157,16 @@ func (c *TLSAuth) UnmarshalJSON(data []byte) error { } func (c *TLSAuth) Certificate() (*tls.Certificate, error) { + key := []byte(c.Key) + var err error + if c.Password != "" { + key, err = parsePrivateKey(c.Key, c.Password) + if err != nil { + return nil, err + } + } if c.certificate == nil { - cert, err := tls.X509KeyPair([]byte(c.Cert), []byte(c.Key)) + cert, err := tls.X509KeyPair([]byte(c.Cert), key) if err != nil { return nil, err } @@ -164,6 +175,26 @@ func (c *TLSAuth) Certificate() (*tls.Certificate, error) { return c.certificate, nil } +func parsePrivateKey(privKey, password string) ([]byte, error) { + key := []byte(privKey) + block, _ := pem.Decode(key) + if block == nil { + return nil, fmt.Errorf("failed to decode PEM key") + } + blockType := block.Type + if encrypted := x509.IsEncryptedPEMBlock(block); encrypted { + decryptedKey, err := x509.DecryptPEMBlock(block, []byte(password)) + if err != nil { + return nil, err + } + key = pem.EncodeToMemory(&pem.Block{ + Type: blockType, + Bytes: decryptedKey, + }) + } + return key, nil +} + // IPNet is a wrapper around net.IPNet for JSON unmarshalling type IPNet struct { net.IPNet diff --git a/lib/options_test.go b/lib/options_test.go index f46801d8b1b..1e81058b409 100644 --- a/lib/options_test.go +++ b/lib/options_test.go @@ -253,6 +253,7 @@ func TestOptions(t *testing.T) { "AwEHoUQDQgAEF8XzmC7x8Ns0Y2Wyu2c77ge+6I/ghcDTjWOMZzMPmRRDxqKFLuGD\n" + "zW1Kss13WODGSS8+j7dNCPOeLKyK6cbeIg==\n" + "-----END EC PRIVATE KEY-----", + Password: "iNGNlsrtnO+4HiQAwRgIhANYDaM18sXAdkjyvuiP4IXA041jdK48Jd6a8aD", }, nil}, } opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) @@ -286,6 +287,124 @@ func TestOptions(t *testing.T) { assert.Error(t, json.Unmarshal([]byte(jsonStr), &opts)) }) }) + + t.Run("TLSAuth encrypted key and invalid password", func(t *testing.T) { + tlsAuth := []*TLSAuth{ + {TLSAuthFields{ + Domains: []string{"example.com", "*.example.com"}, + Cert: "-----BEGIN CERTIFICATE-----\n" + + "MIIBoTCCAUegAwIBAgIUQl0J1Gkd6U2NIMwMDnpfH8c1myEwCgYIKoZIzj0EAwIw\n" + + "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMTgwODE1MTYxODAw\n" + + "WjAQMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLaf\n" + + "xEOmBHkzbqd9/0VZX/39qO2yQq2Gz5faRdvy38kuLMCV+9HYrfMx6GYCZzTUIq6h\n" + + "8QXOrlgYTixuUVfhJNWjfzB9MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\n" + + "BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxmQiq5K3\n" + + "KUnVME945Byt3Ysvkh8wHwYDVR0jBBgwFoAU3qEhcpRgpsqo9V+LFns9a+oZIYww\n" + + "CgYIKoZIzj0EAwIDSAAwRQIgSGxnJ+/cLUNTzt7fhr/mjJn7ShsTW33dAdfLM7H2\n" + + "z/gCIQDyVf8DePtxlkMBScTxZmIlMQdNc6+6VGZQ4QscruVLmg==\n" + + "-----END CERTIFICATE-----", + Key: "-----BEGIN EC PRIVATE KEY-----\n" + + "Proc-Type: 4,ENCRYPTED\n" + + "DEK-Info: AES-256-CBC,DF2445CBFE2E5B112FB2B721063757E5\n" + + "o/VKNZjQcRM2hatqUkQ0dTolL7i2i5hJX9XYsl+TMsq8ZkC83uY/JdR986QS+W2c\n" + + "EoQGtVGVeL0KGvGpzjTX3YAKXM7Lg5btAeS8GvJ9S7YFd8s0q1pqDdffl2RyjJav\n" + + "t1jx6XvLu2nBrOUARvHqjkkJQCTdRf2a34GJdbZqE+4=\n" + + "-----END EC PRIVATE KEY-----", + Password: "iZfYGcrgFHOg4nweEo7ufT", + }, nil}, + } + opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) + assert.Equal(t, tlsAuth, opts.TLSAuth) + + t.Run("Roundtrip", func(t *testing.T) { + optsData, err := json.Marshal(opts) + assert.NoError(t, err) + + var opts2 Options + errMsg := "x509: decryption password incorrect" + err = json.Unmarshal(optsData, &opts2) + assert.Error(t, err) + assert.Contains(t, err.Error(), errMsg) + }) + }) + + t.Run("TLSAuth encrypted key and valid password", func(t *testing.T) { + tlsAuth := []*TLSAuth{ + {TLSAuthFields{ + Domains: []string{"example.com", "*.example.com"}, + Cert: "-----BEGIN CERTIFICATE-----\n" + + "MIIBoTCCAUegAwIBAgIUQl0J1Gkd6U2NIMwMDnpfH8c1myEwCgYIKoZIzj0EAwIw\n" + + "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMTgwODE1MTYxODAw\n" + + "WjAQMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLaf\n" + + "xEOmBHkzbqd9/0VZX/39qO2yQq2Gz5faRdvy38kuLMCV+9HYrfMx6GYCZzTUIq6h\n" + + "8QXOrlgYTixuUVfhJNWjfzB9MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\n" + + "BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxmQiq5K3\n" + + "KUnVME945Byt3Ysvkh8wHwYDVR0jBBgwFoAU3qEhcpRgpsqo9V+LFns9a+oZIYww\n" + + "CgYIKoZIzj0EAwIDSAAwRQIgSGxnJ+/cLUNTzt7fhr/mjJn7ShsTW33dAdfLM7H2\n" + + "z/gCIQDyVf8DePtxlkMBScTxZmIlMQdNc6+6VGZQ4QscruVLmg==\n" + + "-----END CERTIFICATE-----", + Key: "-----BEGIN EC PRIVATE KEY-----\n" + + "Proc-Type: 4,ENCRYPTED\n" + + "DEK-Info: AES-256-CBC,DF2445CBFE2E5B112FB2B721063757E5\n" + + "o/VKNZjQcRM2hatqUkQ0dTolL7i2i5hJX9XYsl+TMsq8ZkC83uY/JdR986QS+W2c\n" + + "EoQGtVGVeL0KGvGpzjTX3YAKXM7Lg5btAeS8GvJ9S7YFd8s0q1pqDdffl2RyjJav\n" + + "t1jx6XvLu2nBrOUARvHqjkkJQCTdRf2a34GJdbZqE+4=\n" + + "-----END EC PRIVATE KEY-----", + Password: "12345", + }, nil}, + } + opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) + assert.Equal(t, tlsAuth, opts.TLSAuth) + + t.Run("Roundtrip", func(t *testing.T) { + optsData, err := json.Marshal(opts) + assert.NoError(t, err) + + var opts2 Options + err = json.Unmarshal(optsData, &opts2) + assert.NoError(t, err) + }) + }) + t.Run("TLSAuth encrypted pks8 format key and valid password", func(t *testing.T) { + tlsAuth := []*TLSAuth{ + {TLSAuthFields{ + Domains: []string{"example.com", "*.example.com"}, + Cert: "-----BEGIN CERTIFICATE-----\n" + + "MIIBoTCCAUegAwIBAgIUQl0J1Gkd6U2NIMwMDnpfH8c1myEwCgYIKoZIzj0EAwIw\n" + + "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMTgwODE1MTYxODAw\n" + + "WjAQMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLaf\n" + + "xEOmBHkzbqd9/0VZX/39qO2yQq2Gz5faRdvy38kuLMCV+9HYrfMx6GYCZzTUIq6h\n" + + "8QXOrlgYTixuUVfhJNWjfzB9MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\n" + + "BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxmQiq5K3\n" + + "KUnVME945Byt3Ysvkh8wHwYDVR0jBBgwFoAU3qEhcpRgpsqo9V+LFns9a+oZIYww\n" + + "CgYIKoZIzj0EAwIDSAAwRQIgSGxnJ+/cLUNTzt7fhr/mjJn7ShsTW33dAdfLM7H2\n" + + "z/gCIQDyVf8DePtxlkMBScTxZmIlMQdNc6+6VGZQ4QscruVLmg==\n" + + "-----END CERTIFICATE-----", + Key: "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + + "MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjcfarGfrRgUgICCAAw\n" + + "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEFmtmKEFmThbkbpxmC6iBvoEgZCE\n" + + "pDCpH/yCLmSpjdi/PC74I794nzHyCWf/oS0JhM0Q7J+abZP+p5pnreKft1f15Dbw\n" + + "QG9alfoM6EffJcVo3gf1tgQrpGGFMwczc4VhQgSGDy0XjZSbd2K0QCFGSmD2ZIR1\n" + + "qPG3WepWjKmIsYffGeKZx+FjXHSFeGk7RnssNAyKcPruDQIdWWyXxX1+ugBKuBw=\n" + + "-----END ENCRYPTED PRIVATE KEY-----\n", + Password: "12345", + }, nil}, + } + opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) + assert.Equal(t, tlsAuth, opts.TLSAuth) + + t.Run("Roundtrip", func(t *testing.T) { + optsData, err := json.Marshal(opts) + assert.NoError(t, err) + + var opts2 Options + errMsg := "tls: failed to parse private key" + err = json.Unmarshal(optsData, &opts2) + assert.Error(t, err) + assert.Contains(t, err.Error(), errMsg) + }) + }) + t.Run("NoConnectionReuse", func(t *testing.T) { opts := Options{}.Apply(Options{NoConnectionReuse: null.BoolFrom(true)}) assert.True(t, opts.NoConnectionReuse.Valid) From 2ea95735eeb9694d8332548be360d6ad746e2b2a Mon Sep 17 00:00:00 2001 From: Gabriel Santos Date: Fri, 8 Apr 2022 22:23:52 +0100 Subject: [PATCH 2/7] Throw an error when pkcs8 format encrypted key is provided --- lib/options.go | 3 +++ lib/options_test.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/options.go b/lib/options.go index f558bf9ef66..f41eb8e4717 100644 --- a/lib/options.go +++ b/lib/options.go @@ -182,6 +182,9 @@ func parsePrivateKey(privKey, password string) ([]byte, error) { return nil, fmt.Errorf("failed to decode PEM key") } blockType := block.Type + if blockType == "ENCRYPTED PRIVATE KEY" { + return nil, fmt.Errorf("encrypted pkcs8 formatted key is not supported") + } if encrypted := x509.IsEncryptedPEMBlock(block); encrypted { decryptedKey, err := x509.DecryptPEMBlock(block, []byte(password)) if err != nil { diff --git a/lib/options_test.go b/lib/options_test.go index 1e81058b409..9ce2949faf2 100644 --- a/lib/options_test.go +++ b/lib/options_test.go @@ -398,7 +398,7 @@ func TestOptions(t *testing.T) { assert.NoError(t, err) var opts2 Options - errMsg := "tls: failed to parse private key" + errMsg := "encrypted pkcs8 formatted key is not supported" err = json.Unmarshal(optsData, &opts2) assert.Error(t, err) assert.Contains(t, err.Error(), errMsg) From 931b7695079c7247831a56cf2e164d1147e9b989 Mon Sep 17 00:00:00 2001 From: Gabriel Santos Date: Mon, 11 Apr 2022 19:00:01 +0100 Subject: [PATCH 3/7] Tests refactoring and fix ci issues --- lib/options.go | 6 +- lib/options_test.go | 165 ++++++++++++++++++-------------------------- 2 files changed, 73 insertions(+), 98 deletions(-) diff --git a/lib/options.go b/lib/options.go index f41eb8e4717..c4c60570d53 100644 --- a/lib/options.go +++ b/lib/options.go @@ -160,7 +160,7 @@ func (c *TLSAuth) Certificate() (*tls.Certificate, error) { key := []byte(c.Key) var err error if c.Password != "" { - key, err = parsePrivateKey(c.Key, c.Password) + key, err = decryptPrivateKey(c.Key, c.Password) if err != nil { return nil, err } @@ -175,12 +175,14 @@ func (c *TLSAuth) Certificate() (*tls.Certificate, error) { return c.certificate, nil } -func parsePrivateKey(privKey, password string) ([]byte, error) { +func decryptPrivateKey(privKey, password string) ([]byte, error) { key := []byte(privKey) + block, _ := pem.Decode(key) if block == nil { return nil, fmt.Errorf("failed to decode PEM key") } + blockType := block.Type if blockType == "ENCRYPTED PRIVATE KEY" { return nil, fmt.Errorf("encrypted pkcs8 formatted key is not supported") diff --git a/lib/options_test.go b/lib/options_test.go index 9ce2949faf2..8710d3c0643 100644 --- a/lib/options_test.go +++ b/lib/options_test.go @@ -215,6 +215,7 @@ func TestOptions(t *testing.T) { }) }) t.Run("TLSAuth", func(t *testing.T) { + t.Parallel() tlsAuth := []*TLSAuth{ {TLSAuthFields{ Domains: []string{"example.com", "*.example.com"}, @@ -287,124 +288,96 @@ func TestOptions(t *testing.T) { assert.Error(t, json.Unmarshal([]byte(jsonStr), &opts)) }) }) - - t.Run("TLSAuth encrypted key and invalid password", func(t *testing.T) { - tlsAuth := []*TLSAuth{ - {TLSAuthFields{ - Domains: []string{"example.com", "*.example.com"}, - Cert: "-----BEGIN CERTIFICATE-----\n" + - "MIIBoTCCAUegAwIBAgIUQl0J1Gkd6U2NIMwMDnpfH8c1myEwCgYIKoZIzj0EAwIw\n" + - "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMTgwODE1MTYxODAw\n" + - "WjAQMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLaf\n" + - "xEOmBHkzbqd9/0VZX/39qO2yQq2Gz5faRdvy38kuLMCV+9HYrfMx6GYCZzTUIq6h\n" + - "8QXOrlgYTixuUVfhJNWjfzB9MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\n" + - "BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxmQiq5K3\n" + - "KUnVME945Byt3Ysvkh8wHwYDVR0jBBgwFoAU3qEhcpRgpsqo9V+LFns9a+oZIYww\n" + - "CgYIKoZIzj0EAwIDSAAwRQIgSGxnJ+/cLUNTzt7fhr/mjJn7ShsTW33dAdfLM7H2\n" + - "z/gCIQDyVf8DePtxlkMBScTxZmIlMQdNc6+6VGZQ4QscruVLmg==\n" + - "-----END CERTIFICATE-----", - Key: "-----BEGIN EC PRIVATE KEY-----\n" + + t.Run("TLSAuth with", func(t *testing.T) { + t.Parallel() + domains := []string{"example.com", "*.example.com"} + cert := "-----BEGIN CERTIFICATE-----\n" + + "MIIBoTCCAUegAwIBAgIUQl0J1Gkd6U2NIMwMDnpfH8c1myEwCgYIKoZIzj0EAwIw\n" + + "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMTgwODE1MTYxODAw\n" + + "WjAQMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLaf\n" + + "xEOmBHkzbqd9/0VZX/39qO2yQq2Gz5faRdvy38kuLMCV+9HYrfMx6GYCZzTUIq6h\n" + + "8QXOrlgYTixuUVfhJNWjfzB9MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\n" + + "BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxmQiq5K3\n" + + "KUnVME945Byt3Ysvkh8wHwYDVR0jBBgwFoAU3qEhcpRgpsqo9V+LFns9a+oZIYww\n" + + "CgYIKoZIzj0EAwIDSAAwRQIgSGxnJ+/cLUNTzt7fhr/mjJn7ShsTW33dAdfLM7H2\n" + + "z/gCIQDyVf8DePtxlkMBScTxZmIlMQdNc6+6VGZQ4QscruVLmg==\n" + + "-----END CERTIFICATE-----" + tests := []struct { + name string + privateKey string + password string + hasError bool + errorMessage string + }{ + { + name: "encrypted key and invalid password", + privateKey: "-----BEGIN EC PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: AES-256-CBC,DF2445CBFE2E5B112FB2B721063757E5\n" + "o/VKNZjQcRM2hatqUkQ0dTolL7i2i5hJX9XYsl+TMsq8ZkC83uY/JdR986QS+W2c\n" + "EoQGtVGVeL0KGvGpzjTX3YAKXM7Lg5btAeS8GvJ9S7YFd8s0q1pqDdffl2RyjJav\n" + "t1jx6XvLu2nBrOUARvHqjkkJQCTdRf2a34GJdbZqE+4=\n" + "-----END EC PRIVATE KEY-----", - Password: "iZfYGcrgFHOg4nweEo7ufT", - }, nil}, - } - opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) - assert.Equal(t, tlsAuth, opts.TLSAuth) - - t.Run("Roundtrip", func(t *testing.T) { - optsData, err := json.Marshal(opts) - assert.NoError(t, err) - - var opts2 Options - errMsg := "x509: decryption password incorrect" - err = json.Unmarshal(optsData, &opts2) - assert.Error(t, err) - assert.Contains(t, err.Error(), errMsg) - }) - }) - - t.Run("TLSAuth encrypted key and valid password", func(t *testing.T) { - tlsAuth := []*TLSAuth{ - {TLSAuthFields{ - Domains: []string{"example.com", "*.example.com"}, - Cert: "-----BEGIN CERTIFICATE-----\n" + - "MIIBoTCCAUegAwIBAgIUQl0J1Gkd6U2NIMwMDnpfH8c1myEwCgYIKoZIzj0EAwIw\n" + - "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMTgwODE1MTYxODAw\n" + - "WjAQMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLaf\n" + - "xEOmBHkzbqd9/0VZX/39qO2yQq2Gz5faRdvy38kuLMCV+9HYrfMx6GYCZzTUIq6h\n" + - "8QXOrlgYTixuUVfhJNWjfzB9MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\n" + - "BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxmQiq5K3\n" + - "KUnVME945Byt3Ysvkh8wHwYDVR0jBBgwFoAU3qEhcpRgpsqo9V+LFns9a+oZIYww\n" + - "CgYIKoZIzj0EAwIDSAAwRQIgSGxnJ+/cLUNTzt7fhr/mjJn7ShsTW33dAdfLM7H2\n" + - "z/gCIQDyVf8DePtxlkMBScTxZmIlMQdNc6+6VGZQ4QscruVLmg==\n" + - "-----END CERTIFICATE-----", - Key: "-----BEGIN EC PRIVATE KEY-----\n" + + password: "iZfYGcrgFHOg4nweEo7ufT", + hasError: true, + errorMessage: "x509: decryption password incorrect", + }, + { + name: "encrypted key and valid password", + privateKey: "-----BEGIN EC PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: AES-256-CBC,DF2445CBFE2E5B112FB2B721063757E5\n" + "o/VKNZjQcRM2hatqUkQ0dTolL7i2i5hJX9XYsl+TMsq8ZkC83uY/JdR986QS+W2c\n" + "EoQGtVGVeL0KGvGpzjTX3YAKXM7Lg5btAeS8GvJ9S7YFd8s0q1pqDdffl2RyjJav\n" + "t1jx6XvLu2nBrOUARvHqjkkJQCTdRf2a34GJdbZqE+4=\n" + "-----END EC PRIVATE KEY-----", - Password: "12345", - }, nil}, - } - opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) - assert.Equal(t, tlsAuth, opts.TLSAuth) - - t.Run("Roundtrip", func(t *testing.T) { - optsData, err := json.Marshal(opts) - assert.NoError(t, err) - - var opts2 Options - err = json.Unmarshal(optsData, &opts2) - assert.NoError(t, err) - }) - }) - t.Run("TLSAuth encrypted pks8 format key and valid password", func(t *testing.T) { - tlsAuth := []*TLSAuth{ - {TLSAuthFields{ - Domains: []string{"example.com", "*.example.com"}, - Cert: "-----BEGIN CERTIFICATE-----\n" + - "MIIBoTCCAUegAwIBAgIUQl0J1Gkd6U2NIMwMDnpfH8c1myEwCgYIKoZIzj0EAwIw\n" + - "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMTgwODE1MTYxODAw\n" + - "WjAQMQ4wDAYDVQQDEwV1c2VyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLaf\n" + - "xEOmBHkzbqd9/0VZX/39qO2yQq2Gz5faRdvy38kuLMCV+9HYrfMx6GYCZzTUIq6h\n" + - "8QXOrlgYTixuUVfhJNWjfzB9MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr\n" + - "BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxmQiq5K3\n" + - "KUnVME945Byt3Ysvkh8wHwYDVR0jBBgwFoAU3qEhcpRgpsqo9V+LFns9a+oZIYww\n" + - "CgYIKoZIzj0EAwIDSAAwRQIgSGxnJ+/cLUNTzt7fhr/mjJn7ShsTW33dAdfLM7H2\n" + - "z/gCIQDyVf8DePtxlkMBScTxZmIlMQdNc6+6VGZQ4QscruVLmg==\n" + - "-----END CERTIFICATE-----", - Key: "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + + password: "12345", + hasError: false, + errorMessage: "", + }, + { + name: "encrypted pks8 format key and valid password", + privateKey: "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + "MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjcfarGfrRgUgICCAAw\n" + "DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEFmtmKEFmThbkbpxmC6iBvoEgZCE\n" + "pDCpH/yCLmSpjdi/PC74I794nzHyCWf/oS0JhM0Q7J+abZP+p5pnreKft1f15Dbw\n" + "QG9alfoM6EffJcVo3gf1tgQrpGGFMwczc4VhQgSGDy0XjZSbd2K0QCFGSmD2ZIR1\n" + "qPG3WepWjKmIsYffGeKZx+FjXHSFeGk7RnssNAyKcPruDQIdWWyXxX1+ugBKuBw=\n" + "-----END ENCRYPTED PRIVATE KEY-----\n", - Password: "12345", - }, nil}, + password: "12345", + hasError: true, + errorMessage: "encrypted pkcs8 formatted key is not supported", + }, } - opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) - assert.Equal(t, tlsAuth, opts.TLSAuth) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tlsAuth := []*TLSAuth{ + {TLSAuthFields{ + Domains: domains, + Cert: cert, + Key: tc.privateKey, + Password: tc.password, + }, nil}, + } + opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) + assert.Equal(t, tlsAuth, opts.TLSAuth) - t.Run("Roundtrip", func(t *testing.T) { - optsData, err := json.Marshal(opts) - assert.NoError(t, err) + t.Run("Roundtrip", func(t *testing.T) { + optsData, err := json.Marshal(opts) + assert.NoError(t, err) - var opts2 Options - errMsg := "encrypted pkcs8 formatted key is not supported" - err = json.Unmarshal(optsData, &opts2) - assert.Error(t, err) - assert.Contains(t, err.Error(), errMsg) - }) + var opts2 Options + err = json.Unmarshal(optsData, &opts2) + if tc.hasError { + assert.Error(t, err) + assert.Contains(t, err.Error(), tc.errorMessage) + } else { + assert.NoError(t, err) + } + }) + }) + } }) - t.Run("NoConnectionReuse", func(t *testing.T) { opts := Options{}.Apply(Options{NoConnectionReuse: null.BoolFrom(true)}) assert.True(t, opts.NoConnectionReuse.Valid) From 013d52b2166c7fe025ea22d2d7b1b11130853916 Mon Sep 17 00:00:00 2001 From: Gabriel Santos Date: Wed, 13 Apr 2022 18:41:39 +0100 Subject: [PATCH 4/7] Removed if clause to check if key is encrypted instead of always trying to decrypt it --- lib/options.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/options.go b/lib/options.go index c4c60570d53..4b02f72133c 100644 --- a/lib/options.go +++ b/lib/options.go @@ -187,16 +187,14 @@ func decryptPrivateKey(privKey, password string) ([]byte, error) { if blockType == "ENCRYPTED PRIVATE KEY" { return nil, fmt.Errorf("encrypted pkcs8 formatted key is not supported") } - if encrypted := x509.IsEncryptedPEMBlock(block); encrypted { - decryptedKey, err := x509.DecryptPEMBlock(block, []byte(password)) - if err != nil { - return nil, err - } - key = pem.EncodeToMemory(&pem.Block{ - Type: blockType, - Bytes: decryptedKey, - }) + decryptedKey, err := x509.DecryptPEMBlock(block, []byte(password)) + if err != nil { + return nil, err } + key = pem.EncodeToMemory(&pem.Block{ + Type: blockType, + Bytes: decryptedKey, + }) return key, nil } From 6d3910b3c87ae79d5f30eb6d846a2d9b8db1662d Mon Sep 17 00:00:00 2001 From: Gabriel Santos Date: Thu, 14 Apr 2022 09:20:12 +0100 Subject: [PATCH 5/7] Fix tests and add test where password is provided and key is not encrypted --- lib/options_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/options_test.go b/lib/options_test.go index 8710d3c0643..9aee1c993dc 100644 --- a/lib/options_test.go +++ b/lib/options_test.go @@ -254,7 +254,6 @@ func TestOptions(t *testing.T) { "AwEHoUQDQgAEF8XzmC7x8Ns0Y2Wyu2c77ge+6I/ghcDTjWOMZzMPmRRDxqKFLuGD\n" + "zW1Kss13WODGSS8+j7dNCPOeLKyK6cbeIg==\n" + "-----END EC PRIVATE KEY-----", - Password: "iNGNlsrtnO+4HiQAwRgIhANYDaM18sXAdkjyvuiP4IXA041jdK48Jd6a8aD", }, nil}, } opts := Options{}.Apply(Options{TLSAuth: tlsAuth}) @@ -348,6 +347,17 @@ func TestOptions(t *testing.T) { hasError: true, errorMessage: "encrypted pkcs8 formatted key is not supported", }, + { + name: "non encrypted key and password", + privateKey: "-----BEGIN EC PRIVATE KEY-----\n" + + "MHcCAQEEINVilD5qOBkSy+AYfd41X0QPB5N3Z6OzgoBj8FZmSJOFoAoGCCqGSM49\n" + + "AwEHoUQDQgAEF8XzmC7x8Ns0Y2Wyu2c77ge+6I/ghcDTjWOMZzMPmRRDxqKFLuGD\n" + + "zW1Kss13WODGSS8+j7dNCPOeLKyK6cbeIg==\n" + + "-----END EC PRIVATE KEY-----", + password: "12345", + hasError: true, + errorMessage: "x509: no DEK-Info header in block", + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { From e55c117354ca2254b0dfebd23f6db59c09ca3aaf Mon Sep 17 00:00:00 2001 From: Gabriel Santos Date: Thu, 14 Apr 2022 16:25:23 +0100 Subject: [PATCH 6/7] fix ci staticcheck of DecryptPEMBlock --- lib/options.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/options.go b/lib/options.go index 4b02f72133c..bd0e716328a 100644 --- a/lib/options.go +++ b/lib/options.go @@ -187,7 +187,12 @@ func decryptPrivateKey(privKey, password string) ([]byte, error) { if blockType == "ENCRYPTED PRIVATE KEY" { return nil, fmt.Errorf("encrypted pkcs8 formatted key is not supported") } - decryptedKey, err := x509.DecryptPEMBlock(block, []byte(password)) + /* + Even though `DecryptPEMBlock` has been depecrated since 1.16.x it is still + being used here because there are not alternatives in the standard library + to decrypt PEM encoded files. + */ + decryptedKey, err := x509.DecryptPEMBlock(block, []byte(password)) // nolint: staticcheck if err != nil { return nil, err } From 7f9bcc98972930ef1fc7cc87c6ffebb290ca389c Mon Sep 17 00:00:00 2001 From: Gabriel Santos Date: Wed, 20 Apr 2022 12:49:10 +0100 Subject: [PATCH 7/7] Update lib/options.go Co-authored-by: Mihail Stoykov --- lib/options.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/options.go b/lib/options.go index bd0e716328a..b247a5aba0a 100644 --- a/lib/options.go +++ b/lib/options.go @@ -188,9 +188,9 @@ func decryptPrivateKey(privKey, password string) ([]byte, error) { return nil, fmt.Errorf("encrypted pkcs8 formatted key is not supported") } /* - Even though `DecryptPEMBlock` has been depecrated since 1.16.x it is still - being used here because there are not alternatives in the standard library - to decrypt PEM encoded files. + Even though `DecryptPEMBlock` has been deprecated since 1.16.x it is still + being used here because it is deprecated due to it not supporting *good* crypography + ultimately though we want to support something so we will be using it for now. */ decryptedKey, err := x509.DecryptPEMBlock(block, []byte(password)) // nolint: staticcheck if err != nil {