Skip to main content
Version: v0.25.0 (Latest)

X.509 Certificate Validation

The IDK provides X.509 certificate chain validation through the X509TrustValidationService. This service handles certificate chain building, CA bundle matching, fingerprint comparison, revocation checking, and integration with the platform trust store. It is a core building block for verifying the PKI trust chains behind mDoc issuer certificates, TLS connections, and other X.509-based trust relationships.

Architecture

X509TrustValidationService extends AbstractTrustValidationService("x509", setOf(TYPE_X509, TYPE_CA_BUNDLE)) and is contributed into the session scope via @ContributesIntoSet(SessionScope::class). It participates in the IDK trust framework alongside other trust validation services (such as the ETSI trust list validator) and can be used independently or as part of a composite trust evaluation.

The service supports four validation methods:

MethodDescription
Certificate chain buildingConstructs a chain from the end-entity certificate up to a trusted root CA
CA bundle matchingCompares certificates against PEM-format CA bundles loaded from files or URLs
Fingerprint comparisonMatches certificates by SHA-256 fingerprint or JWK thumbprint
Platform trust storeDelegates to the operating system's built-in trust store

Certificate Chain Validation

The ValidateX509TrustCommand (command ID: trust.x509.validate) validates a certificate by building a chain to a configured trust anchor and optionally checking revocation status.

val x509Validator = session.graph.x509TrustValidationService

// Validate a certificate
val result = x509Validator.validate(
identifierJson = certificateIdentifierJson,
checkRevocation = true
)

if (result.trusted) {
println("Certificate chain is valid")
println("Trust anchor: ${result.trustAnchorSubject}")
println("Chain depth: ${result.chainDepth}")
} else {
println("Validation failed: ${result.reason}")
}

Chain building works by starting from the end-entity certificate and iterating through intermediate CA certificates until it finds one issued by a configured trust anchor (root CA). Each link in the chain is verified: the issuer's public key must validate the subject's signature, and each certificate must be within its validity period.

CA Bundle Configuration

CA bundles are collections of trusted root certificates in PEM format. The IDK can load them from local file paths or remote URLs, giving you flexibility in how trust anchors are distributed.

From Local Files

// Configure CA bundles from local PEM files
// Set in properties:
// trust.anchors.x509.ca-bundle-paths=/etc/ssl/certs/ca-bundle.pem,/app/config/custom-cas.pem

From Remote URLs

# Load CA bundles from HTTPS endpoints
trust.anchors.x509.ca-bundle-urls=https://example.com/ca-bundle.pem

Remote CA bundles are fetched over HTTPS and cached locally. This is useful for scenarios where the CA bundle is maintained centrally and distributed to multiple application instances.

Fingerprint Matching

For cases where you want to trust specific certificates without maintaining a full CA bundle, you can configure trusted fingerprints. The IDK supports SHA-256 certificate fingerprints.

# Trust specific certificates by SHA-256 fingerprint
trust.anchors.x509.trusted-fingerprints=SHA256:a1b2c3d4e5f6...,SHA256:f6e5d4c3b2a1...

Fingerprint matching compares the SHA-256 hash of the DER-encoded certificate against the configured values. JWK thumbprints (RFC 7638) are also supported when validating keys presented in JWK format.

Revocation Checking

Certificate revocation checking verifies that a certificate has not been revoked by its issuing CA after issuance. The IDK implements revocation checking through the RevocationChecker interface, with CompositeRevocationChecker as the default implementation.

Revocation Strategy

The CompositeRevocationChecker tries multiple revocation checking methods in order:

  1. OCSP first (when preferOcsp = true): sends a real-time query to the certificate's OCSP responder
  2. CRL fallback: downloads and checks the Certificate Revocation List if OCSP is unavailable or fails
  3. If both methods are disabled or unavailable, the result depends on the failOnUnknown setting
val revocationChecker = session.graph.revocationChecker

// Check revocation status for a certificate
val revocationResult = revocationChecker.check(
certificate = endEntityCertificate,
issuerCertificate = issuerCertificate,
options = RevocationCheckOptions(
checkOCSP = true,
checkCRL = true,
preferOCSP = true,
timeoutMs = 10000,
useCache = true,
maxCacheAgeMs = 3600000, // 1 hour
failOnUnknown = false
)
)

when (revocationResult.status) {
RevocationStatus.GOOD -> {
println("Certificate is not revoked")
println("Checked via: ${revocationResult.method}")
}
RevocationStatus.REVOKED -> {
println("Certificate has been revoked")
println("Reason: ${revocationResult.revocationReason}")
}
RevocationStatus.UNKNOWN -> {
println("Revocation status could not be determined")
}
RevocationStatus.UNAVAILABLE -> {
println("Revocation checking service is unavailable")
}
}

OCSP Checking

The OCSPChecker sends an Online Certificate Status Protocol request to the OCSP responder URL embedded in the certificate's Authority Information Access (AIA) extension. You can also provide an explicit responder URL via the ocspResponderUrl option, which overrides the AIA value.

OCSP provides real-time revocation status and is the preferred method for most use cases because it does not require downloading a potentially large CRL file.

CRL Checking

The CRLChecker downloads the Certificate Revocation List from the distribution point specified in the certificate's CRL Distribution Points extension. An explicit crlDistributionPoint URL can be provided in the options to override the extension value.

CRLs are cached locally to avoid repeated downloads. The cache respects the maxCacheAgeMs option and the CRL's own nextUpdate field.

RevocationCheckResult

The result of a revocation check contains the following fields:

FieldTypeDescription
statusRevocationStatusGOOD, REVOKED, UNKNOWN, or UNAVAILABLE
methodRevocationCheckMethodHow the status was determined: OCSP, CRL, OCSP_STAPLING, NONE, or MULTIPLE
revocationReasonRevocationReason?If revoked, the reason code

The RevocationReason enum includes standard RFC 5280 reason codes:

ReasonDescription
UNSPECIFIEDNo specific reason given
KEY_COMPROMISEThe certificate's private key was compromised
CA_COMPROMISEThe issuing CA's private key was compromised
AFFILIATION_CHANGEDThe certificate subject's affiliation changed
SUPERSEDEDThe certificate has been replaced by a newer one
CESSATION_OF_OPERATIONThe entity has ceased operations
CERTIFICATE_HOLDThe certificate is temporarily on hold
PRIVILEGE_WITHDRAWNPrivileges granted by the certificate were withdrawn
AA_COMPROMISEThe attribute authority was compromised

RevocationCheckOptions Reference

The full set of options available for revocation checking:

OptionTypeDefaultDescription
checkOCSPBooleantrueEnable OCSP checking
checkCRLBooleantrueEnable CRL checking
preferOCSPBooleantrueTry OCSP before CRL
timeoutMsLong10000Network timeout in milliseconds
useCacheBooleantrueCache OCSP responses and CRLs
maxCacheAgeMsLong3600000Maximum cache age in milliseconds
ocspResponderUrlString?nullOverride OCSP responder URL
crlDistributionPointString?nullOverride CRL distribution point URL
failOnUnknownBooleanfalseTreat unknown status as a validation failure

Configuration

Configure X.509 certificate validation and revocation checking through properties:

# Enable X.509 trust validation (disabled by default)
trust.anchors.x509.enabled=false

# CA bundle paths (comma-separated, PEM format)
trust.anchors.x509.ca-bundle-paths=/path/to/ca-bundle.pem

# CA bundle URLs (comma-separated, fetched over HTTPS)
trust.anchors.x509.ca-bundle-urls=https://example.com/ca-bundle.pem

# Trusted certificate fingerprints (comma-separated, SHA-256)
trust.anchors.x509.trusted-fingerprints=SHA256:abc123...

Revocation Configuration

# Enable revocation checking
trust.revocation.enabled=true

# OCSP settings
trust.revocation.check-ocsp=true

# CRL settings
trust.revocation.check-crl=true

# Prefer OCSP over CRL when both are available
trust.revocation.prefer-ocsp=true

# Network timeout for revocation checks (milliseconds)
trust.revocation.timeout-ms=10000

Module Dependencies

To use X.509 certificate validation, include the trust module in your project:

// build.gradle.kts
dependencies {
implementation("com.sphereon.idk:lib-trust-x509:<version>")
}

The X.509 trust module depends on:

  • lib-trust-api: core trust validation interfaces (AbstractTrustValidationService, TrustContext)
  • lib-crypto-core-public / lib-crypto-core-impl: cryptographic primitives for signature and fingerprint verification
  • lib-http-client: HTTP client for downloading CA bundles and revocation data (OCSP, CRL)