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

Trust Establishment

The IDK provides a unified trust establishment framework that validates trust across multiple protocols: ETSI trust lists, X.509 PKI, OpenID Federation, and Decentralized Identifiers. Each protocol is implemented as a TrustValidationService that plugs into the framework through dependency injection. Application code uses a single API regardless of the underlying trust mechanism.

Trust validation checks whether:

  • The issuer of this credential is authorized to issue it
  • The certificate chain is valid and rooted in a recognized trust anchor
  • The credential or certificate has not been revoked
  • The issuer meets the regulatory requirements of the target framework

Supported Trust Frameworks

The framework dispatches validation requests based on the TrustContext.type field. Each context type maps to a dedicated TrustValidationService implementation contributed via @ContributesIntoSet(SessionScope::class):

Context TypeConstantModuleDescription
TYPE_ETSI_TSLetsi_tsllib-trust-etsiETSI TS 119 612/602 trust service status lists
TYPE_X509x509lib-trust-x509X.509 certificate chain and CA bundle validation
TYPE_CA_BUNDLEca_bundlelib-trust-x509CA bundle file/URL-based validation
TYPE_OPENID_FEDERATIONopenid_federationopenid-federation-trustOpenID Federation trust chain resolution
TYPE_DIDdidlib-trust-didDID-based trust with method allow-lists
TYPE_PUBLIC_KEYpublic_keylib-trust-coreDirect public key trust

The framework selects the correct implementation by calling supports(context) on each registered service. You can enable multiple frameworks simultaneously and the system will route each request to the appropriate validator.

Validating Trust

All trust validation flows through the TrustValidationService.validate() method. You construct a TrustValidationRequest that pairs an identifier with a TrustContext, and the service returns a TrustValidationResult describing the outcome.

Building a Request

A TrustValidationRequest contains:

  • identifier: The IdentifierOptsOrResult to validate (a certificate, DID, or other identifier).
  • context: A TrustContext specifying which framework to use and any framework-specific parameters.
  • validationTime: Optional Instant to validate against a specific point in time (defaults to now).
  • checkRevocation: Whether to perform revocation checking (defaults to true).

Handling the Result

The TrustValidationResult contains the validation outcome:

  • trusted: true if the identifier is trusted under the given context.
  • status: A TrustStatus enum: TRUSTED, UNTRUSTED, REVOKED, EXPIRED, NOT_YET_VALID, VALIDATION_ERROR, or UNKNOWN.
  • trustAnchor: The TrustAnchor that was matched, if any.
  • validationPath: The chain of identifiers from the subject to the trust anchor.
  • details: A human-readable explanation of the validation outcome.
  • validatedAt: The Instant when validation was performed.
val trustService = session.graph.trustValidationService

// Build a trust context for X.509 validation
val context = TrustContext(
type = TrustContext.TYPE_X509
)

// Create the validation request
val request = TrustValidationRequest(
identifier = certificateIdentifier,
context = context,
checkRevocation = true
)

// Validate and handle the result
val result = trustService.validate(request)

when (result.status) {
TrustStatus.TRUSTED -> {
println("Trusted via anchor: ${result.trustAnchor?.name}")
println("Validation path: ${result.validationPath}")
}
TrustStatus.REVOKED -> {
println("Certificate has been revoked")
}
TrustStatus.EXPIRED -> {
println("Certificate has expired")
}
TrustStatus.UNTRUSTED -> {
println("No matching trust anchor found")
}
TrustStatus.NOT_YET_VALID -> {
println("Certificate is not yet valid")
}
TrustStatus.VALIDATION_ERROR -> {
println("Validation failed: ${result.details}")
}
TrustStatus.UNKNOWN -> {
println("Trust status could not be determined")
}
}

Choosing a Trust Framework

The TrustContext constructor accepts optional framework and parameters fields for framework-specific behavior:

// ETSI trust list validation with territory filter
val etsiContext = TrustContext(
type = TrustContext.TYPE_ETSI_TSL,
framework = "etsi",
parameters = mapOf("territory" to "DE")
)

// OpenID Federation with a specific trust anchor
val oidfContext = TrustContext(
type = TrustContext.TYPE_OPENID_FEDERATION,
parameters = mapOf("trustAnchor" to "https://federation.example.com")
)

// DID trust limited to specific methods
val didContext = TrustContext(
type = TrustContext.TYPE_DID,
parameters = mapOf("allowedMethods" to "web,key")
)

Using Commands

The framework also exposes trust operations as commands for integration with the IDK command infrastructure:

CommandIDDescription
ValidateTrustCommandtrust.validation.validateValidate trust for an identifier
GetTrustAnchorsCommandtrust.anchors.listList trust anchors for a context
RefreshTrustAnchorsCommandtrust.anchors.refreshRefresh cached trust anchors
CheckRevocationCommandtrust.revocation.checkCheck revocation status of a certificate

Trust Anchors

A TrustAnchor represents a root of trust in the system. Each trust framework resolves anchors differently. X.509 uses root CA certificates, ETSI uses Trust Service Providers from published trust lists, OpenID Federation uses federation entity statements, and DID-based trust uses trusted DID documents.

Every TrustAnchor contains:

  • id: A unique identifier for the anchor.
  • type: One of TYPE_ETSI_TSP, TYPE_ROOT_CA, TYPE_DID_DOCUMENT, TYPE_OPENID_FED_ENTITY, or TYPE_PUBLIC_KEY.
  • name: A human-readable name.
  • keyInfo: The anchor's public key as a ResolvedKeyInfoType.
  • uri: An optional URI for the anchor (e.g., the TSL URL or DID).
  • metadata: Framework-specific metadata as key-value pairs.
  • validFrom / validUntil: Optional validity period.

Retrieving Anchors

Use getTrustAnchors() on a TrustValidationService or the GetTrustAnchorsCommand to list the currently loaded trust anchors:

val trustService = session.graph.trustValidationService

// Get all trust anchors for the service
val anchors = trustService.getTrustAnchors()

for (anchor in anchors) {
println("Anchor: ${anchor.name} (${anchor.type})")
println(" ID: ${anchor.id}")
anchor.uri?.let { println(" URI: $it") }
anchor.validUntil?.let { println(" Valid until: $it") }
}

Refreshing Anchors

Trust anchors can become stale as trust lists are updated or certificates rotate. Call refresh() to force a reload from the upstream source:

val refreshed = trustService.refresh()
if (refreshed) {
println("Trust anchors refreshed successfully")
}

The cache TTLs configured in TrustCacheConfig control how often automatic refreshes occur in the background.

Revocation Checking

The framework checks whether certificates have been revoked before establishing trust. The RevocationChecker interface handles this through a composite strategy: it attempts OCSP first, then falls back to CRL if OCSP is unavailable or fails.

How It Works

When checkRevocation is true in a TrustValidationRequest, the validator delegates to a CompositeRevocationChecker that:

  1. Attempts an OCSP check if trust.revocation.check-ocsp is enabled.
  2. Falls back to CRL checking if OCSP is unavailable and trust.revocation.check-crl is enabled.
  3. Returns a RevocationCheckResult with a RevocationStatus and the RevocationCheckMethod used.

The RevocationStatus enum indicates the outcome:

StatusMeaning
GOODCertificate is not revoked
REVOKEDCertificate has been revoked
UNKNOWNRevocation status could not be determined
UNAVAILABLERevocation checking endpoints are unreachable

The RevocationCheckMethod enum indicates which mechanism was used:

MethodDescription
OCSPOnline Certificate Status Protocol
CRLCertificate Revocation List
OCSP_STAPLINGOCSP response stapled to the TLS handshake
NONENo revocation check was performed
MULTIPLEMultiple methods were attempted

Explicit Revocation Checks

You can also check revocation directly using the CheckRevocationCommand or the RevocationChecker interface:

val revocationChecker = session.graph.revocationChecker

val result = revocationChecker.checkRevocation(
certificate = certificateBytes,
issuerCertificate = issuerCertBytes,
options = RevocationCheckOptions()
)

when (result.status) {
RevocationStatus.GOOD -> println("Certificate is valid")
RevocationStatus.REVOKED -> println("Certificate is revoked: ${result.reason}")
RevocationStatus.UNKNOWN -> println("Revocation status unknown")
RevocationStatus.UNAVAILABLE -> println("Revocation check unavailable")
}

Configuration

Trust validation is configured through properties that map to the TrustConfig data class hierarchy. Each section controls a different aspect of the framework.

Validation Settings

# Enable/disable trust validation globally
trust.validation.enabled=true

# Check revocation by default when not specified per-request
trust.validation.default-check-revocation=true

Trust Anchor Sources

Configure which trust frameworks are active and where their anchors come from:

# X.509 CA bundle trust
trust.anchors.x509.enabled=true
trust.anchors.x509.ca-bundle-paths=/path/to/ca-bundle.pem
trust.anchors.x509.trusted-fingerprints=SHA256:...

# ETSI trust lists (EU List of Trusted Lists)
trust.anchors.etsi.enabled=true
trust.anchors.etsi.lotl-url=https://ec.europa.eu/tools/lotl/eu-lotl.xml

# OpenID Federation trust anchors
trust.anchors.oidfed.enabled=true
trust.anchors.oidfed.trust-anchors=https://federation.example.com

# DID-based trust
trust.anchors.did.enabled=true
trust.anchors.did.allowed-methods=web,key

Revocation Settings

# OCSP and CRL configuration
trust.revocation.check-ocsp=true
trust.revocation.check-crl=true
trust.revocation.prefer-ocsp=true
trust.revocation.timeout-ms=5000

Cache TTLs

The framework caches trust lists and revocation responses to avoid repeated network calls. Configure TTLs to balance freshness against performance:

# How long to cache trust lists (ETSI, X.509 bundles)
trust.cache.trust-list-ttl-minutes=60

# How long to cache revocation responses (OCSP, CRL)
trust.cache.revocation-ttl-minutes=15

# How long to cache OpenID Federation entity statements
trust.cache.oidfed-entity-ttl-minutes=30

Full Configuration Reference

The properties above map to the following TrustConfig structure:

TrustConfig
validation: TrustValidationConfig
enabled: Boolean
defaultCheckRevocation: Boolean
anchors: TrustAnchorsConfig
x509: X509TrustConfig
enabled, caBundlePaths, caBundleUrls, trustedFingerprints, maxFailedSources
etsi: EtsiTrustConfig
enabled, lotlUrl, verifySignatures, territories
oidfed: OidfTrustConfig
enabled, trustAnchors, maxChainDepth, requiredTrustMarks
did: DidTrustConfig
enabled, trustedDids, allowedMethods
revocation: RevocationConfig
enabled, checkOcsp, checkCrl, preferOcsp, timeoutMs
cache: TrustCacheConfig
trustListTtlMinutes (default 60)
revocationTtlMinutes (default 15)
oidfedEntityTtlMinutes (default 30)

Next Steps