diff --git a/certcrypto/crypto.go b/certcrypto/crypto.go index c9d0c109..b6da790d 100644 --- a/certcrypto/crypto.go +++ b/certcrypto/crypto.go @@ -3,6 +3,7 @@ package certcrypto import ( "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" @@ -13,6 +14,7 @@ import ( "errors" "fmt" "math/big" + "strings" "time" "golang.org/x/crypto/ocsp" @@ -77,17 +79,35 @@ func ParsePEMBundle(bundle []byte) ([]*x509.Certificate, error) { return certificates, nil } +// ParsePEMPrivateKey parses a private key from key, which is a PEM block. +// Borrowed from Go standard library, to handle various private key and PEM block types. +// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308 +// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238) func ParsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) { - keyBlock, _ := pem.Decode(key) + keyBlockDER, _ := pem.Decode(key) - switch keyBlock.Type { - case "RSA PRIVATE KEY": - return x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - case "EC PRIVATE KEY": - return x509.ParseECPrivateKey(keyBlock.Bytes) - default: - return nil, errors.New("unknown PEM header value") + if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") { + return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type) } + + if key, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil { + return key, nil + } + + if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: + return key, nil + default: + return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key) + } + } + + if key, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil { + return key, nil + } + + return nil, errors.New("failed to parse private key") } func GeneratePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {