1
0
mirror of https://github.com/golang/go synced 2024-11-25 09:57:57 -07:00

crypto/x509: expose arbitary X.509 extensions.

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
This commit is contained in:
Adam Langley 2013-08-30 10:14:45 -04:00
parent 79dca0327e
commit 87404c9887
2 changed files with 70 additions and 11 deletions

View File

@ -453,6 +453,18 @@ type Certificate struct {
NotBefore, NotAfter time.Time // Validity bounds. NotBefore, NotAfter time.Time // Validity bounds.
KeyUsage KeyUsage 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. ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages.
UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package. 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 out.NotAfter = in.TBSCertificate.Validity.NotAfter
for _, e := range in.TBSCertificate.Extensions { 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 { if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 {
switch e.Id[3] { switch e.Id[3] {
case 15: case 15:
@ -1104,11 +1118,23 @@ var (
oidAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2} 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) { func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
ret = make([]pkix.Extension, 10 /* maximum number of elements. */) ret = make([]pkix.Extension, 10 /* maximum number of elements. */)
n := 0 n := 0
if template.KeyUsage != 0 { if template.KeyUsage != 0 &&
!oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) {
ret[n].Id = oidExtensionKeyUsage ret[n].Id = oidExtensionKeyUsage
ret[n].Critical = true ret[n].Critical = true
@ -1128,7 +1154,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ 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 ret[n].Id = oidExtensionExtendedKeyUsage
var oids []asn1.ObjectIdentifier var oids []asn1.ObjectIdentifier
@ -1149,7 +1176,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ n++
} }
if template.BasicConstraintsValid { if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) {
ret[n].Id = oidExtensionBasicConstraints ret[n].Id = oidExtensionBasicConstraints
ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen}) ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen})
ret[n].Critical = true ret[n].Critical = true
@ -1159,7 +1186,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ n++
} }
if len(template.SubjectKeyId) > 0 { if len(template.SubjectKeyId) > 0 && !oidInExtensions(oidExtensionSubjectKeyId, template.ExtraExtensions) {
ret[n].Id = oidExtensionSubjectKeyId ret[n].Id = oidExtensionSubjectKeyId
ret[n].Value, err = asn1.Marshal(template.SubjectKeyId) ret[n].Value, err = asn1.Marshal(template.SubjectKeyId)
if err != nil { if err != nil {
@ -1168,7 +1195,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ n++
} }
if len(template.AuthorityKeyId) > 0 { if len(template.AuthorityKeyId) > 0 && !oidInExtensions(oidExtensionAuthorityKeyId, template.ExtraExtensions) {
ret[n].Id = oidExtensionAuthorityKeyId ret[n].Id = oidExtensionAuthorityKeyId
ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId}) ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId})
if err != nil { if err != nil {
@ -1177,7 +1204,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ 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 ret[n].Id = oidExtensionAuthorityInfoAccess
var aiaValues []authorityInfoAccess var aiaValues []authorityInfoAccess
for _, name := range template.OCSPServer { for _, name := range template.OCSPServer {
@ -1199,7 +1227,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ 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 ret[n].Id = oidExtensionSubjectAltName
var rawValues []asn1.RawValue var rawValues []asn1.RawValue
for _, name := range template.DNSNames { for _, name := range template.DNSNames {
@ -1223,7 +1252,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ n++
} }
if len(template.PolicyIdentifiers) > 0 { if len(template.PolicyIdentifiers) > 0 &&
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
ret[n].Id = oidExtensionCertificatePolicies ret[n].Id = oidExtensionCertificatePolicies
policies := make([]policyInformation, len(template.PolicyIdentifiers)) policies := make([]policyInformation, len(template.PolicyIdentifiers))
for i, policy := range template.PolicyIdentifiers { for i, policy := range template.PolicyIdentifiers {
@ -1236,7 +1266,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ n++
} }
if len(template.PermittedDNSDomains) > 0 { if len(template.PermittedDNSDomains) > 0 &&
!oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) {
ret[n].Id = oidExtensionNameConstraints ret[n].Id = oidExtensionNameConstraints
ret[n].Critical = template.PermittedDNSDomainsCritical ret[n].Critical = template.PermittedDNSDomainsCritical
@ -1252,7 +1283,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ n++
} }
if len(template.CRLDistributionPoints) > 0 { if len(template.CRLDistributionPoints) > 0 &&
!oidInExtensions(oidExtensionCRLDistributionPoints, template.ExtraExtensions) {
ret[n].Id = oidExtensionCRLDistributionPoints ret[n].Id = oidExtensionCRLDistributionPoints
var crlDp []distributionPoint 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 // Adding another extension here? Remember to update the maximum number
// of elements in the make() at the top of the function. // 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) { func subjectBytes(cert *Certificate) ([]byte, error) {

View File

@ -237,6 +237,11 @@ func TestCertificateParse(t *testing.T) {
if err := certs[0].VerifyHostname("mail.google.com"); err != nil { if err := certs[0].VerifyHostname("mail.google.com"); err != nil {
t.Error(err) 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" + var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be1300d06092a864886" +
@ -309,6 +314,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth} testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}} testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}}
extraExtensionData := []byte("extra extension")
for _, test := range tests { for _, test := range tests {
commonName := "test.example.com" commonName := "test.example.com"
@ -341,6 +347,19 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
PermittedDNSDomains: []string{".example.com", "example.com"}, PermittedDNSDomains: []string{".example.com", "example.com"},
CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"}, 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) 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) 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 { if test.checkSig {
err = cert.CheckSignatureFrom(cert) err = cert.CheckSignatureFrom(cert)
if err != nil { if err != nil {