]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/x509: expose arbitary X.509 extensions.
authorAdam Langley <agl@golang.org>
Fri, 30 Aug 2013 14:14:45 +0000 (10:14 -0400)
committerAdam Langley <agl@golang.org>
Fri, 30 Aug 2013 14:14:45 +0000 (10:14 -0400)
This change allows people who want to parse or set odd X.509 extensions
to do so without having to add support for them all to the package.

I tried to make it so that only a single member: Extensions would be
needed. However, that would mean detecting when the caller had altered
the contents of it so that parsing and marshaling a certificate
wouldn't ignore all changes to the other members. This ended up being
messy, thus the current design where there are two members: one for
reading and another for writing.

As crypto/x509 adds support for more extensions in the future, the raw
extensions will still be in Extensions for older code that expects it
there. Also, future extensions will be overridden by any raw extensions
added to ExtraExtensions by code that was written before support was
added.

R=golang-dev, r
CC=golang-dev, jpsugar
https://golang.org/cl/12056043

src/pkg/crypto/x509/x509.go
src/pkg/crypto/x509/x509_test.go

index f8561d3e567eb3d6130dae26809958df05adf2d4..4abe1f08485026d9b53a291b81ac1a28fccf572c 100644 (file)
@@ -453,6 +453,18 @@ type Certificate struct {
        NotBefore, NotAfter time.Time // Validity bounds.
        KeyUsage            KeyUsage
 
+       // Extensions contains raw X.509 extensions. When parsing certificates,
+       // this can be used to extract non-critical extensions that are not
+       // parsed by this package. When marshaling certificates, the Extensions
+       // field is ignored, see ExtraExtensions.
+       Extensions []pkix.Extension
+
+       // ExtraExtensions contains extensions to be copied, raw, into any
+       // marshaled certificates. Values override any extensions that would
+       // otherwise be produced based on the other fields. The ExtraExtensions
+       // field is not populated when parsing certificates, see Extensions.
+       ExtraExtensions []pkix.Extension
+
        ExtKeyUsage        []ExtKeyUsage           // Sequence of extended key usages.
        UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package.
 
@@ -798,6 +810,8 @@ func parseCertificate(in *certificate) (*Certificate, error) {
        out.NotAfter = in.TBSCertificate.Validity.NotAfter
 
        for _, e := range in.TBSCertificate.Extensions {
+               out.Extensions = append(out.Extensions, e)
+
                if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 {
                        switch e.Id[3] {
                        case 15:
@@ -1104,11 +1118,23 @@ var (
        oidAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2}
 )
 
+// oidNotInExtensions returns whether an extension with the given oid exists in
+// extensions.
+func oidInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) bool {
+       for _, e := range extensions {
+               if e.Id.Equal(oid) {
+                       return true
+               }
+       }
+       return false
+}
+
 func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
        ret = make([]pkix.Extension, 10 /* maximum number of elements. */)
        n := 0
 
-       if template.KeyUsage != 0 {
+       if template.KeyUsage != 0 &&
+               !oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) {
                ret[n].Id = oidExtensionKeyUsage
                ret[n].Critical = true
 
@@ -1128,7 +1154,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0 {
+       if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) &&
+               !oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) {
                ret[n].Id = oidExtensionExtendedKeyUsage
 
                var oids []asn1.ObjectIdentifier
@@ -1149,7 +1176,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if template.BasicConstraintsValid {
+       if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) {
                ret[n].Id = oidExtensionBasicConstraints
                ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen})
                ret[n].Critical = true
@@ -1159,7 +1186,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if len(template.SubjectKeyId) > 0 {
+       if len(template.SubjectKeyId) > 0 && !oidInExtensions(oidExtensionSubjectKeyId, template.ExtraExtensions) {
                ret[n].Id = oidExtensionSubjectKeyId
                ret[n].Value, err = asn1.Marshal(template.SubjectKeyId)
                if err != nil {
@@ -1168,7 +1195,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if len(template.AuthorityKeyId) > 0 {
+       if len(template.AuthorityKeyId) > 0 && !oidInExtensions(oidExtensionAuthorityKeyId, template.ExtraExtensions) {
                ret[n].Id = oidExtensionAuthorityKeyId
                ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId})
                if err != nil {
@@ -1177,7 +1204,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if len(template.OCSPServer) > 0 || len(template.IssuingCertificateURL) > 0 {
+       if (len(template.OCSPServer) > 0 || len(template.IssuingCertificateURL) > 0) &&
+               !oidInExtensions(oidExtensionAuthorityInfoAccess, template.ExtraExtensions) {
                ret[n].Id = oidExtensionAuthorityInfoAccess
                var aiaValues []authorityInfoAccess
                for _, name := range template.OCSPServer {
@@ -1199,7 +1227,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 {
+       if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0) &&
+               !oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) {
                ret[n].Id = oidExtensionSubjectAltName
                var rawValues []asn1.RawValue
                for _, name := range template.DNSNames {
@@ -1223,7 +1252,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if len(template.PolicyIdentifiers) > 0 {
+       if len(template.PolicyIdentifiers) > 0 &&
+               !oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
                ret[n].Id = oidExtensionCertificatePolicies
                policies := make([]policyInformation, len(template.PolicyIdentifiers))
                for i, policy := range template.PolicyIdentifiers {
@@ -1236,7 +1266,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if len(template.PermittedDNSDomains) > 0 {
+       if len(template.PermittedDNSDomains) > 0 &&
+               !oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) {
                ret[n].Id = oidExtensionNameConstraints
                ret[n].Critical = template.PermittedDNSDomainsCritical
 
@@ -1252,7 +1283,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
                n++
        }
 
-       if len(template.CRLDistributionPoints) > 0 {
+       if len(template.CRLDistributionPoints) > 0 &&
+               !oidInExtensions(oidExtensionCRLDistributionPoints, template.ExtraExtensions) {
                ret[n].Id = oidExtensionCRLDistributionPoints
 
                var crlDp []distributionPoint
@@ -1277,7 +1309,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
        // Adding another extension here? Remember to update the maximum number
        // of elements in the make() at the top of the function.
 
-       return ret[0:n], nil
+       return append(ret[:n], template.ExtraExtensions...), nil
 }
 
 func subjectBytes(cert *Certificate) ([]byte, error) {
index 5671b56a6d2f48ea5f472d3145e0122a59ec688a..f1097e992e706dab810404c8c38aabf87f917957 100644 (file)
@@ -237,6 +237,11 @@ func TestCertificateParse(t *testing.T) {
        if err := certs[0].VerifyHostname("mail.google.com"); err != nil {
                t.Error(err)
        }
+
+       const expectedExtensions = 4
+       if n := len(certs[0].Extensions); n != expectedExtensions {
+               t.Errorf("want %d extensions, got %d", expectedExtensions, n)
+       }
 }
 
 var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be1300d06092a864886" +
@@ -309,6 +314,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
 
        testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
        testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}}
+       extraExtensionData := []byte("extra extension")
 
        for _, test := range tests {
                commonName := "test.example.com"
@@ -341,6 +347,19 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
                        PermittedDNSDomains: []string{".example.com", "example.com"},
 
                        CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},
+
+                       ExtraExtensions: []pkix.Extension{
+                               {
+                                       Id:    []int{1, 2, 3, 4},
+                                       Value: extraExtensionData,
+                               },
+                               // This extension should override the SubjectKeyId, above.
+                               {
+                                       Id:       oidExtensionSubjectKeyId,
+                                       Critical: false,
+                                       Value:    []byte{0x04, 0x04, 4, 3, 2, 1},
+                               },
+                       },
                }
 
                derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
@@ -403,6 +422,14 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
                        t.Errorf("%s: CRL distribution points differ from template. Got %v, want %v", test.name, cert.CRLDistributionPoints, template.CRLDistributionPoints)
                }
 
+               if !bytes.Equal(cert.SubjectKeyId, []byte{4, 3, 2, 1}) {
+                       t.Errorf("%s: ExtraExtensions didn't override SubjectKeyId", test.name)
+               }
+
+               if bytes.Index(derBytes, extraExtensionData) == -1 {
+                       t.Errorf("%s: didn't find extra extension in DER output", test.name)
+               }
+
                if test.checkSig {
                        err = cert.CheckSignatureFrom(cert)
                        if err != nil {