Commit bbb5f1bf authored by Adam Langley's avatar Adam Langley

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
parent 89d40b91
......@@ -28,7 +28,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
return nil, err
}
switch {
case privKey.Algo.Algorithm.Equal(oidRSA):
case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
if err != nil {
return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
......
......@@ -262,18 +262,18 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
// id-ecPublicKey OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
var (
oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
oidPublicKeyEcdsa = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
)
func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
switch {
case oid.Equal(oidPublicKeyRsa):
case oid.Equal(oidPublicKeyRSA):
return RSA
case oid.Equal(oidPublicKeyDsa):
case oid.Equal(oidPublicKeyDSA):
return DSA
case oid.Equal(oidPublicKeyEcdsa):
case oid.Equal(oidPublicKeyECDSA):
return ECDSA
}
return UnknownPublicKeyAlgorithm
......@@ -302,7 +302,7 @@ var (
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
)
func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
switch {
case oid.Equal(oidNamedCurveP224):
return elliptic.P224()
......@@ -316,6 +316,21 @@ func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
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
// a bitmap of the KeyUsage* constants.
type KeyUsage int
......@@ -648,7 +663,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
if err != nil {
return nil, err
}
namedCurve := getNamedCurveFromOID(*namedCurveOID)
namedCurve := namedCurveFromOID(*namedCurveOID)
if namedCurve == nil {
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
}
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) {
if len(cert.RawSubject) > 0 {
return cert.RawSubject, nil
......@@ -1093,23 +1103,61 @@ func subjectBytes(cert *Certificate) ([]byte, error) {
//
// The returned slice is the certificate in DER encoding.
//
// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey
// for priv).
// The only supported key types are RSA and ECDSA (*rsa.PublicKey or
// *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) {
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
return nil, errors.New("x509: non-RSA public keys not supported")
var publicKeyBytes []byte
var publicKeyAlgorithm pkix.AlgorithmIdentifier
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)
if !ok {
return nil, errors.New("x509: non-RSA private keys not supported")
var signatureAlgorithm pkix.AlgorithmIdentifier
var hashFunc crypto.Hash
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 {
return
}
......@@ -1133,15 +1181,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
return
}
encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey}
encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
c := tbsCertificate{
Version: 2,
SerialNumber: template.SerialNumber,
SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
SignatureAlgorithm: signatureAlgorithm,
Issuer: asn1.RawValue{FullBytes: asn1Issuer},
Validity: validity{template.NotBefore, template.NotAfter},
Subject: asn1.RawValue{FullBytes: asn1Subject},
PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey},
PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
Extensions: extensions,
}
......@@ -1152,11 +1200,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
c.Raw = tbsCertContents
h := sha1.New()
h := hashFunc.New()
h.Write(tbsCertContents)
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 {
return
}
......@@ -1164,7 +1225,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
cert, err = asn1.Marshal(certificate{
nil,
c,
pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
signatureAlgorithm,
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
})
return
......
......@@ -8,6 +8,7 @@ import (
"bytes"
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
_ "crypto/sha256"
......@@ -240,65 +241,83 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
random := rand.Reader
block, _ := pem.Decode([]byte(pemPrivateKey))
priv, err := ParsePKCS1PrivateKey(block.Bytes)
rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
t.Errorf("Failed to parse private key: %s", err)
return
}
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),
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"},
t.Fatalf("Failed to parse private key: %s", err)
}
derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv)
ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Errorf("Failed to create certificate: %s", err)
return
}
t.Fatalf("Failed to generate ECDSA key: %s", err)
}
tests := []struct {
name string
pub, priv interface{}
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},
}
for _, test := range tests {
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),
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"},
}
cert, err := ParseCertificate(derBytes)
if err != nil {
t.Errorf("Failed to parse certificate: %s", err)
return
}
derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
if err != nil {
t.Errorf("%s: failed to create certificate: %s", test.name, err)
continue
}
if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
t.Errorf("Failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers)
}
cert, err := ParseCertificate(derBytes)
if err != nil {
t.Errorf("%s: failed to parse certificate: %s", test.name, err)
continue
}
if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {
t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains)
}
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 cert.Subject.CommonName != commonName {
t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName)
}
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.Issuer.CommonName != commonName {
t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName)
}
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)
}
err = cert.CheckSignatureFrom(cert)
if err != nil {
t.Errorf("Signature verification failed: %s", err)
return
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)
}
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment