golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
124.42k stars 17.71k forks source link

proposal: crypto/x509: Add support for PKCS#10 attributes #60718

Open dachaac opened 1 year ago

dachaac commented 1 year ago

Certificate signing requests also feature other attributes than Extensions.

There are protocols like RFC 7030: Enrollment over Secure Transport (EST) and RFC RFC 8894: Simple Certificate Enrolment Protocol that supports usage of 'challengePassword' attribute.

There are also other attributes that some party might be needing to use. Example attributes can be found from PKCS #9: Selected Object Classes and Attribute Types Version 2.0 (including specification for challengePassword attribute).

Currently it is a bit hard to access this information from application code. This requires copying parts of the src/crypto/x509/x509.go into your own application and then modifying them in order to access the details. One needs to access RawTBSCertificateRequest and from there RawAttributes and then do ASN.1 parsing.

There exists x509.parseRawAttributes() function to parse PKCS#10 attributes into CertificateRequest.Attributes but that cannot handle (as commented) the "simple" attributes. CertificateRequest.Attributes also has been marked as deprecated in favor of CertificateRequest.Extensions.

There is also old issue https://github.com/golang/go/issues/15995 describing some workarounds for accessing the information.

Instead of having those local hacks the proposal here is to export the needed information.

In order not to break previous software that might be using those fields new field and struct is proposed to be added.

Proposed API change is:

diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go
index 9d80b1d8ba..1e5220539e 100644
--- a/src/crypto/x509/x509.go
+++ b/src/crypto/x509/x509.go
@@ -1794,6 +1794,9 @@ type CertificateRequest struct {
        // generating the requestedExtensions attribute.
        Attributes []pkix.AttributeTypeAndValueSET

+       // Pkcs10Attributes contains the CSR attributes that are not pkix.Extension's
+       Pkcs10Attributes []Pkcs10Attribute
+
        // Extensions contains all requested extensions, in raw form. When parsing
        // CSRs, this can be used to extract extensions that are not parsed by this
        // package.
@@ -1815,6 +1818,12 @@ type CertificateRequest struct {
        URIs           []*url.URL
 }

+// Pkcs10Attribute reflects the Attribute structure from RFC 2986, Section 4.1.
+type Pkcs10Attribute struct {
+       Id     asn1.ObjectIdentifier
+       Values []asn1.RawValue `asn1:"set"`
+}
+
 // These structures reflect the ASN.1 structure of X.509 certificate
 // signature requests (see RFC 2986):

Structure pkcs10Attribute from x509.parseCSRExtensions() is now promoted to be exported as x509.Pkcs10Attribute

Then access for in example for challengePassword would be:

var oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7}

func GetChallengePassword(request *x509.CertificateRequest) (string, error) {
    var challengePassword string

    for _, pkcs10attribute := range request.Pkcs10Attributes {
        switch {
        case pkcs10attribute.Id.Equal(oidChallengePassword):
            _, err := asn1.Unmarshal(pkcs10attribute.Values[0].FullBytes, &challengePassword)
            if err != nil {
                return "", err
            }
            return challengePassword, nil
        }
    }

    return "", nil
}

There is pkix.AttributeTypeAndValue and that has been proposed for this use but that is missing value type and asn1 parser guidance.

Note: it might be a good idea to have OID registry available as API for easier access for different attributes. In example x509.OidChallengePassword could be defined in the library.

Alternative implementation could be to add CertificateRequest.ChallengePassowrd to give access for the needed challengePassowrd value.

In here there was only example for using it (as that was the present need) but there should also be support for creating the certificate signing request with the challengePassword.

ianlancetaylor commented 1 year ago

CC @golang/security