acme/autocert: support External Account Binding (EAB) tokens
Support External Account Binding (EAB) tokens to the Manager as defined in RFC 8555, Section 7.3.4. If the ExternalAccountBinding field is set on Manager, pass it into the acme Account during registration. Fixes golang/go#48809 Change-Id: I64c38b05ab577acbde9f526638cc8104d15ff055 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/354189 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Trust: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org> Run-TryBot: Filippo Valsorda <filippo@golang.org> Auto-Submit: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
committed by
Gopher Robot
parent
ae2d96664a
commit
5352b09029
@@ -170,6 +170,11 @@ type Manager struct {
|
||||
// in the template's ExtraExtensions field as is.
|
||||
ExtraExtensions []pkix.Extension
|
||||
|
||||
// ExternalAccountBinding optionally represents an arbitrary binding to an
|
||||
// account of the CA to which the ACME server is tied.
|
||||
// See RFC 8555, Section 7.3.4 for more details.
|
||||
ExternalAccountBinding *acme.ExternalAccountBinding
|
||||
|
||||
clientMu sync.Mutex
|
||||
client *acme.Client // initialized by acmeClient method
|
||||
|
||||
@@ -996,7 +1001,7 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
|
||||
if m.Email != "" {
|
||||
contact = []string{"mailto:" + m.Email}
|
||||
}
|
||||
a := &acme.Account{Contact: contact}
|
||||
a := &acme.Account{Contact: contact, ExternalAccountBinding: m.ExternalAccountBinding}
|
||||
_, err := client.Register(ctx, a, m.Prompt)
|
||||
if err == nil || isAccountAlreadyExist(err) {
|
||||
m.client = client
|
||||
|
||||
@@ -394,6 +394,19 @@ func TestGetCertificate(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "provideExternalAuth",
|
||||
hello: clientHelloInfo("example.org", algECDSA),
|
||||
domain: "example.org",
|
||||
prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
|
||||
s.ExternalAccountRequired()
|
||||
|
||||
man.ExternalAccountBinding = &acme.ExternalAccountBinding{
|
||||
KID: "test-key",
|
||||
Key: make([]byte, 32),
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -49,6 +49,7 @@ type CAServer struct {
|
||||
challengeTypes []string
|
||||
url string
|
||||
roots *x509.CertPool
|
||||
eabRequired bool
|
||||
|
||||
mu sync.Mutex
|
||||
certCount int // number of issued certs
|
||||
@@ -152,6 +153,15 @@ func (ca *CAServer) Roots() *x509.CertPool {
|
||||
return ca.roots
|
||||
}
|
||||
|
||||
// ExternalAccountRequired makes an EAB JWS required for account registration.
|
||||
func (ca *CAServer) ExternalAccountRequired() *CAServer {
|
||||
if ca.url != "" {
|
||||
panic("ExternalAccountRequired must be called before Start")
|
||||
}
|
||||
ca.eabRequired = true
|
||||
return ca
|
||||
}
|
||||
|
||||
// Start starts serving requests. The server address becomes available in the
|
||||
// URL field.
|
||||
func (ca *CAServer) Start() *CAServer {
|
||||
@@ -224,6 +234,12 @@ type discovery struct {
|
||||
NewAccount string `json:"newAccount"`
|
||||
NewOrder string `json:"newOrder"`
|
||||
NewAuthz string `json:"newAuthz"`
|
||||
|
||||
Meta discoveryMeta `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
type discoveryMeta struct {
|
||||
ExternalAccountRequired bool `json:"externalAccountRequired,omitempty"`
|
||||
}
|
||||
|
||||
type challenge struct {
|
||||
@@ -264,6 +280,9 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
NewNonce: ca.serverURL("/new-nonce"),
|
||||
NewAccount: ca.serverURL("/new-account"),
|
||||
NewOrder: ca.serverURL("/new-order"),
|
||||
Meta: discoveryMeta{
|
||||
ExternalAccountRequired: ca.eabRequired,
|
||||
},
|
||||
}
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
panic(fmt.Sprintf("discovery response: %v", err))
|
||||
@@ -283,6 +302,21 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
ca.acctRegistered = true
|
||||
|
||||
var req struct {
|
||||
ExternalAccountBinding json.RawMessage
|
||||
}
|
||||
|
||||
if err := decodePayload(&req, r.Body); err != nil {
|
||||
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if ca.eabRequired && len(req.ExternalAccountBinding) == 0 {
|
||||
ca.httpErrorf(w, http.StatusBadRequest, "registration failed: no JWS for EAB")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Check the user account key against a ca.accountKeys?
|
||||
w.Header().Set("Location", ca.serverURL("/accounts/1"))
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
Reference in New Issue
Block a user