]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/x509: add support for SEC1/EC private keys.
authorJoel Sing <jsing@google.com>
Wed, 14 Nov 2012 16:39:00 +0000 (03:39 +1100)
committerJoel Sing <jsing@google.com>
Wed, 14 Nov 2012 16:39:00 +0000 (03:39 +1100)
Add support for parsing SEC1 EC private keys and PKCS8 encapsulated
EC private key structures.

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

src/pkg/crypto/x509/pkcs8.go
src/pkg/crypto/x509/pkcs8_test.go
src/pkg/crypto/x509/sec1.go [new file with mode: 0644]
src/pkg/crypto/x509/sec1_test.go [new file with mode: 0644]

index 539d1c5238015d7bf3e8082f336a9efe30af440b..30caacb3c54df35c22cabdb26651354aa5a7d1a2 100644 (file)
@@ -12,7 +12,8 @@ import (
 )
 
 // pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
-// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn.
+// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
+// and RFC5208.
 type pkcs8 struct {
        Version    int
        Algo       pkix.AlgorithmIdentifier
@@ -21,7 +22,7 @@ type pkcs8 struct {
 }
 
 // ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
-// http://www.rsa.com/rsalabs/node.asp?id=2130
+// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208.
 func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
        var privKey pkcs8
        if _, err := asn1.Unmarshal(der, &privKey); err != nil {
@@ -34,6 +35,19 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
                        return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
                }
                return key, nil
+
+       case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA):
+               bytes := privKey.Algo.Parameters.FullBytes
+               namedCurveOID := new(asn1.ObjectIdentifier)
+               if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil {
+                       namedCurveOID = nil
+               }
+               key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
+               if err != nil {
+                       return nil, errors.New("crypto/x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
+               }
+               return key, nil
+
        default:
                return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
        }
index 372005f908cf285a8a8e8d46ba68b4842ade9063..4114efd0e0d1ad24c5d3aa736a37b92c3009d18c 100644 (file)
@@ -9,12 +9,20 @@ import (
        "testing"
 )
 
-var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
+var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
+
+// Generated using:
+//   openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt
+var pkcs8ECPrivateKeyHex = `3081ed020100301006072a8648ce3d020106052b810400230481d53081d20201010441850d81618c5da1aec74c2eed608ba816038506975e6427237c2def150c96a3b13efbfa1f89f1be15cdf4d0ac26422e680e65a0ddd4ad3541ad76165fbf54d6e34ba18189038186000400da97bcedba1eb6d30aeb93c9f9a1454598fa47278df27d6f60ea73eb672d8dc528a9b67885b5b5dcef93c9824f7449ab512ee6a27e76142f56b94b474cfd697e810046c8ca70419365245c1d7d44d0db82c334073835d002232714548abbae6e5700f5ef315ee08b929d8581383dcf2d1c98c2f8a9fccbf79c9579f7b2fd8a90115ac2`
 
 func TestPKCS8(t *testing.T) {
-       derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex)
-       _, err := ParsePKCS8PrivateKey(derBytes)
-       if err != nil {
-               t.Errorf("failed to decode PKCS8 key: %s", err)
+       derBytes, _ := hex.DecodeString(pkcs8RSAPrivateKeyHex)
+       if _, err := ParsePKCS8PrivateKey(derBytes); err != nil {
+               t.Errorf("failed to decode PKCS8 with RSA private key: %s", err)
+       }
+
+       derBytes, _ = hex.DecodeString(pkcs8ECPrivateKeyHex)
+       if _, err := ParsePKCS8PrivateKey(derBytes); err != nil {
+               t.Errorf("failed to decode PKCS8 with EC private key: %s", err)
        }
 }
diff --git a/src/pkg/crypto/x509/sec1.go b/src/pkg/crypto/x509/sec1.go
new file mode 100644 (file)
index 0000000..8a2840f
--- /dev/null
@@ -0,0 +1,69 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+import (
+       "crypto/ecdsa"
+       "crypto/elliptic"
+       "encoding/asn1"
+       "errors"
+       "fmt"
+       "math/big"
+)
+
+const ecPrivKeyVersion = 1
+
+// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
+// References:
+//   RFC5915
+//   SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf
+// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
+// most cases it is not.
+type ecPrivateKey struct {
+       Version       int
+       PrivateKey    []byte
+       NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
+       PublicKey     asn1.BitString        `asn1:"optional,explicit,tag:1"`
+}
+
+// ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
+func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
+       return parseECPrivateKey(nil, der)
+}
+
+// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
+// The OID for the named curve may be provided from another source (such as
+// the PKCS8 container) - if it is provided then use this instead of the OID
+// that may exist in the EC private key structure.
+func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
+       var privKey ecPrivateKey
+       if _, err := asn1.Unmarshal(der, &privKey); err != nil {
+               return nil, errors.New("crypto/x509: failed to parse EC private key: " + err.Error())
+       }
+       if privKey.Version != ecPrivKeyVersion {
+               return nil, fmt.Errorf("crypto/x509: unknown EC private key version %d", privKey.Version)
+       }
+
+       var curve elliptic.Curve
+       if namedCurveOID != nil {
+               curve = namedCurveFromOID(*namedCurveOID)
+       } else {
+               curve = namedCurveFromOID(privKey.NamedCurveOID)
+       }
+       if curve == nil {
+               return nil, errors.New("crypto/x509: unknown elliptic curve")
+       }
+
+       k := new(big.Int).SetBytes(privKey.PrivateKey)
+       if k.Cmp(curve.Params().N) >= 0 {
+               return nil, errors.New("crypto/x509: invalid elliptic curve private key value")
+       }
+       priv := new(ecdsa.PrivateKey)
+       priv.Curve = curve
+       priv.D = k
+       priv.X, priv.Y = curve.ScalarBaseMult(privKey.PrivateKey)
+
+       return priv, nil
+}
diff --git a/src/pkg/crypto/x509/sec1_test.go b/src/pkg/crypto/x509/sec1_test.go
new file mode 100644 (file)
index 0000000..7135699
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+import (
+       "encoding/hex"
+       "testing"
+)
+
+// Generated using:
+//   openssl ecparam -genkey -name secp384r1 -outform PEM
+var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50`
+
+func TestParseECPrivateKey(t *testing.T) {
+       derBytes, _ := hex.DecodeString(ecPrivateKeyHex)
+       _, err := ParseECPrivateKey(derBytes)
+       if err != nil {
+               t.Errorf("failed to decode EC private key: %s", err)
+       }
+}