Skip to content

STRICT request-attribute validation rejects standard CSR extensions (EKU), making ACME/protocol issuance fail on RA profiles with no extension mappings #1883

Description

@lubomirw

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:

  1. 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()).
  2. Warn-only mode — run the whitelist under LENIENT too, emitting warnings instead of skipping it (one-line change at ProtocolRequestAttributeValidator.java:80new RequestAttributePolicy(strict, true)); gives operators visibility before enabling STRICT.

Lower-priority hardening (fix 1 does NOT cover these):

  1. 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).
  2. 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

  1. Pick or create an RA profile with no custom request attributes and no extension mappings (e.g. EJBCA-Empty on an EJBCA authority).
  2. Set the profile's request-attribute validation to STRICT.
  3. 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.
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug fix

    Type

    Projects

    Status
    Open

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions