Skip to main content
Version: v0.13

Certificate Validation

The IDK provides comprehensive X.509 certificate validation capabilities, including chain building, signature verification, revocation checking, and policy enforcement. This is essential for validating mDoc issuer certificates and other PKI-based trust relationships.

Certificate Validator

val certificateValidator = session.component.certificateValidator

Basic Validation

// Validate a certificate chain
val result = certificateValidator.validate(
certificate = endEntityCertificate,
intermediates = intermediateCertificates,
trustAnchors = rootCertificates
)

when (result) {
is ValidationResult.Valid -> {
println("Certificate is valid")
println("Chain: ${result.certPath.certificates.size} certificates")
println("Trust anchor: ${result.trustAnchor.subject}")
}
is ValidationResult.Invalid -> {
println("Validation failed: ${result.reason}")
when (result.reason) {
is CertificateExpired -> println("Certificate has expired")
is CertificateNotYetValid -> println("Certificate not yet valid")
is CertificateRevoked -> println("Certificate has been revoked")
is SignatureInvalid -> println("Signature verification failed")
is PathBuildingFailed -> println("Could not build chain to trust anchor")
else -> println("Other error: ${result.reason}")
}
}
}

Chain Building

The validator automatically builds certificate chains:

// Configure chain building options
val options = ChainBuildingOptions(
// Maximum chain depth
maxPathLength = 5,

// Include certificates from these URLs if needed
fetchIntermediates = true,
intermediateUrls = listOf(
"https://certs.example.com/intermediates/"
),

// Timeout for fetching
fetchTimeout = Duration.ofSeconds(10)
)

val result = certificateValidator.validate(
certificate = endEntityCertificate,
options = options
)

Revocation Checking

OCSP (Online Certificate Status Protocol)

// Check revocation via OCSP
val result = certificateValidator.validate(
certificate = endEntityCertificate,
trustAnchors = rootCertificates,
revocationOptions = RevocationOptions(
checkOcsp = true,
ocspResponderUrl = "https://ocsp.example.com", // Optional override
ocspTimeout = Duration.ofSeconds(5),
allowOcspNoCheck = false, // Require OCSP response
cacheOcspResponses = true,
ocspCacheDuration = Duration.ofHours(1)
)
)

// Access OCSP details
if (result is ValidationResult.Valid) {
val ocspResponse = result.ocspResponse
println("OCSP status: ${ocspResponse?.certStatus}")
println("Produced at: ${ocspResponse?.producedAt}")
println("Next update: ${ocspResponse?.nextUpdate}")
}

CRL (Certificate Revocation List)

// Check revocation via CRL
val result = certificateValidator.validate(
certificate = endEntityCertificate,
trustAnchors = rootCertificates,
revocationOptions = RevocationOptions(
checkCrl = true,
crlDistributionPoints = listOf(
"https://crl.example.com/ca.crl"
),
crlTimeout = Duration.ofSeconds(10),
cacheCrls = true,
crlCacheDuration = Duration.ofHours(24)
)
)

Combined Revocation Checking

val result = certificateValidator.validate(
certificate = endEntityCertificate,
trustAnchors = rootCertificates,
revocationOptions = RevocationOptions(
// Try OCSP first, fall back to CRL
checkOcsp = true,
checkCrl = true,
preferOcsp = true,

// Soft fail: continue if revocation check fails
softFail = false, // Set true to allow when revocation info unavailable

// What to do if no revocation information
unknownRevocationPolicy = UnknownRevocationPolicy.REJECT
)
)

Key Usage Validation

Validate certificate key usage extensions:

val result = certificateValidator.validate(
certificate = endEntityCertificate,
trustAnchors = rootCertificates,
keyUsageOptions = KeyUsageOptions(
// Required key usages
requiredKeyUsages = setOf(
KeyUsage.DIGITAL_SIGNATURE,
KeyUsage.KEY_ENCIPHERMENT
),

// Required extended key usages
requiredExtendedKeyUsages = setOf(
ExtendedKeyUsage.SERVER_AUTH,
ExtendedKeyUsage.CLIENT_AUTH
),

// Strict: fail if extension is missing
strictKeyUsage = true
)
)

mDoc Certificate Validation

Validate certificates for ISO 18013-5 mDoc:

// Validate IACA (Issuing Authority Certificate Authority) certificate
val iacaResult = certificateValidator.validateIaca(
certificate = iacaCertificate,
trustAnchors = iacaRootCertificates
)

// Validate Document Signer certificate
val dsResult = certificateValidator.validateDocumentSigner(
certificate = documentSignerCertificate,
iacaCertificate = iacaCertificate
)

// Validate complete mDoc certificate chain
val mdocResult = certificateValidator.validateMdocChain(
mso = mobileSecurityObject,
iacaTrustAnchors = iacaRootCertificates
)

when (mdocResult) {
is MdocValidationResult.Valid -> {
println("mDoc issuer chain is valid")
println("Issuing country: ${mdocResult.issuingCountry}")
println("Issuing authority: ${mdocResult.issuingAuthority}")
}
is MdocValidationResult.Invalid -> {
println("mDoc validation failed: ${mdocResult.reason}")
}
}

Time Validation

Control time-based validation:

val result = certificateValidator.validate(
certificate = endEntityCertificate,
trustAnchors = rootCertificates,
timeOptions = TimeValidationOptions(
// Use specific time for validation (e.g., historical validation)
validationTime = Instant.now(),

// Allow some clock skew
clockSkew = Duration.ofMinutes(5),

// Grace period for recently expired certificates
expirationGracePeriod = Duration.ofDays(1)
)
)

Policy Constraints

Enforce certificate policies:

val result = certificateValidator.validate(
certificate = endEntityCertificate,
trustAnchors = rootCertificates,
policyOptions = PolicyValidationOptions(
// Required certificate policies
requiredPolicies = setOf(
"2.16.840.1.101.2.1.11.5", // Example policy OID
"1.3.6.1.4.1.12345.1.1"
),

// Inhibit policy mapping
inhibitPolicyMapping = false,

// Require explicit policy
requireExplicitPolicy = true,

// Inhibit any policy
inhibitAnyPolicy = false
)
)

Certificate Parsing

Parse and inspect certificate details:

// Parse a certificate from PEM
val certificate = certificateValidator.parseCertificate(pemString)

// Or from DER bytes
val certificate = certificateValidator.parseCertificate(derBytes)

// Inspect certificate details
println("Subject: ${certificate.subject}")
println("Issuer: ${certificate.issuer}")
println("Serial: ${certificate.serialNumber}")
println("Not Before: ${certificate.notBefore}")
println("Not After: ${certificate.notAfter}")
println("Public Key: ${certificate.publicKey.algorithm}")

// Extensions
certificate.extensions.forEach { ext ->
println("Extension: ${ext.oid} (critical: ${ext.isCritical})")
}

// Subject Alternative Names
certificate.subjectAltNames?.forEach { san ->
println("SAN: ${san.type} = ${san.value}")
}

Configuration

Configure certificate validation via properties:

# Trust anchors
certificate.trust-anchors.path=/path/to/trust-anchors/
certificate.trust-anchors.include-system=true

# Chain building
certificate.chain.max-depth=10
certificate.chain.fetch-intermediates=true

# Revocation checking
certificate.revocation.check-ocsp=true
certificate.revocation.check-crl=true
certificate.revocation.prefer-ocsp=true
certificate.revocation.soft-fail=false
certificate.revocation.cache-enabled=true
certificate.revocation.cache-duration-hours=1

# Time validation
certificate.time.clock-skew-minutes=5
certificate.time.expiration-grace-days=0

# mDoc/IACA validation
certificate.iaca.trust-anchors.path=/path/to/iaca-roots/

Best Practices

Always validate complete chains. Don't just validate the end-entity certificate; ensure the entire chain to a trust anchor is valid.

Enable revocation checking for production systems. Use OCSP for real-time status and CRL as a fallback.

Cache revocation information. OCSP responses and CRLs can be cached to improve performance and reduce network traffic.

Use appropriate trust anchors. Only include root certificates you explicitly trust. Don't blindly trust system certificates for high-security applications.

Monitor certificate expiration. Track certificate validity periods and plan for renewal before expiration.