mirror of
https://github.com/golang/go
synced 2024-11-21 15:24:45 -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:
parent
79dca0327e
commit
87404c9887
@ -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) {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user