diff --git a/acme/api/handler.go b/acme/api/handler.go index dd30c7c1..921e614e 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -2,6 +2,8 @@ package api import ( "context" + "crypto/x509" + "encoding/pem" "fmt" "net/http" @@ -162,6 +164,18 @@ func (h *Handler) GetCertificate(w http.ResponseWriter, r *http.Request) { return } + block, _ := pem.Decode(certBytes) + if block == nil { + api.WriteError(w, acme.ServerInternalErr(errors.New("failed to decode any certificates from generated certBytes"))) + return + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + api.WriteError(w, acme.Wrap(err, "failed to parse generated leaf certificate")) + return + } + + api.LogCertificate(w, cert) w.Header().Set("Content-Type", "application/pem-certificate-chain; charset=utf-8") w.Write(certBytes) } diff --git a/acme/api/handler_test.go b/acme/api/handler_test.go index f8bac96c..e3db69b7 100644 --- a/acme/api/handler_test.go +++ b/acme/api/handler_test.go @@ -526,6 +526,43 @@ func TestHandlerGetCertificate(t *testing.T) { problem: acme.ServerInternalErr(errors.New("force")), } }, + "fail/decode-leaf-for-loggger": func(t *testing.T) test { + acc := &acme.Account{ID: "accID"} + ctx := context.WithValue(context.Background(), acme.AccContextKey, acc) + ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) + return test{ + auth: &mockAcmeAuthority{ + getCertificate: func(accID, id string) ([]byte, error) { + assert.Equals(t, accID, acc.ID) + assert.Equals(t, id, certID) + return []byte("foo"), nil + }, + }, + ctx: ctx, + statusCode: 500, + problem: acme.ServerInternalErr(errors.New("failed to decode any certificates from generated certBytes")), + } + }, + "fail/parse-x509-leaf-for-logger": func(t *testing.T) test { + acc := &acme.Account{ID: "accID"} + ctx := context.WithValue(context.Background(), acme.AccContextKey, acc) + ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) + return test{ + auth: &mockAcmeAuthority{ + getCertificate: func(accID, id string) ([]byte, error) { + assert.Equals(t, accID, acc.ID) + assert.Equals(t, id, certID) + return pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE REQUEST", + Bytes: []byte("foo"), + }), nil + }, + }, + ctx: ctx, + statusCode: 500, + problem: acme.ServerInternalErr(errors.New("failed to parse generated leaf certificate")), + } + }, "ok": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := context.WithValue(context.Background(), acme.AccContextKey, acc) @@ -565,7 +602,7 @@ func TestHandlerGetCertificate(t *testing.T) { prob := tc.problem.ToACME() assert.Equals(t, ae.Type, prob.Type) - assert.Equals(t, ae.Detail, prob.Detail) + assert.HasPrefix(t, ae.Detail, prob.Detail) assert.Equals(t, ae.Identifier, prob.Identifier) assert.Equals(t, ae.Subproblems, prob.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) diff --git a/api/api.go b/api/api.go index f83a2354..6e0b08eb 100644 --- a/api/api.go +++ b/api/api.go @@ -395,7 +395,8 @@ func logOtt(w http.ResponseWriter, token string) { } } -func logCertificate(w http.ResponseWriter, cert *x509.Certificate) { +// LogCertificate add certificate fields to the log message. +func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { if rl, ok := w.(logging.ResponseLogger); ok { m := map[string]interface{}{ "serial": cert.SerialNumber, @@ -413,7 +414,11 @@ func logCertificate(w http.ResponseWriter, cert *x509.Certificate) { if err != nil || len(rest) > 0 { break } - m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) + if len(val.CredentialID) > 0 { + m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) + } else { + m["provisioner"] = fmt.Sprintf("%s", val.Name) + } break } } diff --git a/api/rekey.go b/api/rekey.go index 2d24dbb8..c0d88e55 100644 --- a/api/rekey.go +++ b/api/rekey.go @@ -54,7 +54,7 @@ func (h *caHandler) Rekey(w http.ResponseWriter, r *http.Request) { caPEM = certChainPEM[1] } - logCertificate(w, certChain[0]) + LogCertificate(w, certChain[0]) JSONStatus(w, &SignResponse{ ServerPEM: certChainPEM[0], CaPEM: caPEM, diff --git a/api/renew.go b/api/renew.go index bf32518b..74ef2034 100644 --- a/api/renew.go +++ b/api/renew.go @@ -25,7 +25,7 @@ func (h *caHandler) Renew(w http.ResponseWriter, r *http.Request) { caPEM = certChainPEM[1] } - logCertificate(w, certChain[0]) + LogCertificate(w, certChain[0]) JSONStatus(w, &SignResponse{ ServerPEM: certChainPEM[0], CaPEM: caPEM, diff --git a/api/revoke.go b/api/revoke.go index 547ed366..21c3154c 100644 --- a/api/revoke.go +++ b/api/revoke.go @@ -91,7 +91,7 @@ func (h *caHandler) Revoke(w http.ResponseWriter, r *http.Request) { // TODO: should probably be checking if the certificate was revoked here. // Will need to thread that request down to the authority, so will need // to add API for that. - logCertificate(w, opts.Crt) + LogCertificate(w, opts.Crt) opts.MTLS = true } diff --git a/api/sign.go b/api/sign.go index f30b0b4b..7826f47b 100644 --- a/api/sign.go +++ b/api/sign.go @@ -79,7 +79,7 @@ func (h *caHandler) Sign(w http.ResponseWriter, r *http.Request) { if len(certChainPEM) > 1 { caPEM = certChainPEM[1] } - logCertificate(w, certChain[0]) + LogCertificate(w, certChain[0]) JSONStatus(w, &SignResponse{ ServerPEM: certChainPEM[0], CaPEM: caPEM,