diff --git a/acme/autocert/autocert.go b/acme/autocert/autocert.go index 97281d8..95c3928 100644 --- a/acme/autocert/autocert.go +++ b/acme/autocert/autocert.go @@ -146,6 +146,15 @@ type Manager struct { // is EC-based keys using the P-256 curve. ForceRSA bool + // ExtraExtensions are used when generating a new CSR (Certificate Request), + // thus allowing customization of the resulting certificate. + // For instance, TLS Feature Extension (RFC 7633) can be used + // to prevent an OCSP downgrade attack. + // + // The field value is passed to crypto/x509.CreateCertificateRequest + // in the template's ExtraExtensions field as is. + ExtraExtensions []pkix.Extension + clientMu sync.Mutex client *acme.Client // initialized by acmeClient method @@ -527,7 +536,7 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain if err := m.verify(ctx, client, domain); err != nil { return nil, nil, err } - csr, err := certRequest(key, domain) + csr, err := certRequest(key, domain, m.ExtraExtensions) if err != nil { return nil, nil, err } @@ -870,12 +879,12 @@ func (s *certState) tlscert() (*tls.Certificate, error) { }, nil } -// certRequest creates a certificate request for the given common name cn -// and optional SANs. -func certRequest(key crypto.Signer, cn string, san ...string) ([]byte, error) { +// certRequest generates a CSR for the given common name cn and optional SANs. +func certRequest(key crypto.Signer, cn string, ext []pkix.Extension, san ...string) ([]byte, error) { req := &x509.CertificateRequest{ - Subject: pkix.Name{CommonName: cn}, - DNSNames: san, + Subject: pkix.Name{CommonName: cn}, + DNSNames: san, + ExtraExtensions: ext, } return x509.CreateCertificateRequest(rand.Reader, req, key) } diff --git a/acme/autocert/autocert_test.go b/acme/autocert/autocert_test.go index 54d38e2..8549082 100644 --- a/acme/autocert/autocert_test.go +++ b/acme/autocert/autocert_test.go @@ -14,6 +14,7 @@ import ( "crypto/tls" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "encoding/base64" "encoding/json" "fmt" @@ -854,3 +855,33 @@ func TestManagerGetCertificateBogusSNI(t *testing.T) { } } } + +func TestCertRequest(t *testing.T) { + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + // An extension from RFC7633. Any will do. + ext := pkix.Extension{ + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1}, + Value: []byte("dummy"), + } + b, err := certRequest(key, "example.org", []pkix.Extension{ext}, "san.example.org") + if err != nil { + t.Fatalf("certRequest: %v", err) + } + r, err := x509.ParseCertificateRequest(b) + if err != nil { + t.Fatalf("ParseCertificateRequest: %v", err) + } + var found bool + for _, v := range r.Extensions { + if v.Id.Equal(ext.Id) { + found = true + break + } + } + if !found { + t.Errorf("want %v in Extensions: %v", ext, r.Extensions) + } +}