]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/x509: add code for dealing with PKIX public keys.
authorAdam Langley <agl@golang.org>
Fri, 14 Oct 2011 19:11:21 +0000 (15:11 -0400)
committerAdam Langley <agl@golang.org>
Fri, 14 Oct 2011 19:11:21 +0000 (15:11 -0400)
We also have functions for dealing with PKCS#1 private keys. This
change adds functions for PKIX /public/ keys. Most of the time one
won't be parsing them because they usually come in certificates, but
marshaling them happens and I've previously copied the code from
x509.go for this.

R=bradfitz, rsc
CC=golang-dev
https://golang.org/cl/5286042

src/pkg/crypto/x509/Makefile
src/pkg/crypto/x509/pkcs1.go [new file with mode: 0644]
src/pkg/crypto/x509/x509.go
src/pkg/crypto/x509/x509_test.go

index 14ffd095f106a6e929d5b6fe2f58add16fb63e92..abc43abb6c1d1a4ec7c318a5de46c0cd95cc47f1 100644 (file)
@@ -7,6 +7,7 @@ include ../../../Make.inc
 TARG=crypto/x509
 GOFILES=\
        cert_pool.go\
+       pkcs1.go\
        verify.go\
        x509.go\
 
diff --git a/src/pkg/crypto/x509/pkcs1.go b/src/pkg/crypto/x509/pkcs1.go
new file mode 100644 (file)
index 0000000..42afcb4
--- /dev/null
@@ -0,0 +1,122 @@
+// Copyright 2011 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 (
+       "asn1"
+       "big"
+       "os"
+       "crypto/rsa"
+)
+
+// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
+type pkcs1PrivateKey struct {
+       Version int
+       N       *big.Int
+       E       int
+       D       *big.Int
+       P       *big.Int
+       Q       *big.Int
+       // We ignore these values, if present, because rsa will calculate them.
+       Dp   *big.Int `asn1:"optional"`
+       Dq   *big.Int `asn1:"optional"`
+       Qinv *big.Int `asn1:"optional"`
+
+       AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
+}
+
+type pkcs1AdditionalRSAPrime struct {
+       Prime *big.Int
+
+       // We ignore these values because rsa will calculate them.
+       Exp   *big.Int
+       Coeff *big.Int
+}
+
+// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
+func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {
+       var priv pkcs1PrivateKey
+       rest, err := asn1.Unmarshal(der, &priv)
+       if len(rest) > 0 {
+               err = asn1.SyntaxError{"trailing data"}
+               return
+       }
+       if err != nil {
+               return
+       }
+
+       if priv.Version > 1 {
+               return nil, os.NewError("x509: unsupported private key version")
+       }
+
+       if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
+               return nil, os.NewError("private key contains zero or negative value")
+       }
+
+       key = new(rsa.PrivateKey)
+       key.PublicKey = rsa.PublicKey{
+               E: priv.E,
+               N: priv.N,
+       }
+
+       key.D = priv.D
+       key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
+       key.Primes[0] = priv.P
+       key.Primes[1] = priv.Q
+       for i, a := range priv.AdditionalPrimes {
+               if a.Prime.Sign() <= 0 {
+                       return nil, os.NewError("private key contains zero or negative prime")
+               }
+               key.Primes[i+2] = a.Prime
+               // We ignore the other two values because rsa will calculate
+               // them as needed.
+       }
+
+       err = key.Validate()
+       if err != nil {
+               return nil, err
+       }
+       key.Precompute()
+
+       return
+}
+
+// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
+func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
+       key.Precompute()
+
+       version := 0
+       if len(key.Primes) > 2 {
+               version = 1
+       }
+
+       priv := pkcs1PrivateKey{
+               Version: version,
+               N:       key.N,
+               E:       key.PublicKey.E,
+               D:       key.D,
+               P:       key.Primes[0],
+               Q:       key.Primes[1],
+               Dp:      key.Precomputed.Dp,
+               Dq:      key.Precomputed.Dq,
+               Qinv:    key.Precomputed.Qinv,
+       }
+
+       priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
+       for i, values := range key.Precomputed.CRTValues {
+               priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
+               priv.AdditionalPrimes[i].Exp = values.Exp
+               priv.AdditionalPrimes[i].Coeff = values.Coeff
+       }
+
+       b, _ := asn1.Marshal(priv)
+       return b
+}
+
+// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key.
+type rsaPublicKey struct {
+       N *big.Int
+       E int
+}
index 5e08df752a2f68e66008c3ab7d4429e6b5c88586..4b8ecc56c5e18441dee80d118907bd8e430304d3 100644 (file)
@@ -20,108 +20,59 @@ import (
        "time"
 )
 
-// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
-type pkcs1PrivateKey struct {
-       Version int
-       N       *big.Int
-       E       int
-       D       *big.Int
-       P       *big.Int
-       Q       *big.Int
-       // We ignore these values, if present, because rsa will calculate them.
-       Dp   *big.Int `asn1:"optional"`
-       Dq   *big.Int `asn1:"optional"`
-       Qinv *big.Int `asn1:"optional"`
-
-       AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
+// pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo
+// in RFC 3280.
+type pkixPublicKey struct {
+       Algo      pkix.AlgorithmIdentifier
+       BitString asn1.BitString
 }
 
-type pkcs1AdditionalRSAPrime struct {
-       Prime *big.Int
-
-       // We ignore these values because rsa will calculate them.
-       Exp   *big.Int
-       Coeff *big.Int
-}
-
-// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
-func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {
-       var priv pkcs1PrivateKey
-       rest, err := asn1.Unmarshal(der, &priv)
-       if len(rest) > 0 {
-               err = asn1.SyntaxError{"trailing data"}
+// ParsePKIXPublicKey parses a DER encoded public key. These values are
+// typically found in PEM blocks with "BEGIN PUBLIC KEY".
+func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err os.Error) {
+       var pki publicKeyInfo
+       if _, err = asn1.Unmarshal(derBytes, &pki); err != nil {
                return
        }
-       if err != nil {
-               return
-       }
-
-       if priv.Version > 1 {
-               return nil, os.NewError("x509: unsupported private key version")
-       }
-
-       if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
-               return nil, os.NewError("private key contains zero or negative value")
-       }
-
-       key = new(rsa.PrivateKey)
-       key.PublicKey = rsa.PublicKey{
-               E: priv.E,
-               N: priv.N,
-       }
-
-       key.D = priv.D
-       key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
-       key.Primes[0] = priv.P
-       key.Primes[1] = priv.Q
-       for i, a := range priv.AdditionalPrimes {
-               if a.Prime.Sign() <= 0 {
-                       return nil, os.NewError("private key contains zero or negative prime")
-               }
-               key.Primes[i+2] = a.Prime
-               // We ignore the other two values because rsa will calculate
-               // them as needed.
+       algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm)
+       if algo == UnknownPublicKeyAlgorithm {
+               return nil, os.NewError("ParsePKIXPublicKey: unknown public key algorithm")
        }
-
-       err = key.Validate()
-       if err != nil {
-               return nil, err
-       }
-       key.Precompute()
-
-       return
+       return parsePublicKey(algo, &pki)
 }
 
-// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
-func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
-       key.Precompute()
-
-       version := 0
-       if len(key.Primes) > 2 {
-               version = 1
-       }
+// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format.
+func MarshalPKIXPublicKey(pub interface{}) ([]byte, os.Error) {
+       var pubBytes []byte
 
-       priv := pkcs1PrivateKey{
-               Version: version,
-               N:       key.N,
-               E:       key.PublicKey.E,
-               D:       key.D,
-               P:       key.Primes[0],
-               Q:       key.Primes[1],
-               Dp:      key.Precomputed.Dp,
-               Dq:      key.Precomputed.Dq,
-               Qinv:    key.Precomputed.Qinv,
+       switch pub := pub.(type) {
+       case *rsa.PublicKey:
+               pubBytes, _ = asn1.Marshal(rsaPublicKey{
+                       N: pub.N,
+                       E: pub.E,
+               })
+       default:
+               return nil, os.NewError("MarshalPKIXPublicKey: unknown public key type")
        }
 
-       priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
-       for i, values := range key.Precomputed.CRTValues {
-               priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
-               priv.AdditionalPrimes[i].Exp = values.Exp
-               priv.AdditionalPrimes[i].Coeff = values.Coeff
+       pkix := pkixPublicKey{
+               Algo: pkix.AlgorithmIdentifier{
+                       Algorithm: []int{1, 2, 840, 113549, 1, 1, 1},
+                       // This is a NULL parameters value which is technically
+                       // superfluous, but most other code includes it and, by
+                       // doing this, we match their public key hashes.
+                       Parameters: asn1.RawValue{
+                               Tag: 5,
+                       },
+               },
+               BitString: asn1.BitString{
+                       Bytes:     pubBytes,
+                       BitLength: 8 * len(pubBytes),
+               },
        }
 
-       b, _ := asn1.Marshal(priv)
-       return b
+       ret, _ := asn1.Marshal(pkix)
+       return ret, nil
 }
 
 // These structures reflect the ASN.1 structure of X.509 certificates.:
@@ -485,11 +436,6 @@ type basicConstraints struct {
        MaxPathLen int  `asn1:"optional"`
 }
 
-type rsaPublicKey struct {
-       N *big.Int
-       E int
-}
-
 // RFC 5280 4.2.1.4
 type policyInformation struct {
        Policy asn1.ObjectIdentifier
index dc216505efe8feb668fd19c1ab616ebba5adb35f..dbc5273ca1244dcf0d8b259b0b39348bf402c8ae 100644 (file)
@@ -6,6 +6,7 @@ package x509
 
 import (
        "asn1"
+       "bytes"
        "big"
        "crypto/dsa"
        "crypto/rand"
@@ -34,6 +35,40 @@ func TestParsePKCS1PrivateKey(t *testing.T) {
        }
 }
 
+func TestParsePKIXPublicKey(t *testing.T) {
+       block, _ := pem.Decode([]byte(pemPublicKey))
+       pub, err := ParsePKIXPublicKey(block.Bytes)
+       if err != nil {
+               t.Errorf("Failed to parse RSA public key: %s", err)
+               return
+       }
+       rsaPub, ok := pub.(*rsa.PublicKey)
+       if !ok {
+               t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key")
+               return
+       }
+
+       pubBytes2, err := MarshalPKIXPublicKey(rsaPub)
+       if err != nil {
+               t.Errorf("Failed to marshal RSA public key for the second time: %s", err)
+               return
+       }
+       if !bytes.Equal(pubBytes2, block.Bytes) {
+               t.Errorf("Reserialization of public key didn't match. got %x, want %x", pubBytes2, block.Bytes)
+       }
+}
+
+var pemPublicKey = `-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3VoPN9PKUjKFLMwOge6+
+wnDi8sbETGIx2FKXGgqtAKpzmem53kRGEQg8WeqRmp12wgp74TGpkEXsGae7RS1k
+enJCnma4fii+noGH7R0qKgHvPrI2Bwa9hzsH8tHxpyM3qrXslOmD45EH9SxIDUBJ
+FehNdaPbLP1gFyahKMsdfxFJLUvbUycuZSJ2ZnIgeVxwm4qbSvZInL9Iu4FzuPtg
+fINKcbbovy1qq4KvPIrXzhbY3PWDc6btxCf3SE0JdE1MCPThntB62/bLMSQ7xdDR
+FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ
++QIDAQAB
+-----END PUBLIC KEY-----
+`
+
 var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
 MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
 fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu