]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/x509: add additional convenience fields to CertificateRequest
authorRoland Shoemaker <rolandshoemaker@gmail.com>
Sun, 10 May 2020 18:23:58 +0000 (11:23 -0700)
committerRoland Shoemaker <roland@golang.org>
Mon, 9 Nov 2020 17:24:38 +0000 (17:24 +0000)
Adds the following additional convenience fields to CertificateRequest:
* KeyUsage
* ExtKeyUsage
* UnknownExtKeyUsage
* IsCA
* MaxPathLen
* BasicConstraintsValid
* MaxPathLenZero
* SubjectKeyId
* PolicyIdentifier

These fields are parsed during ParseCertificateRequest and marshalled
during CreateCertificateRequest. The parsing/marshalling code is
factored out of parseCertificate and buildExtensions (which is renamed
buildCertExtensions). This has the side effect of making these methods
somewhat easier to read.

Documentation for the fields is copied from Certificate.

Example CSR created with all of these fields parsed with openssl:
$ openssl req -in ~/test-csr.pem -noout -text
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject:
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:a4:cb:64:35:8e:dd:8c:2b:a6:f1:aa:39:d1:be:
                    d0:b9:95:1e:59:19:82:76:28:d3:85:1b:c6:88:62:
                    e1:15:33:be:26:18:80:14:fe:f4:d4:91:66:4e:a4:
                    a4:47:bd:53:db:f7:2e:e3:31:ce:5f:86:cb:92:59:
                    93:bb:d0:7f:a2
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        Attributes:
        Requested Extensions:
            X509v3 Key Usage: critical
                Certificate Sign
            X509v3 Extended Key Usage:
                Any Extended Key Usage, 1.2.3
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Subject Key Identifier:
                01:02:03
            X509v3 Certificate Policies:
                Policy: 1.2.3

    Signature Algorithm: ecdsa-with-SHA256
         30:45:02:21:00:a7:88:e5:96:d4:ad:ae:24:26:ab:5f:15:6a:
         3f:22:6d:0e:a6:ba:15:64:8d:78:34:f4:c4:7d:ac:37:b0:2a:
         84:02:20:68:44:f0:8e:8a:1b:c1:68:be:14:a6:e3:83:41:fd:
         2d:cc:00:aa:bc:50:f6:50:56:12:9e:a4:09:84:5c:bf:c1

Fixes #37172

Change-Id: Ife79d01e203827ef0ac3c787aa13c00d0751a1ec
Reviewed-on: https://go-review.googlesource.com/c/go/+/233163
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Trust: Katie Hockman <katie@golang.org>
Trust: Roland Shoemaker <roland@golang.org>

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

index 537c207f38fd5847e1e930ee330228bc3c7fab1d..60dfac741b8774017dd967abb868c9ba99a8a44a 100644 (file)
@@ -1338,36 +1338,17 @@ func parseCertificate(in *certificate) (*Certificate, error) {
                if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 {
                        switch e.Id[3] {
                        case 15:
-                               // RFC 5280, 4.2.1.3
-                               var usageBits asn1.BitString
-                               if rest, err := asn1.Unmarshal(e.Value, &usageBits); err != nil {
+                               out.KeyUsage, err = parseKeyUsageExtension(e.Value)
+                               if err != nil {
                                        return nil, err
-                               } else if len(rest) != 0 {
-                                       return nil, errors.New("x509: trailing data after X.509 KeyUsage")
                                }
-
-                               var usage int
-                               for i := 0; i < 9; i++ {
-                                       if usageBits.At(i) != 0 {
-                                               usage |= 1 << uint(i)
-                                       }
-                               }
-                               out.KeyUsage = KeyUsage(usage)
-
                        case 19:
-                               // RFC 5280, 4.2.1.9
-                               var constraints basicConstraints
-                               if rest, err := asn1.Unmarshal(e.Value, &constraints); err != nil {
+                               out.IsCA, out.MaxPathLen, err = parseBasicConstraintsExtension(e.Value)
+                               if err != nil {
                                        return nil, err
-                               } else if len(rest) != 0 {
-                                       return nil, errors.New("x509: trailing data after X.509 BasicConstraints")
                                }
-
                                out.BasicConstraintsValid = true
-                               out.IsCA = constraints.IsCA
-                               out.MaxPathLen = constraints.MaxPathLen
                                out.MaxPathLenZero = out.MaxPathLen == 0
-                               // TODO: map out.MaxPathLen to 0 if it has the -1 default value? (Issue 19285)
                        case 17:
                                out.DNSNames, out.EmailAddresses, out.IPAddresses, out.URIs, err = parseSANExtension(e.Value)
                                if err != nil {
@@ -1430,52 +1411,20 @@ func parseCertificate(in *certificate) (*Certificate, error) {
                                out.AuthorityKeyId = a.Id
 
                        case 37:
-                               // RFC 5280, 4.2.1.12.  Extended Key Usage
-
-                               // id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
-                               //
-                               // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
-                               //
-                               // KeyPurposeId ::= OBJECT IDENTIFIER
-
-                               var keyUsage []asn1.ObjectIdentifier
-                               if rest, err := asn1.Unmarshal(e.Value, &keyUsage); err != nil {
+                               out.ExtKeyUsage, out.UnknownExtKeyUsage, err = parseExtKeyUsageExtension(e.Value)
+                               if err != nil {
                                        return nil, err
-                               } else if len(rest) != 0 {
-                                       return nil, errors.New("x509: trailing data after X.509 ExtendedKeyUsage")
-                               }
-
-                               for _, u := range keyUsage {
-                                       if extKeyUsage, ok := extKeyUsageFromOID(u); ok {
-                                               out.ExtKeyUsage = append(out.ExtKeyUsage, extKeyUsage)
-                                       } else {
-                                               out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u)
-                                       }
                                }
-
                        case 14:
-                               // RFC 5280, 4.2.1.2
-                               var keyid []byte
-                               if rest, err := asn1.Unmarshal(e.Value, &keyid); err != nil {
+                               out.SubjectKeyId, err = parseSubjectKeyIdExtension(e.Value)
+                               if err != nil {
                                        return nil, err
-                               } else if len(rest) != 0 {
-                                       return nil, errors.New("x509: trailing data after X.509 key-id")
                                }
-                               out.SubjectKeyId = keyid
-
                        case 32:
-                               // RFC 5280 4.2.1.4: Certificate Policies
-                               var policies []policyInformation
-                               if rest, err := asn1.Unmarshal(e.Value, &policies); err != nil {
+                               out.PolicyIdentifiers, err = parseCertificatePoliciesExtension(e.Value)
+                               if err != nil {
                                        return nil, err
-                               } else if len(rest) != 0 {
-                                       return nil, errors.New("x509: trailing data after X.509 certificate policies")
-                               }
-                               out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, len(policies))
-                               for i, policy := range policies {
-                                       out.PolicyIdentifiers[i] = policy.Policy
                                }
-
                        default:
                                // Unknown extensions are recorded if critical.
                                unhandled = true
@@ -1513,6 +1462,87 @@ func parseCertificate(in *certificate) (*Certificate, error) {
        return out, nil
 }
 
+// parseKeyUsageExtension parses id-ce-keyUsage (2.5.29.15) from RFC 5280
+// Section 4.2.1.3
+func parseKeyUsageExtension(ext []byte) (KeyUsage, error) {
+       var usageBits asn1.BitString
+       if rest, err := asn1.Unmarshal(ext, &usageBits); err != nil {
+               return 0, err
+       } else if len(rest) != 0 {
+               return 0, errors.New("x509: trailing data after X.509 KeyUsage")
+       }
+
+       var usage int
+       for i := 0; i < 9; i++ {
+               if usageBits.At(i) != 0 {
+                       usage |= 1 << uint(i)
+               }
+       }
+       return KeyUsage(usage), nil
+}
+
+// parseBasicConstraintsExtension parses id-ce-basicConstraints (2.5.29.19)
+// from RFC 5280 Section 4.2.1.9
+func parseBasicConstraintsExtension(ext []byte) (isCA bool, maxPathLen int, err error) {
+       var constraints basicConstraints
+       if rest, err := asn1.Unmarshal(ext, &constraints); err != nil {
+               return false, 0, err
+       } else if len(rest) != 0 {
+               return false, 0, errors.New("x509: trailing data after X.509 BasicConstraints")
+       }
+
+       // TODO: map out.MaxPathLen to 0 if it has the -1 default value? (Issue 19285)
+       return constraints.IsCA, constraints.MaxPathLen, nil
+}
+
+// parseExtKeyUsageExtension parses id-ce-extKeyUsage (2.5.29.37) from
+// RFC 5280 Section 4.2.1.12
+func parseExtKeyUsageExtension(ext []byte) ([]ExtKeyUsage, []asn1.ObjectIdentifier, error) {
+       var keyUsage []asn1.ObjectIdentifier
+       if rest, err := asn1.Unmarshal(ext, &keyUsage); err != nil {
+               return nil, nil, err
+       } else if len(rest) != 0 {
+               return nil, nil, errors.New("x509: trailing data after X.509 ExtendedKeyUsage")
+       }
+
+       var extKeyUsages []ExtKeyUsage
+       var unknownUsages []asn1.ObjectIdentifier
+       for _, u := range keyUsage {
+               if extKeyUsage, ok := extKeyUsageFromOID(u); ok {
+                       extKeyUsages = append(extKeyUsages, extKeyUsage)
+               } else {
+                       unknownUsages = append(unknownUsages, u)
+               }
+       }
+       return extKeyUsages, unknownUsages, nil
+}
+
+// parseSubjectKeyIdExtension parses id-ce-subjectKeyIdentifier (2.5.29.14)
+// from RFC 5280 Section 4.2.1.2
+func parseSubjectKeyIdExtension(ext []byte) ([]byte, error) {
+       var keyid []byte
+       if rest, err := asn1.Unmarshal(ext, &keyid); err != nil {
+               return nil, err
+       } else if len(rest) != 0 {
+               return nil, errors.New("x509: trailing data after X.509 key-id")
+       }
+       return keyid, nil
+}
+
+func parseCertificatePoliciesExtension(ext []byte) ([]asn1.ObjectIdentifier, error) {
+       var policies []policyInformation
+       if rest, err := asn1.Unmarshal(ext, &policies); err != nil {
+               return nil, err
+       } else if len(rest) != 0 {
+               return nil, errors.New("x509: trailing data after X.509 certificate policies")
+       }
+       oids := make([]asn1.ObjectIdentifier, len(policies))
+       for i, policy := range policies {
+               oids[i] = policy.Policy
+       }
+       return oids, nil
+}
+
 // ParseCertificate parses a single certificate from the given ASN.1 DER data.
 func ParseCertificate(asn1Data []byte) (*Certificate, error) {
        var cert certificate
@@ -1656,68 +1686,32 @@ func isIA5String(s string) error {
        return nil
 }
 
-func buildExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId []byte, subjectKeyId []byte) (ret []pkix.Extension, err error) {
+func buildCertExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId []byte, subjectKeyId []byte) (ret []pkix.Extension, err error) {
        ret = make([]pkix.Extension, 10 /* maximum number of elements. */)
        n := 0
 
        if template.KeyUsage != 0 &&
                !oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) {
-               ret[n].Id = oidExtensionKeyUsage
-               ret[n].Critical = true
-
-               var a [2]byte
-               a[0] = reverseBitsInAByte(byte(template.KeyUsage))
-               a[1] = reverseBitsInAByte(byte(template.KeyUsage >> 8))
-
-               l := 1
-               if a[1] != 0 {
-                       l = 2
-               }
-
-               bitString := a[:l]
-               ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)})
+               ret[n], err = marshalKeyUsage(template.KeyUsage)
                if err != nil {
-                       return
+                       return nil, err
                }
                n++
        }
 
        if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) &&
                !oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) {
-               ret[n].Id = oidExtensionExtendedKeyUsage
-
-               var oids []asn1.ObjectIdentifier
-               for _, u := range template.ExtKeyUsage {
-                       if oid, ok := oidFromExtKeyUsage(u); ok {
-                               oids = append(oids, oid)
-                       } else {
-                               err = errors.New("x509: unknown extended key usage")
-                               return
-                       }
-               }
-
-               oids = append(oids, template.UnknownExtKeyUsage...)
-
-               ret[n].Value, err = asn1.Marshal(oids)
+               ret[n], err = marshalExtKeyUsage(template.ExtKeyUsage, template.UnknownExtKeyUsage)
                if err != nil {
-                       return
+                       return nil, err
                }
                n++
        }
 
        if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) {
-               // Leaving MaxPathLen as zero indicates that no maximum path
-               // length is desired, unless MaxPathLenZero is set. A value of
-               // -1 causes encoding/asn1 to omit the value as desired.
-               maxPathLen := template.MaxPathLen
-               if maxPathLen == 0 && !template.MaxPathLenZero {
-                       maxPathLen = -1
-               }
-               ret[n].Id = oidExtensionBasicConstraints
-               ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, maxPathLen})
-               ret[n].Critical = true
+               ret[n], err = marshalBasicConstraints(template.IsCA, template.MaxPathLen, template.MaxPathLenZero)
                if err != nil {
-                       return
+                       return nil, err
                }
                n++
        }
@@ -1779,14 +1773,9 @@ func buildExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId
 
        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 {
-                       policies[i].Policy = policy
-               }
-               ret[n].Value, err = asn1.Marshal(policies)
+               ret[n], err = marshalCertificatePolicies(template.PolicyIdentifiers)
                if err != nil {
-                       return
+                       return nil, err
                }
                n++
        }
@@ -1919,6 +1908,141 @@ func buildExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId
        return append(ret[:n], template.ExtraExtensions...), nil
 }
 
+func marshalKeyUsage(ku KeyUsage) (pkix.Extension, error) {
+       ext := pkix.Extension{Id: oidExtensionKeyUsage, Critical: true}
+
+       var a [2]byte
+       a[0] = reverseBitsInAByte(byte(ku))
+       a[1] = reverseBitsInAByte(byte(ku >> 8))
+
+       l := 1
+       if a[1] != 0 {
+               l = 2
+       }
+
+       bitString := a[:l]
+       var err error
+       ext.Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)})
+       if err != nil {
+               return ext, err
+       }
+       return ext, nil
+}
+
+func marshalExtKeyUsage(extUsages []ExtKeyUsage, unknownUsages []asn1.ObjectIdentifier) (pkix.Extension, error) {
+       ext := pkix.Extension{Id: oidExtensionExtendedKeyUsage}
+
+       oids := make([]asn1.ObjectIdentifier, len(extUsages)+len(unknownUsages))
+       for i, u := range extUsages {
+               if oid, ok := oidFromExtKeyUsage(u); ok {
+                       oids[i] = oid
+               } else {
+                       return ext, errors.New("x509: unknown extended key usage")
+               }
+       }
+
+       copy(oids[len(extUsages):], unknownUsages)
+
+       var err error
+       ext.Value, err = asn1.Marshal(oids)
+       if err != nil {
+               return ext, err
+       }
+       return ext, nil
+}
+
+func marshalBasicConstraints(isCA bool, maxPathLen int, maxPathLenZero bool) (pkix.Extension, error) {
+       ext := pkix.Extension{Id: oidExtensionBasicConstraints, Critical: true}
+       // Leaving MaxPathLen as zero indicates that no maximum path
+       // length is desired, unless MaxPathLenZero is set. A value of
+       // -1 causes encoding/asn1 to omit the value as desired.
+       if maxPathLen == 0 && !maxPathLenZero {
+               maxPathLen = -1
+       }
+       var err error
+       ext.Value, err = asn1.Marshal(basicConstraints{isCA, maxPathLen})
+       if err != nil {
+               return ext, nil
+       }
+       return ext, nil
+}
+
+func marshalCertificatePolicies(policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) {
+       ext := pkix.Extension{Id: oidExtensionCertificatePolicies}
+       policies := make([]policyInformation, len(policyIdentifiers))
+       for i, policy := range policyIdentifiers {
+               policies[i].Policy = policy
+       }
+       var err error
+       ext.Value, err = asn1.Marshal(policies)
+       if err != nil {
+               return ext, err
+       }
+       return ext, nil
+}
+
+func buildCSRExtensions(template *CertificateRequest) ([]pkix.Extension, error) {
+       var ret []pkix.Extension
+
+       if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 || len(template.URIs) > 0) &&
+               !oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) {
+               sanBytes, err := marshalSANs(template.DNSNames, template.EmailAddresses, template.IPAddresses, template.URIs)
+               if err != nil {
+                       return nil, err
+               }
+
+               ret = append(ret, pkix.Extension{
+                       Id:    oidExtensionSubjectAltName,
+                       Value: sanBytes,
+               })
+       }
+
+       if template.KeyUsage != 0 &&
+               !oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) {
+               ext, err := marshalKeyUsage(template.KeyUsage)
+               if err != nil {
+                       return nil, err
+               }
+               ret = append(ret, ext)
+       }
+
+       if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) &&
+               !oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) {
+               ext, err := marshalExtKeyUsage(template.ExtKeyUsage, template.UnknownExtKeyUsage)
+               if err != nil {
+                       return nil, err
+               }
+               ret = append(ret, ext)
+       }
+
+       if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) {
+               ext, err := marshalBasicConstraints(template.IsCA, template.MaxPathLen, template.MaxPathLenZero)
+               if err != nil {
+                       return nil, err
+               }
+               ret = append(ret, ext)
+       }
+
+       if len(template.SubjectKeyId) > 0 && !oidInExtensions(oidExtensionSubjectKeyId, template.ExtraExtensions) {
+               skidBytes, err := asn1.Marshal(template.SubjectKeyId)
+               if err != nil {
+                       return nil, err
+               }
+               ret = append(ret, pkix.Extension{Id: oidExtensionSubjectKeyId, Value: skidBytes})
+       }
+
+       if len(template.PolicyIdentifiers) > 0 &&
+               !oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
+               ext, err := marshalCertificatePolicies(template.PolicyIdentifiers)
+               if err != nil {
+                       return nil, err
+               }
+               ret = append(ret, ext)
+       }
+
+       return append(ret, template.ExtraExtensions...), nil
+}
+
 func subjectBytes(cert *Certificate) ([]byte, error) {
        if len(cert.RawSubject) > 0 {
                return cert.RawSubject, nil
@@ -2105,7 +2229,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
                subjectKeyId = h[:]
        }
 
-       extensions, err := buildExtensions(template, bytes.Equal(asn1Subject, emptyASN1Subject), authorityKeyId, subjectKeyId)
+       extensions, err := buildCertExtensions(template, bytes.Equal(asn1Subject, emptyASN1Subject), authorityKeyId, subjectKeyId)
        if err != nil {
                return
        }
@@ -2281,6 +2405,7 @@ type CertificateRequest struct {
        Version            int
        Signature          []byte
        SignatureAlgorithm SignatureAlgorithm
+       KeyUsage           KeyUsage
 
        PublicKeyAlgorithm PublicKeyAlgorithm
        PublicKey          interface{}
@@ -2313,6 +2438,37 @@ type CertificateRequest struct {
        EmailAddresses []string
        IPAddresses    []net.IP
        URIs           []*url.URL
+
+       ExtKeyUsage        []ExtKeyUsage           // Sequence of extended key usages.
+       UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package.
+
+       // BasicConstraintsValid indicates whether IsCA, MaxPathLen,
+       // and MaxPathLenZero are valid.
+       BasicConstraintsValid bool
+       IsCA                  bool
+
+       // MaxPathLen and MaxPathLenZero indicate the presence and
+       // value of the BasicConstraints' "pathLenConstraint".
+       //
+       // When parsing a certificate, a positive non-zero MaxPathLen
+       // means that the field was specified, -1 means it was unset,
+       // and MaxPathLenZero being true mean that the field was
+       // explicitly set to zero. The case of MaxPathLen==0 with MaxPathLenZero==false
+       // should be treated equivalent to -1 (unset).
+       //
+       // When generating a certificate, an unset pathLenConstraint
+       // can be requested with either MaxPathLen == -1 or using the
+       // zero value for both MaxPathLen and MaxPathLenZero.
+       MaxPathLen int
+       // MaxPathLenZero indicates that BasicConstraintsValid==true
+       // and MaxPathLen==0 should be interpreted as an actual
+       // maximum path length of zero. Otherwise, that combination is
+       // interpreted as MaxPathLen not being set.
+       MaxPathLenZero bool
+
+       SubjectKeyId []byte
+
+       PolicyIdentifiers []asn1.ObjectIdentifier
 }
 
 // These structures reflect the ASN.1 structure of X.509 certificate
@@ -2410,6 +2566,15 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
 //  - EmailAddresses
 //  - IPAddresses
 //  - URIs
+//  - KeyUsage
+//  - ExtKeyUsage
+//  - UnknownExtKeyUsage
+//  - BasicConstraintsValid
+//  - IsCA
+//  - MaxPathLen
+//  - MaxPathLenZero
+//  - SubjectKeyId
+//  - PolicyIdentifiers
 //  - ExtraExtensions
 //  - Attributes (deprecated)
 //
@@ -2440,23 +2605,11 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv
                return nil, err
        }
 
-       var extensions []pkix.Extension
-
-       if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 || len(template.URIs) > 0) &&
-               !oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) {
-               sanBytes, err := marshalSANs(template.DNSNames, template.EmailAddresses, template.IPAddresses, template.URIs)
-               if err != nil {
-                       return nil, err
-               }
-
-               extensions = append(extensions, pkix.Extension{
-                       Id:    oidExtensionSubjectAltName,
-                       Value: sanBytes,
-               })
+       extensions, err := buildCSRExtensions(template)
+       if err != nil {
+               return nil, err
        }
 
-       extensions = append(extensions, template.ExtraExtensions...)
-
        // Make a copy of template.Attributes because we may alter it below.
        attributes := make([]pkix.AttributeTypeAndValueSET, 0, len(template.Attributes))
        for _, attr := range template.Attributes {
@@ -2640,11 +2793,36 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
        }
 
        for _, extension := range out.Extensions {
-               if extension.Id.Equal(oidExtensionSubjectAltName) {
+               switch {
+               case extension.Id.Equal(oidExtensionSubjectAltName):
                        out.DNSNames, out.EmailAddresses, out.IPAddresses, out.URIs, err = parseSANExtension(extension.Value)
                        if err != nil {
                                return nil, err
                        }
+               case extension.Id.Equal(oidExtensionKeyUsage):
+                       out.KeyUsage, err = parseKeyUsageExtension(extension.Value)
+               case extension.Id.Equal(oidExtensionExtendedKeyUsage):
+                       out.ExtKeyUsage, out.UnknownExtKeyUsage, err = parseExtKeyUsageExtension(extension.Value)
+                       if err != nil {
+                               return nil, err
+                       }
+               case extension.Id.Equal(oidExtensionBasicConstraints):
+                       out.IsCA, out.MaxPathLen, err = parseBasicConstraintsExtension(extension.Value)
+                       if err != nil {
+                               return nil, err
+                       }
+                       out.BasicConstraintsValid = true
+                       out.MaxPathLenZero = out.MaxPathLen == 0
+               case extension.Id.Equal(oidExtensionSubjectKeyId):
+                       out.SubjectKeyId, err = parseSubjectKeyIdExtension(extension.Value)
+                       if err != nil {
+                               return nil, err
+                       }
+               case extension.Id.Equal(oidExtensionCertificatePolicies):
+                       out.PolicyIdentifiers, err = parseCertificatePoliciesExtension(extension.Value)
+                       if err != nil {
+                               return nil, err
+                       }
                }
        }
 
index 1ba31aeff3225f974cd7603be1bc000cb8cbbd8c..65d105db3441607d5b4c392b388b79a4599f4631 100644 (file)
@@ -2962,3 +2962,46 @@ func certPoolEqual(a, b *CertPool) bool {
 
        return true
 }
+
+func TestCertificateRequestRoundtripFields(t *testing.T) {
+       in := &CertificateRequest{
+               KeyUsage:              KeyUsageCertSign,
+               ExtKeyUsage:           []ExtKeyUsage{ExtKeyUsageAny},
+               UnknownExtKeyUsage:    []asn1.ObjectIdentifier{{1, 2, 3}},
+               BasicConstraintsValid: true,
+               IsCA:                  true,
+               MaxPathLen:            0,
+               MaxPathLenZero:        true,
+               SubjectKeyId:          []byte{1, 2, 3},
+               PolicyIdentifiers:     []asn1.ObjectIdentifier{{1, 2, 3}},
+       }
+       out := marshalAndParseCSR(t, in)
+
+       if in.KeyUsage != out.KeyUsage {
+               t.Fatalf("Unexpected KeyUsage: got %v, want %v", out.KeyUsage, in.KeyUsage)
+       }
+       if !reflect.DeepEqual(in.ExtKeyUsage, out.ExtKeyUsage) {
+               t.Fatalf("Unexpected ExtKeyUsage: got %v, want %v", out.ExtKeyUsage, in.ExtKeyUsage)
+       }
+       if !reflect.DeepEqual(in.UnknownExtKeyUsage, out.UnknownExtKeyUsage) {
+               t.Fatalf("Unexpected UnknownExtKeyUsage: got %v, want %v", out.UnknownExtKeyUsage, in.UnknownExtKeyUsage)
+       }
+       if in.BasicConstraintsValid != out.BasicConstraintsValid {
+               t.Fatalf("Unexpected BasicConstraintsValid: got %v, want %v", out.BasicConstraintsValid, in.BasicConstraintsValid)
+       }
+       if in.IsCA != out.IsCA {
+               t.Fatalf("Unexpected IsCA: got %v, want %v", out.IsCA, in.IsCA)
+       }
+       if in.MaxPathLen != out.MaxPathLen {
+               t.Fatalf("Unexpected MaxPathLen: got %v, want %v", out.MaxPathLen, in.MaxPathLen)
+       }
+       if in.MaxPathLenZero != out.MaxPathLenZero {
+               t.Fatalf("Unexpected MaxPathLenZero: got %v, want %v", out.MaxPathLenZero, in.MaxPathLenZero)
+       }
+       if !reflect.DeepEqual(in.SubjectKeyId, out.SubjectKeyId) {
+               t.Fatalf("Unexpected SubjectKeyId: got %v, want %v", out.SubjectKeyId, in.SubjectKeyId)
+       }
+       if !reflect.DeepEqual(in.PolicyIdentifiers, out.PolicyIdentifiers) {
+               t.Fatalf("Unexpected PolicyIdentifiers: got %v, want %v", out.PolicyIdentifiers, in.PolicyIdentifiers)
+       }
+}