diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 3ada91d09..bf76a55f3 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -43,6 +43,14 @@ type SCEP struct { // MinimumPublicKeyLength is the minimum length for public keys in CSRs MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"` + // AllowUnsortedAuthenticatedAttributes permits verification of PKCS#7 messages + // from legacy SCEP clients that sign authenticated attributes without DER sorting. + AllowUnsortedAuthenticatedAttributes bool `json:"allowUnsortedAuthenticatedAttributes,omitempty"` + + // LegacyRSADigestEncryptionAlgorithm makes SCEP responses use rsaEncryption as the + // SignerInfo digestEncryptionAlgorithm for compatibility with legacy SCEP clients. + LegacyRSADigestEncryptionAlgorithm bool `json:"legacyRSADigestEncryptionAlgorithm,omitempty"` + // TODO(hs): also support a separate signer configuration? DecrypterCertificate []byte `json:"decrypterCertificate,omitempty"` DecrypterKeyPEM []byte `json:"decrypterKeyPEM,omitempty"` @@ -447,6 +455,18 @@ func (s *SCEP) ShouldIncludeIntermediateInChain() bool { return !s.ExcludeIntermediate } +// ShouldAllowUnsortedAuthenticatedAttributes indicates whether verification may +// fall back to the authenticated attribute order used by legacy SCEP clients. +func (s *SCEP) ShouldAllowUnsortedAuthenticatedAttributes() bool { + return s.AllowUnsortedAuthenticatedAttributes +} + +// ShouldUseLegacyRSADigestEncryptionAlgorithm indicates whether SCEP responses +// should use rsaEncryption as the SignerInfo digestEncryptionAlgorithm. +func (s *SCEP) ShouldUseLegacyRSADigestEncryptionAlgorithm() bool { + return s.LegacyRSADigestEncryptionAlgorithm +} + // GetContentEncryptionAlgorithm returns the numeric identifier // for the pkcs7 package encryption algorithm to use. func (s *SCEP) GetContentEncryptionAlgorithm() int { diff --git a/scep/api/api.go b/scep/api/api.go index 32dd754d6..7e9376cbb 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -345,8 +345,10 @@ func GetCACaps(ctx context.Context) (Response, error) { // PKIOperation performs PKI operations and returns a SCEP response func PKIOperation(ctx context.Context, req request) (Response, error) { + auth := scep.MustFromContext(ctx) + // parse the message using smallscep implementation - microMsg, err := smallscep.ParsePKIMessage(req.Message) + microMsg, err := smallscep.ParsePKIMessage(req.Message, smallscep.WithUnsortedAuthenticatedAttributes(auth.ShouldAllowUnsortedAuthenticatedAttributes(ctx))) if err != nil { // return the error, because we can't use the msg for creating a CertRep return Response{}, fmt.Errorf("failed parsing SCEP request: %w", err) @@ -369,7 +371,6 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { P7: p7, } - auth := scep.MustFromContext(ctx) if err := auth.DecryptPKIEnvelope(ctx, msg); err != nil { return Response{}, err } diff --git a/scep/authority.go b/scep/authority.go index 8decab81c..59a13ac63 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -372,6 +372,10 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m return nil, fmt.Errorf("failed selecting signer: %w", err) } + if p.ShouldUseLegacyRSADigestEncryptionAlgorithm() { + signedData.SetEncryptionAlgorithm(pkcs7.OIDEncryptionAlgorithmRSA) + } + // sign the attributes if err := signedData.AddSigner(signerCert, signer, config); err != nil { return nil, err @@ -424,6 +428,8 @@ func (a *Authority) encrypt(content []byte, recipients []*x509.Certificate, algo // CreateFailureResponse creates an appropriately signed reply for PKI operations func (a *Authority) CreateFailureResponse(ctx context.Context, _ *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error) { + p := provisionerFromContext(ctx) + config := pkcs7.SignerInfoConfig{ ExtraSignedAttributes: []pkcs7.Attribute{ { @@ -467,6 +473,10 @@ func (a *Authority) CreateFailureResponse(ctx context.Context, _ *x509.Certifica return nil, fmt.Errorf("failed selecting signer: %w", err) } + if p.ShouldUseLegacyRSADigestEncryptionAlgorithm() { + signedData.SetEncryptionAlgorithm(pkcs7.OIDEncryptionAlgorithmRSA) + } + // sign the attributes if err := signedData.AddSigner(signerCert, signer, config); err != nil { return nil, err @@ -512,6 +522,13 @@ func (a *Authority) GetCACaps(ctx context.Context) []string { return caps } +// ShouldAllowUnsortedAuthenticatedAttributes indicates whether verification may +// accept authenticated attributes in their encoded order for the current provisioner. +func (a *Authority) ShouldAllowUnsortedAuthenticatedAttributes(ctx context.Context) bool { + p := provisionerFromContext(ctx) + return p.ShouldAllowUnsortedAuthenticatedAttributes() +} + func (a *Authority) ValidateChallenge(ctx context.Context, csr *x509.CertificateRequest, challenge, transactionID string) ([]provisioner.SignCSROption, error) { p := provisionerFromContext(ctx) return p.ValidateChallenge(ctx, csr, challenge, transactionID) diff --git a/scep/provisioner.go b/scep/provisioner.go index 35821d8cc..d62d5bdcf 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -17,6 +17,8 @@ type Provisioner interface { GetCapabilities() []string ShouldIncludeRootInChain() bool ShouldIncludeIntermediateInChain() bool + ShouldAllowUnsortedAuthenticatedAttributes() bool + ShouldUseLegacyRSADigestEncryptionAlgorithm() bool GetDecrypter() (*x509.Certificate, crypto.Decrypter) GetSigner() (*x509.Certificate, crypto.Signer) GetContentEncryptionAlgorithm() int