mirror of
https://github.com/golang/go
synced 2024-11-25 09:17:57 -07: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:
parent
89d40b911c
commit
bbb5f1bffb
@ -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
|
||||
t.Fatalf("Failed to parse private key: %s", err)
|
||||
}
|
||||
|
||||
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"},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
cert, err := ParseCertificate(derBytes)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to parse certificate: %s", err)
|
||||
return
|
||||
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},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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),
|
||||
|
||||
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)
|
||||
}
|
||||
SubjectKeyId: []byte{1, 2, 3, 4},
|
||||
KeyUsage: KeyUsageCertSign,
|
||||
|
||||
if cert.Subject.CommonName != commonName {
|
||||
t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName)
|
||||
}
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
DNSNames: []string{"test.example.com"},
|
||||
|
||||
if cert.Issuer.CommonName != commonName {
|
||||
t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName)
|
||||
}
|
||||
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
|
||||
PermittedDNSDomains: []string{".example.com", "example.com"},
|
||||
}
|
||||
|
||||
err = cert.CheckSignatureFrom(cert)
|
||||
if err != nil {
|
||||
t.Errorf("Signature verification failed: %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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user