Description
When issuing a certificate via ACME finalize against RA profile EJBCA-Empty (no custom request attributes, no extension mappings, request-attribute validation set to STRICT), the request is rejected with acme:error:badCsr because the CSR carries the Extended Key Usage extension (OID 2.5.29.37). Switching the profile to LENIENT lets issuance succeed.
Root cause (confirmed against a local core checkout): STRICT validation runs a presence-whitelist over CSR extensions — every extension OID in the CSR must be explicitly mapped by an ExtensionMappedField in the resolved request-attribute set, or the request is rejected (CertificateRequestContentValidator.java:260-265). A profile with no mappings does not get an empty set: resolution falls back to the platform default set, which is 6 RDN-only attributes with zero SAN/extension mappings (RaProfileCertificateRequestAttributeServiceImpl.java:85-89, CsrAttributes.java:45-54). So under STRICT, any CSR carrying any non-SAN standard extension (EKU, keyUsage, basicConstraints) fails. Only SAN (2.5.29.17) is exempt (X509RequestContentParser.java:243-245). LENIENT skips the whitelist entirely (ProtocolRequestAttributeValidator.java:78-80).
Why this is a bug, not merely strict config: nearly every real CSR generator — and all common ACME clients — emit EKU/keyUsage. Platform-generated CSRs are built from the mapped definitions so they always pass; only externally-supplied CSRs (ACME/SCEP/CMP + REST upload) are exposed — exactly the CSRs the operator cannot control. The whitelist is presence-only (extension values are opaque DER, not constrained), so rejecting benign standard extensions adds no real security. Net effect: STRICT is unusable with real protocol clients out of the box.
Recommended fixes:
Primary:
- Default-allow standard extensions — a built-in, configurable allowlist of benign OIDs (
2.5.29.37 EKU, 2.5.29.15 keyUsage, 2.5.29.19 basicConstraints) consulted in the whitelist check (CertificateRequestContentValidator.java:260), OR seed non-required extension-mapped attributes for those OIDs into the platform default set (CsrAttributes.java:45 / DefaultRequestAttributeSet.seed()).
- Warn-only mode — run the whitelist under LENIENT too, emitting warnings instead of skipping it (one-line change at
ProtocolRequestAttributeValidator.java:80 → new RequestAttributePolicy(strict, true)); gives operators visibility before enabling STRICT.
Lower-priority hardening (fix 1 does NOT cover these):
- Config-time guardrail — fix 1 only allowlists the 3 standard extensions; a STRICT profile with the RDN-only default set still rejects SAN-only CSRs (no SAN mapping in the default set) and any custom / non-standard extension. When STRICT is enabled on a profile whose resolved set has no SAN and no extension mappings, warn that protocol clients will likely be rejected, and make the empty-set → RDN-only-default fallback explicit rather than silent (
RaProfileCertificateRequestAttributeServiceImpl.updateConfiguration, ~line 129).
- Canonicalize extension OIDs on both sides of the whitelist comparison (
CertificateRequestContentValidator.java:202) the way RDN keys already are, so definition/registry code-vs-OID variants can't cause false rejections.
Note: analysis based on a local core checkout; line numbers are approximate to that revision.
Steps to Reproduce
- Pick or create an RA profile with no custom request attributes and no extension mappings (e.g.
EJBCA-Empty on an EJBCA authority).
- Set the profile's request-attribute validation to STRICT.
- From an ACME client, submit an order and finalize with a CSR that includes the Extended Key Usage extension (serverAuth/clientAuth) — the default for common clients.
- Observe that finalize fails.
Expected Behavior
Certificate issues successfully. Standard, benign CSR extensions (EKU, keyUsage, basicConstraints) should not be rejected by STRICT validation when the RA profile merely has no extension mappings configured — or the operator should be clearly warned that STRICT with an empty mapping set will reject them.
Actual Behavior
ACME finalize returns HTTP != 200 with:
Signing failed. Finalize code was not 200.
{"type":"urn:ietf:params:acme:error:badCsr","title":"CSR Invalid","detail":"Certificate request does not satisfy the request-attribute policy of RA profile 'EJBCA-Empty': Extension '2.5.29.37' is not allowed by the request-attribute set"}
Switching the RA profile to LENIENT makes issuance succeed.
Severity
Major
Description
When issuing a certificate via ACME finalize against RA profile
EJBCA-Empty(no custom request attributes, no extension mappings, request-attribute validation set to STRICT), the request is rejected withacme:error:badCsrbecause the CSR carries the Extended Key Usage extension (OID2.5.29.37). Switching the profile to LENIENT lets issuance succeed.Root cause (confirmed against a local
corecheckout): STRICT validation runs a presence-whitelist over CSR extensions — every extension OID in the CSR must be explicitly mapped by anExtensionMappedFieldin the resolved request-attribute set, or the request is rejected (CertificateRequestContentValidator.java:260-265). A profile with no mappings does not get an empty set: resolution falls back to the platform default set, which is 6 RDN-only attributes with zero SAN/extension mappings (RaProfileCertificateRequestAttributeServiceImpl.java:85-89,CsrAttributes.java:45-54). So under STRICT, any CSR carrying any non-SAN standard extension (EKU, keyUsage, basicConstraints) fails. Only SAN (2.5.29.17) is exempt (X509RequestContentParser.java:243-245). LENIENT skips the whitelist entirely (ProtocolRequestAttributeValidator.java:78-80).Why this is a bug, not merely strict config: nearly every real CSR generator — and all common ACME clients — emit EKU/keyUsage. Platform-generated CSRs are built from the mapped definitions so they always pass; only externally-supplied CSRs (ACME/SCEP/CMP + REST upload) are exposed — exactly the CSRs the operator cannot control. The whitelist is presence-only (extension values are opaque DER, not constrained), so rejecting benign standard extensions adds no real security. Net effect: STRICT is unusable with real protocol clients out of the box.
Recommended fixes:
Primary:
2.5.29.37EKU,2.5.29.15keyUsage,2.5.29.19basicConstraints) consulted in the whitelist check (CertificateRequestContentValidator.java:260), OR seed non-required extension-mapped attributes for those OIDs into the platform default set (CsrAttributes.java:45/DefaultRequestAttributeSet.seed()).ProtocolRequestAttributeValidator.java:80→new RequestAttributePolicy(strict, true)); gives operators visibility before enabling STRICT.Lower-priority hardening (fix 1 does NOT cover these):
RaProfileCertificateRequestAttributeServiceImpl.updateConfiguration, ~line 129).CertificateRequestContentValidator.java:202) the way RDN keys already are, so definition/registry code-vs-OID variants can't cause false rejections.Note: analysis based on a local
corecheckout; line numbers are approximate to that revision.Steps to Reproduce
EJBCA-Emptyon an EJBCA authority).Expected Behavior
Certificate issues successfully. Standard, benign CSR extensions (EKU, keyUsage, basicConstraints) should not be rejected by STRICT validation when the RA profile merely has no extension mappings configured — or the operator should be clearly warned that STRICT with an empty mapping set will reject them.
Actual Behavior
ACME finalize returns HTTP != 200 with:
Switching the RA profile to LENIENT makes issuance succeed.
Severity
Major