Skip to content

Commit bc33254

Browse files
committed
Remove support for encrypted TLS private keys
> Legacy PEM encryption as specified in RFC 1423 is insecure by design. Since > it does not authenticate the ciphertext, it is vulnerable to padding oracle > attacks that can let an attacker recover the plaintext From https://go-review.googlesource.com/c/go/+/264159 > It's unfortunate that we don't implement PKCS#8 encryption so we can't > recommend an alternative but PEM encryption is so broken that it's worth > deprecating outright. This feature allowed using an encrypted private key with a supplied password, but did not provide additional security as the encryption is known to be broken, and the key is sitting next to the password in the filesystem. Users are recommended to decrypt the private key, and store it un-encrypted to continue using it. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent 784042e commit bc33254

2 files changed

Lines changed: 25 additions & 55 deletions

File tree

tlsconfig/config.go

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,6 @@ type Options struct {
3434
// the system pool will be used.
3535
ExclusiveRootPools bool
3636
MinVersion uint16
37-
// If Passphrase is set, it will be used to decrypt a TLS private key
38-
// if the key is encrypted.
39-
//
40-
// Deprecated: Use of encrypted TLS private keys has been deprecated, and
41-
// will be removed in a future release. Golang has deprecated support for
42-
// legacy PEM encryption (as specified in RFC 1423), as it is insecure by
43-
// design (see https://go-review.googlesource.com/c/go/+/264159).
44-
Passphrase string
4537
}
4638

4739
// Extra (server-side) accepted CBC cipher suites - will phase out in the future
@@ -144,34 +136,32 @@ func adjustMinVersion(options Options, config *tls.Config) error {
144136
return nil
145137
}
146138

147-
// IsErrEncryptedKey returns true if the 'err' is an error of incorrect
148-
// password when trying to decrypt a TLS private key.
139+
// errEncryptedKeyDeprecated is produced when we encounter an encrypted
140+
// (password-protected) key. From https://go-review.googlesource.com/c/go/+/264159;
149141
//
150-
// Deprecated: Use of encrypted TLS private keys has been deprecated, and
151-
// will be removed in a future release. Golang has deprecated support for
152-
// legacy PEM encryption (as specified in RFC 1423), as it is insecure by
153-
// design (see https://go-review.googlesource.com/c/go/+/264159).
154-
func IsErrEncryptedKey(err error) bool {
155-
return errors.Is(err, x509.IncorrectPasswordError)
156-
}
142+
// > Legacy PEM encryption as specified in RFC 1423 is insecure by design. Since
143+
// > it does not authenticate the ciphertext, it is vulnerable to padding oracle
144+
// > attacks that can let an attacker recover the plaintext
145+
// >
146+
// > It's unfortunate that we don't implement PKCS#8 encryption so we can't
147+
// > recommend an alternative but PEM encryption is so broken that it's worth
148+
// > deprecating outright.
149+
//
150+
// Also see https://docs.docker.com/go/deprecated/
151+
var errEncryptedKeyDeprecated = errors.New("private key is encrypted; encrypted private keys are obsolete, and not supported")
157152

158153
// getPrivateKey returns the private key in 'keyBytes', in PEM-encoded format.
159-
// If the private key is encrypted, 'passphrase' is used to decrypted the
160-
// private key.
161-
func getPrivateKey(keyBytes []byte, passphrase string) ([]byte, error) {
154+
// It returns an error if the file could not be decoded or was protected by
155+
// a passphrase.
156+
func getPrivateKey(keyBytes []byte) ([]byte, error) {
162157
// this section makes some small changes to code from notary/tuf/utils/x509.go
163158
pemBlock, _ := pem.Decode(keyBytes)
164159
if pemBlock == nil {
165160
return nil, fmt.Errorf("no valid private key found")
166161
}
167162

168-
var err error
169163
if x509.IsEncryptedPEMBlock(pemBlock) { //nolint:staticcheck // Ignore SA1019 (IsEncryptedPEMBlock is deprecated)
170-
keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(passphrase)) //nolint:staticcheck // Ignore SA1019 (DecryptPEMBlock is deprecated)
171-
if err != nil {
172-
return nil, fmt.Errorf("private key is encrypted, but could not decrypt it: %w", err)
173-
}
174-
keyBytes = pem.EncodeToMemory(&pem.Block{Type: pemBlock.Type, Bytes: keyBytes})
164+
return nil, errEncryptedKeyDeprecated
175165
}
176166

177167
return keyBytes, nil
@@ -195,7 +185,7 @@ func getCert(options Options) ([]tls.Certificate, error) {
195185
return nil, err
196186
}
197187

198-
prKeyBytes, err = getPrivateKey(prKeyBytes, options.Passphrase)
188+
prKeyBytes, err = getPrivateKey(prKeyBytes)
199189
if err != nil {
200190
return nil, err
201191
}

tlsconfig/config_test.go

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/tls"
66
"crypto/x509"
77
"encoding/pem"
8+
"errors"
89
"os"
910
"reflect"
1011
"runtime"
@@ -523,39 +524,18 @@ func TestConfigClientTLSValidClientCertAndKey(t *testing.T) {
523524
}
524525
}
525526

526-
// The certificate is set if the client cert and encrypted client key are
527-
// provided and valid and passphrase can decrypt the key
528-
func TestConfigClientTLSValidClientCertAndEncryptedKey(t *testing.T) {
527+
func TestConfigClientTLSEncryptedKey(t *testing.T) {
529528
key, cert := getCertAndEncryptedKey()
530529

531530
tlsConfig, err := Client(Options{
532-
CertFile: cert,
533-
KeyFile: key,
534-
Passphrase: "FooBar123",
531+
CertFile: cert,
532+
KeyFile: key,
535533
})
536-
537-
if err != nil || tlsConfig == nil {
538-
t.Fatal("Unable to configure client TLS", err)
539-
}
540-
541-
if len(tlsConfig.Certificates) != 1 {
542-
t.Fatal("Unexpected client certificates")
534+
if !errors.Is(err, errEncryptedKeyDeprecated) {
535+
t.Errorf("Expected %v but got %v", errEncryptedKeyDeprecated, err)
543536
}
544-
}
545-
546-
// The certificate is not set if the provided passphrase cannot decrypt
547-
// the encrypted key.
548-
func TestConfigClientTLSNotSetWithInvalidPassphrase(t *testing.T) {
549-
key, cert := getCertAndEncryptedKey()
550-
551-
tlsConfig, err := Client(Options{
552-
CertFile: cert,
553-
KeyFile: key,
554-
Passphrase: "InvalidPassphrase",
555-
})
556-
557-
if !IsErrEncryptedKey(err) || tlsConfig != nil {
558-
t.Fatal("Expected failure due to incorrect passphrase.")
537+
if tlsConfig != nil {
538+
t.Errorf("Expected nil but got %v", tlsConfig)
559539
}
560540
}
561541

0 commit comments

Comments
 (0)