From f49d2c5b021a898e5f63ca5e008acdfc9ad81f1d Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Tue, 5 Sep 2023 17:59:10 +0300 Subject: [PATCH] allow customizable CSR key ID/name and key parameters --- modules/caddypki/adminapi.go | 33 ++++++++++++++++++++++++++++++++- modules/caddypki/ca.go | 31 +++++++++++++++++++------------ 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/modules/caddypki/adminapi.go b/modules/caddypki/adminapi.go index 720a5dee..c90ecac1 100644 --- a/modules/caddypki/adminapi.go +++ b/modules/caddypki/adminapi.go @@ -24,6 +24,7 @@ import ( "go.uber.org/zap" "github.com/caddyserver/caddy/v2" + "github.com/google/uuid" ) func init() { @@ -177,6 +178,29 @@ func (a *adminAPI) handleCACerts(w http.ResponseWriter, r *http.Request) error { } type csrRequest struct { + // Custom name assigned to the CSR key. If empty, UUID is generated and assigned. + ID string `json:"id,omitempty"` + + // Customization knobs of the generated/loaded key, if desired. + // If empty, sane defaults will be managed internally without exposing their details + // to the user. At the moment, the default parameters are: + // { + // "type": "EC", + // "curve": "P-256" + // } + Key *struct { + // The key type to be used for signing the CSR. The possible types are: + // EC, RSA, and OKP. + Type string `json:"type"` + + // The curve to use with key types EC and OKP. + // If the Type is OKP, then acceptable curves are: Ed25519, or X25519 + // If the Type is EC, then acceptable curves are: P-256, P-384, or P-521 + Curve string `json:"curve,omitempty"` + + // Only used with RSA keys and accepts minimum of 2048. + Size int `json:"size,omitempty"` + } `json:"key,omitempty"` // SANs is a list of subject alternative names for the certificate. SANs []string `json:"sans"` } @@ -205,8 +229,13 @@ func (a *adminAPI) handleCSRGeneration(w http.ResponseWriter, r *http.Request) e Err: fmt.Errorf("failed to decode CSR request: %v", err), } } + csrReq.ID = strings.TrimSpace(csrReq.ID) + if len(csrReq.ID) == 0 { + csrReq.ID = uuid.New().String() + } + // Generate the CSR - csr, err := ca.generateCSR(csrReq.SANs) + csr, err := ca.generateCSR(csrReq) if err != nil { return caddy.APIError{ HTTPStatus: http.StatusInternalServerError, @@ -227,6 +256,8 @@ func (a *adminAPI) handleCSRGeneration(w http.ResponseWriter, r *http.Request) e } } w.Header().Set("Content-Type", "application/pkcs10") + w.Header().Set("content-disposition", fmt.Sprintf(`attachment; filename="%s"`, csrReq.ID)) + if _, err := w.Write(bs); err != nil { return caddy.APIError{ HTTPStatus: http.StatusInternalServerError, diff --git a/modules/caddypki/ca.go b/modules/caddypki/ca.go index 15339ced..326f1711 100644 --- a/modules/caddypki/ca.go +++ b/modules/caddypki/ca.go @@ -396,8 +396,8 @@ func (ca CA) storageKeyIntermediateKey() string { return path.Join(ca.storageKeyCAPrefix(), "intermediate.key") } -func (ca CA) storageKeyCSRKey() string { - return path.Join(ca.storageKeyCAPrefix(), "csr.key") +func (ca CA) storageKeyCSRKey(id string) string { + return path.Join(ca.storageKeyCAPrefix(), id+".csr.key") } func (ca CA) newReplacer() *caddy.Replacer { @@ -427,34 +427,41 @@ func (ca CA) installRoot() error { ) } -func (ca CA) generateCSR(sans []string) (csr *x509.CertificateRequest, err error) { +func (ca CA) generateCSR(csrReq csrRequest) (csr *x509.CertificateRequest, err error) { var signer crypto.Signer - csrKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyCSRKey()) + csrKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyCSRKey(csrReq.ID)) if err != nil { if !errors.Is(err, fs.ErrNotExist) { - return nil, fmt.Errorf("loading csr key: %v", err) + return nil, fmt.Errorf("loading csr key '%s': %v", csrReq.ID, err) + } + if csrReq.Key == nil { + signer, err = keyutil.GenerateDefaultSigner() + if err != nil { + return nil, err + } + } else { + signer, err = keyutil.GenerateSigner(csrReq.Key.Type, csrReq.Key.Curve, csrReq.Key.Size) + if err != nil { + return nil, err + } } - signer, err = keyutil.GenerateDefaultSigner() - if err != nil { - return nil, err - } csrKeyPEM, err = certmagic.PEMEncodePrivateKey(signer) if err != nil { return nil, fmt.Errorf("encoding csr key: %v", err) } - if err := ca.storage.Store(ca.ctx, ca.storageKeyCSRKey(), csrKeyPEM); err != nil { + if err := ca.storage.Store(ca.ctx, ca.storageKeyCSRKey(csrReq.ID), csrKeyPEM); err != nil { return nil, fmt.Errorf("saving csr key: %v", err) } } if signer == nil { signer, err = certmagic.PEMDecodePrivateKey(csrKeyPEM) if err != nil { - return nil, fmt.Errorf("decoding root key: %v", err) + return nil, fmt.Errorf("decoding csr key: %v", err) } } - csr, err = x509util.CreateCertificateRequest("", sans, signer) + csr, err = x509util.CreateCertificateRequest("", csrReq.SANs, signer) if err != nil { return nil, err }