From e1e58a2102cfc19ac0db50ee9f9a8d04ee755d75 Mon Sep 17 00:00:00 2001 From: mishaslavin <63741893+mishaslavin@users.noreply.github.com> Date: Thu, 12 Jun 2025 15:51:57 -0700 Subject: [PATCH] create option to return all intermediates in cert chain within SCEP response creation of ReturnEntireCertChain option for SCEP provisioner which controls whether to use the default behavior of just returning the leaf cert or to return all certificates that we get from the signer response --- authority/provisioner/scep.go | 11 +++++++++++ scep/authority.go | 14 ++++++++++---- scep/provisioner.go | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index b6e8b925e..ce34d88c3 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -41,6 +41,10 @@ type SCEP struct { // GetCACerts response ExcludeIntermediate bool `json:"excludeIntermediate,omitempty"` + // ReturnEntireCertChain makes the provisioner return the full certificate chain + // provided by the CA rather than just the leaf certificate + ReturnEntireCertChain bool `json:"returnEntireCertChain,omitempty"` + // MinimumPublicKeyLength is the minimum length for public keys in CSRs MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` @@ -447,6 +451,13 @@ func (s *SCEP) ShouldIncludeIntermediateInChain() bool { return !s.ExcludeIntermediate } +// ShouldReturnEntireCertChain indicates if the +// CA should return the entire chain of certificates in the SCEP +// PKIOperation response rather than just the leaf certificate. +func (s *SCEP) ShouldReturnEntireCertChain() bool { + return s.ReturnEntireCertChain +} + // GetContentEncryptionAlgorithm returns the numeric identifier // for the pkcs7 package encryption algorithm to use. func (s *SCEP) GetContentEncryptionAlgorithm() int { diff --git a/scep/authority.go b/scep/authority.go index 256c540d0..40c1afc77 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -363,10 +363,16 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m return nil, err } - // add the certificate into the signed data type - // this cert must be added before the signedData because the recipient will expect it - // as the first certificate in the array - signedData.AddCertificate(cert) + // add the certificate chain into the signed data type if specified + // otherwise just return the leaf cert. the ordering of the added signed data is important + // because the recipient will expect the leaf as the first certificate + if p.ShouldReturnEntireCertChain() { + for _, c := range certChain { + signedData.AddCertificate(c) + } + } else { + signedData.AddCertificate(cert) + } signerCert, signer, err := a.selectSigner(ctx) if err != nil { diff --git a/scep/provisioner.go b/scep/provisioner.go index 35821d8cc..1c0f06792 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -17,6 +17,7 @@ type Provisioner interface { GetCapabilities() []string ShouldIncludeRootInChain() bool ShouldIncludeIntermediateInChain() bool + ShouldReturnEntireCertChain() bool GetDecrypter() (*x509.Certificate, crypto.Decrypter) GetSigner() (*x509.Certificate, crypto.Signer) GetContentEncryptionAlgorithm() int