acme/autocert: support configurable CSR extensions
Package users can now provide extra CSR extensions to serve certificates with desired properties. Fixes golang/go#17801. Change-Id: Iac1010f41391c865f6e318bad2e0dafc2ffef6b1 Reviewed-on: https://go-review.googlesource.com/42470 Reviewed-by: Adam Langley <agl@golang.org> Run-TryBot: Adam Langley <agl@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user