]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/tls: add support for loading EC X.509 key pairs
authorJoel Sing <jsing@google.com>
Fri, 16 Nov 2012 08:33:59 +0000 (19:33 +1100)
committerJoel Sing <jsing@google.com>
Fri, 16 Nov 2012 08:33:59 +0000 (19:33 +1100)
Add support for loading X.509 key pairs that consist of a certificate
with an EC public key and its corresponding EC private key.

R=agl
CC=golang-dev
https://golang.org/cl/6776043

src/pkg/crypto/tls/tls.go
src/pkg/crypto/tls/tls_test.go

index 80f852edf7ba9d80cf1ca00518b0681588da9a36..182506c59efd077d866fc62d24b6aeea61c3e475 100644 (file)
@@ -6,6 +6,8 @@
 package tls
 
 import (
+       "crypto"
+       "crypto/ecdsa"
        "crypto/rsa"
        "crypto/x509"
        "encoding/pem"
@@ -153,30 +155,16 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
                        err = errors.New("crypto/tls: failed to parse key PEM data")
                        return
                }
-               if keyDERBlock.Type != "CERTIFICATE" {
+               if strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
                        break
                }
        }
 
-       // OpenSSL 0.9.8 generates PKCS#1 private keys by default, while
-       // OpenSSL 1.0.0 generates PKCS#8 keys. We try both.
-       var key *rsa.PrivateKey
-       if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil {
-               var privKey interface{}
-               if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil {
-                       err = errors.New("crypto/tls: failed to parse key: " + err.Error())
-                       return
-               }
-
-               var ok bool
-               if key, ok = privKey.(*rsa.PrivateKey); !ok {
-                       err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping")
-                       return
-               }
+       cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
+       if err != nil {
+               return
        }
 
-       cert.PrivateKey = key
-
        // We don't need to parse the public key for TLS, but we so do anyway
        // to check that it looks sane and matches the private key.
        x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
@@ -184,10 +172,54 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
                return
        }
 
-       if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 {
-               err = errors.New("crypto/tls: private key does not match public key")
+       switch pub := x509Cert.PublicKey.(type) {
+       case *rsa.PublicKey:
+               priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
+               if !ok {
+                       err = errors.New("crypto/tls: private key type does not match public key type")
+                       return
+               }
+               if pub.N.Cmp(priv.N) != 0 {
+                       err = errors.New("crypto/tls: private key does not match public key")
+                       return
+               }
+       case *ecdsa.PublicKey:
+               priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+               if !ok {
+                       err = errors.New("crypto/tls: private key type does not match public key type")
+                       return
+
+               }
+               if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
+                       err = errors.New("crypto/tls: private key does not match public key")
+                       return
+               }
+       default:
+               err = errors.New("crypto/tls: unknown public key algorithm")
                return
        }
 
        return
 }
+
+// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
+// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
+// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
+func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
+       if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
+               return key, nil
+       }
+       if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
+               switch key := key.(type) {
+               case *rsa.PrivateKey, *ecdsa.PrivateKey:
+                       return key, nil
+               default:
+                       return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
+               }
+       }
+       if key, err := x509.ParseECPrivateKey(der); err == nil {
+               return key, nil
+       }
+
+       return nil, errors.New("crypto/tls: failed to parse private key")
+}
index 5df43c385fd8d073272cdd2ef2e60c686ac34f35..31b858d83274d8f5ec67c90a8504ada54e3d70be 100644 (file)
@@ -8,7 +8,7 @@ import (
        "testing"
 )
 
-var certPEM = `-----BEGIN CERTIFICATE-----
+var rsaCertPEM = `-----BEGIN CERTIFICATE-----
 MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
 BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
 aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
@@ -22,7 +22,7 @@ r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
 -----END CERTIFICATE-----
 `
 
-var keyPEM = `-----BEGIN RSA PRIVATE KEY-----
+var rsaKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
 MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
 k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
 6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
@@ -33,15 +33,61 @@ D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
 -----END RSA PRIVATE KEY-----
 `
 
+var ecdsaCertPEM = `-----BEGIN CERTIFICATE-----
+MIIB/jCCAWICCQDscdUxw16XFDAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw
+EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQwHhcNMTIxMTE0MTI0MDQ4WhcNMTUxMTE0MTI0MDQ4WjBFMQswCQYDVQQG
+EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lk
+Z2l0cyBQdHkgTHRkMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBY9+my9OoeSUR
+lDQdV/x8LsOuLilthhiS1Tz4aGDHIPwC1mlvnf7fg5lecYpMCrLLhauAc1UJXcgl
+01xoLuzgtAEAgv2P/jgytzRSpUYvgLBt1UA0leLYBy6mQQbrNEuqT3INapKIcUv8
+XxYP0xMEUksLPq6Ca+CRSqTtrd/23uTnapkwCQYHKoZIzj0EAQOBigAwgYYCQXJo
+A7Sl2nLVf+4Iu/tAX/IF4MavARKC4PPHK3zfuGfPR3oCCcsAoz3kAzOeijvd0iXb
+H5jBImIxPL4WxQNiBTexAkF8D1EtpYuWdlVQ80/h/f4pBcGiXPqX5h2PQSQY7hP1
++jwM1FGS4fREIOvlBYr/SzzQRtwrvrzGYxDEDbsC0ZGRnA==
+-----END CERTIFICATE-----
+`
+
+var ecdsaKeyPEM = `-----BEGIN EC PARAMETERS-----
+BgUrgQQAIw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBrsoKp0oqcv6/JovJJDoDVSGWdirrkgCWxrprGlzB9o0X8fV675X0
+NwuBenXFfeZvVcwluO7/Q9wkYoPd/t3jGImgBwYFK4EEACOhgYkDgYYABAFj36bL
+06h5JRGUNB1X/Hwuw64uKW2GGJLVPPhoYMcg/ALWaW+d/t+DmV5xikwKssuFq4Bz
+VQldyCXTXGgu7OC0AQCC/Y/+ODK3NFKlRi+AsG3VQDSV4tgHLqZBBus0S6pPcg1q
+kohxS/xfFg/TEwRSSws+roJr4JFKpO2t3/be5OdqmQ==
+-----END EC PRIVATE KEY-----
+`
+
+var keyPairTests = []struct {
+       algo string
+       cert *string
+       key  *string
+}{
+       {"ECDSA", &ecdsaCertPEM, &ecdsaKeyPEM},
+       {"RSA", &rsaCertPEM, &rsaKeyPEM},
+}
+
 func TestX509KeyPair(t *testing.T) {
-       _, err := X509KeyPair([]byte(keyPEM+certPEM), []byte(keyPEM+certPEM))
-       if err != nil {
-               t.Errorf("Failed to load key followed by cert: %s", err)
+       var pem []byte
+       for _, test := range keyPairTests {
+               pem = []byte(*test.cert + *test.key)
+               if _, err := X509KeyPair(pem, pem); err != nil {
+                       t.Errorf("Failed to load %s cert followed by %s key: %s", test.algo, test.algo, err)
+               }
+               pem = []byte(*test.key + *test.cert)
+               if _, err := X509KeyPair(pem, pem); err != nil {
+                       t.Errorf("Failed to load %s key followed by %s cert: %s", test.algo, test.algo, err)
+               }
        }
+}
 
-       _, err = X509KeyPair([]byte(certPEM+keyPEM), []byte(certPEM+keyPEM))
-       if err != nil {
-               t.Errorf("Failed to load cert followed by key: %s", err)
-               println(err.Error())
+func TestX509MixedKeyPair(t *testing.T) {
+       if _, err := X509KeyPair([]byte(rsaCertPEM), []byte(ecdsaKeyPEM)); err == nil {
+               t.Error("Load of RSA certificate succeeded with ECDSA private key")
+       }
+       if _, err := X509KeyPair([]byte(ecdsaCertPEM), []byte(rsaKeyPEM)); err == nil {
+               t.Error("Load of ECDSA certificate succeeded with RSA private key")
        }
 }