1
0
mirror of https://github.com/golang/go synced 2024-10-04 06:11:21 -06:00

crypto/x509: support ECDSA keys when generating certificates.

We already support reading ECDSA certificates and this change adds
write support.

R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/6422046
This commit is contained in:
Adam Langley 2012-08-03 10:37:30 -04:00
parent 89d40b911c
commit bbb5f1bffb
3 changed files with 159 additions and 79 deletions

View File

@ -28,7 +28,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
return nil, err return nil, err
} }
switch { switch {
case privKey.Algo.Algorithm.Equal(oidRSA): case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey) key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
if err != nil { if err != nil {
return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())

View File

@ -262,18 +262,18 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
// id-ecPublicKey OBJECT IDENTIFIER ::= { // id-ecPublicKey OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } // iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
var ( var (
oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
oidPublicKeyEcdsa = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
) )
func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm { func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
switch { switch {
case oid.Equal(oidPublicKeyRsa): case oid.Equal(oidPublicKeyRSA):
return RSA return RSA
case oid.Equal(oidPublicKeyDsa): case oid.Equal(oidPublicKeyDSA):
return DSA return DSA
case oid.Equal(oidPublicKeyEcdsa): case oid.Equal(oidPublicKeyECDSA):
return ECDSA return ECDSA
} }
return UnknownPublicKeyAlgorithm return UnknownPublicKeyAlgorithm
@ -302,7 +302,7 @@ var (
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
) )
func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve { func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
switch { switch {
case oid.Equal(oidNamedCurveP224): case oid.Equal(oidNamedCurveP224):
return elliptic.P224() return elliptic.P224()
@ -316,6 +316,21 @@ func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
return nil return nil
} }
func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
switch curve {
case elliptic.P224():
return oidNamedCurveP224, true
case elliptic.P256():
return oidNamedCurveP256, true
case elliptic.P384():
return oidNamedCurveP384, true
case elliptic.P521():
return oidNamedCurveP521, true
}
return nil, false
}
// KeyUsage represents the set of actions that are valid for a given key. It's // KeyUsage represents the set of actions that are valid for a given key. It's
// a bitmap of the KeyUsage* constants. // a bitmap of the KeyUsage* constants.
type KeyUsage int type KeyUsage int
@ -648,7 +663,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
if err != nil { if err != nil {
return nil, err return nil, err
} }
namedCurve := getNamedCurveFromOID(*namedCurveOID) namedCurve := namedCurveFromOID(*namedCurveOID)
if namedCurve == nil { if namedCurve == nil {
return nil, errors.New("crypto/x509: unsupported elliptic curve") return nil, errors.New("crypto/x509: unsupported elliptic curve")
} }
@ -1069,11 +1084,6 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
return ret[0:n], nil return ret[0:n], nil
} }
var (
oidSHA1WithRSA = []int{1, 2, 840, 113549, 1, 1, 5}
oidRSA = []int{1, 2, 840, 113549, 1, 1, 1}
)
func subjectBytes(cert *Certificate) ([]byte, error) { func subjectBytes(cert *Certificate) ([]byte, error) {
if len(cert.RawSubject) > 0 { if len(cert.RawSubject) > 0 {
return cert.RawSubject, nil return cert.RawSubject, nil
@ -1093,23 +1103,61 @@ func subjectBytes(cert *Certificate) ([]byte, error) {
// //
// The returned slice is the certificate in DER encoding. // The returned slice is the certificate in DER encoding.
// //
// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey // The only supported key types are RSA and ECDSA (*rsa.PublicKey or
// for priv). // *ecdsa.PublicKey for pub, *rsa.PrivateKey or *ecdsa.PublicKey for priv).
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) { func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) {
rsaPub, ok := pub.(*rsa.PublicKey) var publicKeyBytes []byte
if !ok { var publicKeyAlgorithm pkix.AlgorithmIdentifier
return nil, errors.New("x509: non-RSA public keys not supported")
switch pub := pub.(type) {
case *rsa.PublicKey:
publicKeyBytes, err = asn1.Marshal(rsaPublicKey{
N: pub.N,
E: pub.E,
})
publicKeyAlgorithm.Algorithm = oidPublicKeyRSA
case *ecdsa.PublicKey:
oid, ok := oidFromNamedCurve(pub.Curve)
if !ok {
return nil, errors.New("x509: unknown elliptic curve")
}
publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
var paramBytes []byte
paramBytes, err = asn1.Marshal(oid)
if err != nil {
return
}
publicKeyAlgorithm.Parameters.FullBytes = paramBytes
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
default:
return nil, errors.New("x509: only RSA and ECDSA public keys supported")
} }
rsaPriv, ok := priv.(*rsa.PrivateKey) var signatureAlgorithm pkix.AlgorithmIdentifier
if !ok { var hashFunc crypto.Hash
return nil, errors.New("x509: non-RSA private keys not supported")
switch priv := priv.(type) {
case *rsa.PrivateKey:
signatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA
hashFunc = crypto.SHA1
case *ecdsa.PrivateKey:
switch priv.Curve {
case elliptic.P224(), elliptic.P256():
hashFunc = crypto.SHA256
signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256
case elliptic.P384():
hashFunc = crypto.SHA384
signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384
case elliptic.P521():
hashFunc = crypto.SHA512
signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512
default:
return nil, errors.New("x509: unknown elliptic curve")
}
default:
return nil, errors.New("x509: only RSA and ECDSA private keys supported")
} }
asn1PublicKey, err := asn1.Marshal(rsaPublicKey{
N: rsaPub.N,
E: rsaPub.E,
})
if err != nil { if err != nil {
return return
} }
@ -1133,15 +1181,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
return return
} }
encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
c := tbsCertificate{ c := tbsCertificate{
Version: 2, Version: 2,
SerialNumber: template.SerialNumber, SerialNumber: template.SerialNumber,
SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, SignatureAlgorithm: signatureAlgorithm,
Issuer: asn1.RawValue{FullBytes: asn1Issuer}, Issuer: asn1.RawValue{FullBytes: asn1Issuer},
Validity: validity{template.NotBefore, template.NotAfter}, Validity: validity{template.NotBefore, template.NotAfter},
Subject: asn1.RawValue{FullBytes: asn1Subject}, Subject: asn1.RawValue{FullBytes: asn1Subject},
PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
Extensions: extensions, Extensions: extensions,
} }
@ -1152,11 +1200,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
c.Raw = tbsCertContents c.Raw = tbsCertContents
h := sha1.New() h := hashFunc.New()
h.Write(tbsCertContents) h.Write(tbsCertContents)
digest := h.Sum(nil) digest := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) var signature []byte
switch priv := priv.(type) {
case *rsa.PrivateKey:
signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest)
case *ecdsa.PrivateKey:
var r, s *big.Int
if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil {
signature, err = asn1.Marshal(ecdsaSignature{r, s})
}
default:
panic("internal error")
}
if err != nil { if err != nil {
return return
} }
@ -1164,7 +1225,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
cert, err = asn1.Marshal(certificate{ cert, err = asn1.Marshal(certificate{
nil, nil,
c, c,
pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, signatureAlgorithm,
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
}) })
return return

View File

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
_ "crypto/sha256" _ "crypto/sha256"
@ -240,65 +241,83 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
random := rand.Reader random := rand.Reader
block, _ := pem.Decode([]byte(pemPrivateKey)) block, _ := pem.Decode([]byte(pemPrivateKey))
priv, err := ParsePKCS1PrivateKey(block.Bytes) rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
if err != nil { if err != nil {
t.Errorf("Failed to parse private key: %s", err) t.Fatalf("Failed to parse private key: %s", err)
return
} }
commonName := "test.example.com" ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
template := Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{"Σ Acme Co"},
},
NotBefore: time.Unix(1000, 0),
NotAfter: time.Unix(100000, 0),
SubjectKeyId: []byte{1, 2, 3, 4},
KeyUsage: KeyUsageCertSign,
BasicConstraintsValid: true,
IsCA: true,
DNSNames: []string{"test.example.com"},
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
PermittedDNSDomains: []string{".example.com", "example.com"},
}
derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv)
if err != nil { if err != nil {
t.Errorf("Failed to create certificate: %s", err) t.Fatalf("Failed to generate ECDSA key: %s", err)
return
} }
cert, err := ParseCertificate(derBytes) tests := []struct {
if err != nil { name string
t.Errorf("Failed to parse certificate: %s", err) pub, priv interface{}
return checkSig bool
}{
{"RSA/RSA", &rsaPriv.PublicKey, rsaPriv, true},
{"RSA/ECDSA", &rsaPriv.PublicKey, ecdsaPriv, false},
{"ECDSA/RSA", &ecdsaPriv.PublicKey, rsaPriv, false},
{"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true},
} }
if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) { for _, test := range tests {
t.Errorf("Failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers) commonName := "test.example.com"
} template := Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{"Σ Acme Co"},
},
NotBefore: time.Unix(1000, 0),
NotAfter: time.Unix(100000, 0),
if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" { SubjectKeyId: []byte{1, 2, 3, 4},
t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains) KeyUsage: KeyUsageCertSign,
}
if cert.Subject.CommonName != commonName { BasicConstraintsValid: true,
t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName) IsCA: true,
} DNSNames: []string{"test.example.com"},
if cert.Issuer.CommonName != commonName { PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName) PermittedDNSDomains: []string{".example.com", "example.com"},
} }
err = cert.CheckSignatureFrom(cert) derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
if err != nil { if err != nil {
t.Errorf("Signature verification failed: %s", err) t.Errorf("%s: failed to create certificate: %s", test.name, err)
return continue
}
cert, err := ParseCertificate(derBytes)
if err != nil {
t.Errorf("%s: failed to parse certificate: %s", test.name, err)
continue
}
if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
t.Errorf("%s: failed to parse policy identifiers: got:%#v want:%#v", test.name, cert.PolicyIdentifiers, template.PolicyIdentifiers)
}
if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {
t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
}
if cert.Subject.CommonName != commonName {
t.Errorf("%s: subject wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Subject.CommonName, commonName)
}
if cert.Issuer.CommonName != commonName {
t.Errorf("%s: issuer wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Issuer.CommonName, commonName)
}
if test.checkSig {
err = cert.CheckSignatureFrom(cert)
if err != nil {
t.Errorf("%s: signature verification failed: %s", test.name, err)
}
}
} }
} }