Java Buildpack Client Certificate Mapper

June 16, 2026 · View on GitHub

WorkflowStatus
CICI
ReleaseRelease

The java-buildpack-client-certificate-mapper is a Servlet filter that maps the X-Forwarded-Client-Cert header to the javax.servlet.request.X509Certificate (javax) or jakarta.servlet.request.X509Certificate (jakarta) Servlet attribute. Both base64-encoded DER and URL-encoded PEM certificates, as well as Envoy XFCC format, are supported.

Download

Pre-built jars are available on the Releases page:

  • Releases — tagged versions (e.g. v2.0.2)
  • Snapshot — latest build from main (pre-release, updated on every push)

Development

The project requires Java 8. To build and test from source:

$ ./mvnw clean package

XFCC Header Format

The filter supports base64-encoded DER certificates, URL-encoded PEM certificates, and the Envoy XFCC format. In XFCC format, the header contains key-value fields such as Hash=, Cert=, and Subject=. Field names are matched case-insensitively. Multiple header values and the RFC 9110 comma-delimited equivalent are both supported.

The Hash= field (a SHA-256 fingerprint of the leaf certificate, set by the router) is recognised for format detection and optionally sanity-checked, but it cannot be mapped to an X509Certificate without a Cert= field.

XFCC detection and fallback behaviour

An entry is detected as XFCC format when it structurally begins with a short (≤ 20 characters) all-letter key followed by =, and contains at least one of Hash=, Cert=, or Chain=. JSON format is not supported.

If an entry passes the structural check but contains none of the recognised cert-related fields (e.g. only unknown future fields), it is treated as a raw certificate value; parsing will fail and a warning is logged. This preserves the same external behaviour as the raw-cert fallback path.

CF Gorouter XFCC fields

CF Gorouter emits only Hash= and Subject= in the XFCC header. The Cert= field is emitted only when the router is configured to forward the full client certificate. By= may also be present as the proxy identity, but its value is not a Subject Alternative Name (SAN) — CF app identity certs do not carry URI SANs or DNS SANs, so URI= and DNS= are not emitted and are not recognised by this library.

Request attributes set from XFCC fields

When the header is in XFCC format, the filter sets the following request attributes (first entry that contains the field wins for multi-entry headers):

AttributeSourceValue
org.cloudfoundry.router.xfcc.hashHash=SHA-256 fingerprint of the client certificate
org.cloudfoundry.router.xfcc.subjectSubject=Full Subject DN of the client certificate
org.cloudfoundry.router.xfcc.app.guidSubject= OU=app:<guid>CF app GUID parsed from the Subject DN
org.cloudfoundry.router.xfcc.space.guidSubject= OU=space:<guid>CF space GUID parsed from the Subject DN
org.cloudfoundry.router.xfcc.org.guidSubject= OU=organization:<guid>CF organization GUID parsed from the Subject DN
org.cloudfoundry.router.xfcc.instance.guidSubject= CN=<guid>CF app instance GUID parsed from the Subject DN

The CF Subject DN format is: CN=<instance-guid>,OU=app:<app-guid>,OU=space:<space-guid>,OU=organization:<org-guid>.

These attributes are set regardless of whether a Cert= field is present, so applications can identify the caller even when only a Hash= and Subject= are forwarded by the router.

Unknown fields are silently skipped and logged at FINE level.

Specifications:

Error Handling

Certificate parse failures (invalid Base64, invalid CertificateFactory input) are caught inside doFilter(), logged as a WARNING, and the request is passed to the next filter without setting the certificate attribute. This behaviour is unchanged from earlier versions.

Behaviour change (this version): previously, a malformed URL-encoded value in the header (e.g. a %GG sequence that URLDecoder cannot decode) caused an IllegalArgumentException to propagate uncaught out of doFilter(), typically resulting in a 500 response. From this version that exception is caught alongside CertificateException and the same log-and-continue behaviour applies.

Debug Logging

The filter uses Java Util Logging (JUL). To enable debug output, set the logger level for org.cloudfoundry.router to FINE. When enabled, the filter logs the XFCC field names present in each header (e.g. Hash, Cert, Subject). Certificate values are never logged.

CI / Workflows

WorkflowTriggerDescription
CIpush to main, pull requests, manualBuilds and runs all tests. On push to main (after tests pass) also publishes the jar to the rolling snapshot release.
Releasemanual (workflow_dispatch)Bumps to release version, tags vX.Y.Z, creates a GitHub Release with the jar attached, then advances to the next SNAPSHOT version.

All workflows can be triggered manually from Actions → select workflow → Run workflow in the GitHub UI.

License

This project is released under version 2.0 of the Apache License.