diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index 1f06b4fbc5..c1166cea16 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -1484,7 +1484,7 @@ func marshalCertificatePolicies(policies []OID, policyIdentifiers []asn1.ObjectI return ext, err } -func buildCSRExtensions(template *CertificateRequest) ([]pkix.Extension, error) { +func buildCSRExtensions(template *CertificateRequest, subjectIsEmpty bool) ([]pkix.Extension, error) { var ret []pkix.Extension if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 || len(template.URIs) > 0) && @@ -1495,8 +1495,12 @@ func buildCSRExtensions(template *CertificateRequest) ([]pkix.Extension, error) } ret = append(ret, pkix.Extension{ - Id: oidExtensionSubjectAltName, - Value: sanBytes, + Id: oidExtensionSubjectAltName, + // From RFC 5280, Section 4.2.1.6: + // “If the subject field contains an empty sequence ... then + // subjectAltName extension ... is marked as critical” + Critical: subjectIsEmpty, + Value: sanBytes, }) } @@ -2066,7 +2070,15 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv return nil, err } - extensions, err := buildCSRExtensions(template) + asn1Subject := template.RawSubject + if len(asn1Subject) == 0 { + asn1Subject, err = asn1.Marshal(template.Subject.ToRDNSequence()) + if err != nil { + return nil, err + } + } + + extensions, err := buildCSRExtensions(template, bytes.Equal(asn1Subject, emptyASN1Subject)) if err != nil { return nil, err } @@ -2153,14 +2165,6 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv rawAttributes = append(rawAttributes, rawValue) } - asn1Subject := template.RawSubject - if len(asn1Subject) == 0 { - asn1Subject, err = asn1.Marshal(template.Subject.ToRDNSequence()) - if err != nil { - return nil, err - } - } - tbsCSR := tbsCertificateRequest{ Version: 0, // PKCS #10, RFC 2986 Subject: asn1.RawValue{FullBytes: asn1Subject}, diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index 98f3f7941c..971fda0a1e 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -2464,6 +2464,33 @@ func TestEmptySubject(t *testing.T) { t.Fatal("SAN extension is missing") } +func TestEmptySubjectInCSR(t *testing.T) { + template := CertificateRequest{ + DNSNames: []string{"example.com"}, + } + + derBytes, err := CreateCertificateRequest(rand.Reader, &template, testPrivateKey) + if err != nil { + t.Fatalf("failed to create certificate request: %s", err) + } + + csr, err := ParseCertificateRequest(derBytes) + if err != nil { + t.Fatalf("failed to parse certificate request: %s", err) + } + + for _, ext := range csr.Extensions { + if ext.Id.Equal(oidExtensionSubjectAltName) { + if !ext.Critical { + t.Fatal("SAN extension is not critical") + } + return + } + } + + t.Fatal("SAN extension is missing") +} + // multipleURLsInCRLDPPEM contains two URLs in a single CRL DistributionPoint // structure. It is taken from https://crt.sh/?id=12721534. const multipleURLsInCRLDPPEM = `