Appearance
Certificate Revocation Lists (CRLs)
A CSCA can revoke a Document Signer Certificate, for example when its private key was stolen. The CSCA then lists the certificate in its Certificate Revocation List (CRL).
During Passive Authentication the DocVal Service checks the Document Signer Certificate of the document against the CRLs that you supply. The result is the object revocation in the Passive Authentication details. Its field cert_status gives the revocation status.
The check is optional. The DocVal Service ships no CRLs and does not download CRLs. Without your own CRLs, cert_status is always undetermined. With the default revocation policy soft-fail, this does not change the Passive Authentication result.
To use the check:
- Get the CRLs of the CSCAs of your target countries.
- Mount them into the DocVal Service, see Providing CRLs.
- Keep them fresh, see Updating CRLs.
- Watch the state of the CRLs, see Monitoring.
Providing CRLs
Mount your CRL files into the directory /app/crls, as a volume or as individual files. The DocVal Service reads every file in this directory. It skips files whose name starts with a dot, for example .gitkeep.
File Formats
| Format | Description |
|---|---|
| DER | Binary X.509 CRL. |
| PEM | Base64 with the -----BEGIN X509 CRL----- header. |
| LDIF | RFC 2849, the format of the ICAO PKD download. The DocVal Service reads the CRLs and skips the certificates. |
If you use the ICAO PKD, use the complete file icaopkd-001-complete-*.ldif. Delta files are not supported.
One file can hold several CRLs. When several CRLs have the same issuer name and come from the same CSCA, the DocVal Service uses only one of them:
- The CRL signed with the newest CSCA key wins.
- Between CRLs of one key, the newer
thisUpdatewins, then the highercRLNumber.
The other CRLs get the state superseded in the CRL endpoint.
Keep only the newest CRL of each CSCA, and remove older CRL files. When a CSCA changes its name, or its key without a link certificate, the DocVal Service cannot see that the old CRL belongs to the same CSCA. The old CRL then stays accepted, and later it shows up as a stale CRL.
When the DocVal Service cannot read or parse a file, or when a file holds no CRL, the DocVal Service does not start. A CRL that the DocVal Service can read but does not accept does not stop the start. The DocVal Service rejects this CRL and shows the reason in the CRL endpoint, for example a signature that is not valid.
Updating CRLs
A CSCA issues a new CRL at least every 90 days. After a revocation, the CSCA distributes a new CRL within 48 hours (ICAO Doc 9303-12, eighth edition, 2021, §4.1.5). Update your CRLs so that a new CRL reaches your installation within 48 hours.
The DocVal Service reads the directory only at start. Restart the DocVal Service after a CRL update.
A CRL is stale when its nextUpdate is in the past. The DocVal Service still uses a stale CRL. A document that is listed on it gets revoked. All other documents of that CSCA get undetermined.
CRLs That Nobody Renews
Some CSCAs publish no new CRL after nextUpdate, for months or for years. Many CRLs in the complete ICAO PKD file are stale for this reason, even after an update of the file.
With soft-fail, such a CRL still revokes the certificates that it lists. With hard-fail, all documents of the CSCA fail Passive Authentication until the CSCA publishes a new CRL. The CRL endpoint shows which CRLs are stale.
Revocation Status
The object revocation in the Passive Authentication details has the field cert_status. It says whether the Document Signer Certificate is revoked.
json
"revocation": {
"cert_status": "unrevoked",
"policy": "soft-fail",
"stale_crl_grace_seconds": 172800,
"crl": {
"this_update": "2026-09-01T12:00:00Z",
"next_update": "2026-11-30T12:00:00Z",
"issuer": "CN=CSCA Utopia,O=Test Gov,C=UT"
}
}| Value | Meaning |
|---|---|
unrevoked | A fresh CRL applies to the certificate and does not list it. |
revoked | An applicable CRL lists the certificate, or the CSCA key that issued it is compromised. |
undetermined | The DocVal Service cannot tell whether the certificate is revoked. |
The values follow ICAO Doc 9303-12, eighth edition, 2021, Appendix D.1.2.4. There, a listed certificate has the status UNSPECIFIED. The DocVal Service reports it as revoked.
The object crl describes the CRL behind cert_status, with the fields this_update, next_update and issuer. For revoked, this is the CRL that lists the certificate, or the CRL that revoked the key of the issuing CSCA. Otherwise, it is the newest CRL that applies to the certificate.
undetermined has two forms:
- With a
crlobject: the CRL that applies is stale. Itsnext_updateis in the past. - With
crl: null: no CRL applies to the certificate. This is always the case when you provide no CRLs.
The DocVal Service runs the check only when it finds a trusted Country Certificate for the Document Signer Certificate. Otherwise cert_status is undetermined with crl: null, and document_certificate_valid is false or null. Passive Authentication fails in this case anyway.
policy gives the revocation policy of the server. stale_crl_grace_seconds gives the grace period of a stale CRL under hard-fail, in seconds.
Revocation Policy
The environment variable REVOCATION_POLICY sets how cert_status changes the overall passive_authentication result. cert_status itself is the same under both policies.
REVOCATION_POLICY | unrevoked | revoked | undetermined |
|---|---|---|---|
soft-fail (default) | unchanged | passive_authentication: false | unchanged |
hard-fail | unchanged | passive_authentication: false | passive_authentication: false |
With hard-fail, every document without a fresh CRL fails Passive Authentication. For a stale CRL, the documents fail only after a grace period. Set hard-fail only when you have the CRLs of all CSCAs of your target countries and keep them fresh.
With hard-fail, the DocVal Service does not start without at least one accepted CRL. The start-up message names the rejected files and the reasons.
Grace Period for Stale CRLs
With hard-fail, a stale CRL has a grace period. A CSCA can publish its next CRL as late as nextUpdate (RFC 5280, 2008, §5.1.2.5). The grace period gives the new CRL time to reach your installation.
The environment variable REVOCATION_STALE_CRL_GRACE sets the length of the grace period. The value is a number with a unit, for example 48h, 2d or 30m. The units are d (days), h (hours), m (minutes) and s (seconds). With 0, there is no grace period. The default is 48h. An empty value also gives the default.
The grace period starts at crl.next_update. During the grace period, cert_status: undetermined with a crl object does not change the Passive Authentication result. After the grace period, the document fails Passive Authentication. cert_status stays undetermined during the grace period. It does not become unrevoked.
When no CRL applies to a document, there is no grace period. With hard-fail, the document fails Passive Authentication at once.
Only a result in the grace period has all of these values:
policyishard-fail.cert_statusisundetermined.crl.next_updateis in the past, but less thanstale_crl_grace_secondsseconds ago.passive_authenticationistrue.
The grace period changes only the Passive Authentication result. It does not change the health status, the CRL metrics docval_crl_stale and docval_crl_next_update_seconds, the log or the CRL endpoint. For them, a CRL is stale from its nextUpdate on. So you see the stale CRL at nextUpdate. You then have the grace period to react before documents fail.
Which CRL Applies to a Document
A CRL applies to a document when it comes from the CSCA that issued the Document Signer Certificate. The DocVal Service identifies the CSCA by its name and by its key. It also follows a name change through the issuerAltName extension of the CRL, and a key change through a CSCA link certificate.
A gap remains when a CSCA changed its name and its key without a link certificate and without issuerAltName. Documents under the old name then get undetermined. The CRL endpoint lists the certificates of the master list without an applicable CRL, so you can see such a gap.
A Revoked CSCA
A CRL can also list a CSCA certificate. Then the key of this CSCA is compromised. All documents whose Document Signer Certificate was issued with this key get cert_status: revoked. This also applies to a newer CSCA key that a link certificate certified with the compromised key (ICAO Doc 9303-12, eighth edition, 2021, §4.1.5.1).
document_certificate_valid stays true, because the certificate chain itself is intact. /certificate-list shows the CSCA certificate with revoked: true.
Monitoring
Three sources show the state of the CRLs:
- The health indicator
crlof/actuator/health, see The StatusDEGRADED. Use it to notice a problem. - The CRL metrics. They give the
nextUpdateof every accepted CRL. Use them to raise an alert. - The CRL endpoint
/actuator/crl. It lists every CRL, the certificates of the master list without an applicable CRL and the compromised CSCA keys. Use it to find the cause of a problem.
The Status DEGRADED
The health indicator crl of the health endpoint reports DEGRADED when at least one CRL gives DEGRADED. The result for each CRL depends on its state and on REVOCATION_STALE_CRL_HEALTH:
| State of the CRL | degraded | up |
|---|---|---|
accepted, nextUpdate in the future | UP | UP |
| accepted, stale | DEGRADED | UP |
| superseded | UP | UP |
rejected, reason no_anchor_for_country | UP | UP |
rejected, nextUpdate in the past | DEGRADED | UP |
rejected, nextUpdate in the future or absent | DEGRADED | DEGRADED |
Otherwise the indicator reports UP, even when no CRL is loaded.
When the indicator reports DEGRADED, the status field of /actuator/health is also DEGRADED. The HTTP status code stays 200 OK, so Docker health checks and Kubernetes probes stay green.
The wget health check does not see DEGRADED
The health check in the Docker Compose example tests only the HTTP status code. To notice DEGRADED, read the status field of the response body, or watch the CRL metrics.
Stale CRLs and the Health Status
The environment variable REVOCATION_STALE_CRL_HEALTH sets whether a stale CRL gives DEGRADED. The values are degraded and up. An empty value follows the revocation policy: hard-fail gives degraded, soft-fail gives up.
The setting changes only the status. A stale CRL stays in the health details, in the log, in docval_crl_stale and in the CRL endpoint.
Health Details
By default, /actuator/health shows only the overall status. Two settings of Spring Boot show more:
MANAGEMENT_ENDPOINT_HEALTH_SHOWCOMPONENTS=alwaysshows the status of each health indicator, for examplecrl,diskSpaceandping. Use it to see whethercrlcauses the overall status.MANAGEMENT_ENDPOINT_HEALTH_SHOWDETAILS=alwaysalso shows the details of each indicator.
The details of crl are:
| Field | Description |
|---|---|
accepted_count | Number of accepted CRLs. |
stale_crl_health | The effective value of REVOCATION_STALE_CRL_HEALTH, degraded or up. |
stale_crl_health_source | explicit, default for soft-fail or default for hard-fail. |
stale | The stale accepted CRLs, each with issuer and next_update. |
rejected | The rejected CRLs except the reason no_anchor_for_country, each with label, issuer, country, reason and expired. |
reason has the values of reject_reason in the CRL endpoint. expired is true when the nextUpdate of the CRL is in the past.
The health details have no authentication
The details show the issuers of the stale and the rejected CRLs. With soft-fail, the revocation check for these CSCAs is weaker. Do not make the health endpoint with details available on the public network. MANAGEMENT_SERVER_PORT moves all Actuator endpoints to a separate port, see CRL Endpoint.
Example JSON
json
{
"status": "DEGRADED",
"components": {
"crl": {
"status": "DEGRADED",
"details": {
"accepted_count": 2,
"stale_crl_health": "degraded",
"stale_crl_health_source": "default for hard-fail",
"stale": [
{
"issuer": "CN=CSCA Utopia,O=Test Gov,C=UT",
"next_update": "2026-08-01T12:00:00Z"
}
],
"rejected": [
{
"label": "old-utopia.crl",
"issuer": "CN=CSCA Utopia 2010,O=Test Gov,C=UT",
"country": "UT",
"reason": "bad_signature",
"expired": true
}
]
}
},
"ping": {
"status": "UP"
}
}
}CRL Metrics
The Prometheus endpoint also exposes the state of the CRLs:
| Metric | Description |
|---|---|
docval_crl_next_update_seconds{label,issuer,country} | The nextUpdate of an accepted CRL as epoch seconds. Use it to raise an alert for a stale CRL. |
docval_crl_accepted | Number of accepted CRLs. |
docval_crl_stale | Number of accepted CRLs whose nextUpdate is in the past. |
docval_crl_rejected{country,reason} | Number of rejected CRLs per country and reason. |
docval_crl_anchors_without_crl | Number of certificates in the master list without an applicable CRL. |
All five metrics are gauges. docval_crl_rejected has no label that tells whether a rejected CRL is expired. The health details show it.
Examples of alert rules in PromQL, for the target countries DE and FR:
# Replace 86400 with your own download interval in seconds.
time() - min by (country) (docval_crl_next_update_seconds{country=~"DE|FR"}) > 86400
# A CRL of a target country becomes stale in less than 7 days.
min by (country) (docval_crl_next_update_seconds{country=~"DE|FR"}) - time() < 7 * 86400
# A target country has no accepted CRL.
absent(docval_crl_next_update_seconds{country="DE"})
# A CRL of a target country is rejected. This includes expired CRLs.
docval_crl_rejected{country=~"DE|FR", reason!="no_anchor_for_country"} > 0absent() with a regular expression fires only when all matching series are missing. So write one absent() rule per country.
CRL Endpoint
The DocVal Service can expose a /actuator/crl endpoint with the state of the CRLs. It explains the crl health indicator and shows which CSCAs are covered.
The endpoint is disabled by default. Enable it with the same mechanism as the Prometheus endpoint:
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=health,crl
Separate several endpoints with commas, for example health,prometheus,crl.
The endpoint has no authentication
The endpoint shows for which CSCAs the DocVal Service has no usable CRL. With the policy soft-fail, documents of these CSCAs pass without a revocation check. Do not make the endpoint available on the public network. Set MANAGEMENT_SERVER_PORT to move all Actuator endpoints to a separate port.
The response has the counters accepted_count, rejected_count and stale_count, and three lists.
crls has one entry per CRL:
| Field | Description |
|---|---|
label | The name of the file that holds the CRL. |
issuer | The issuer name of the CRL. |
country | The country of the issuer. |
crl_number | The value of the cRLNumber extension, as a decimal string. |
this_update | The thisUpdate of the CRL. |
next_update | The nextUpdate of the CRL. |
entry_count | The number of revoked certificates on the CRL. |
signing_anchor | The subject name of the certificate in the master list that verified the signature. |
state | accepted, superseded or rejected. |
stale | Whether the CRL is accepted and its next_update is in the past. |
reject_reason | Why the CRL was rejected, see below. null when the CRL was not rejected. |
detail | Further text for the state. null when the CRL was accepted. |
All date fields are strings in ISO 8601, UTC, with Z, for example 2026-09-01T12:00:00Z.
A CRL is superseded when another CRL wins over it, see File Formats.
reject_reason has one of these values:
reject_reason | Meaning |
|---|---|
no_anchor_for_country | The issuer name has no countryName, or the master list has no certificate of that country. |
missing_next_update | The CRL has no nextUpdate. |
delta_crl | The CRL is a delta CRL. |
issuing_distribution_point | The CRL has an issuingDistributionPoint extension. |
unknown_critical_extension | The CRL or one of its entries has a critical extension that the DocVal Service does not process. |
malformed | The revoked certificates of the CRL, or the value of a critical extension, cannot be decoded. |
bad_signature | The signature verifies against no certificate of the country. |
anchor_without_crl_sign | The certificate that signed the CRL has a keyUsage extension without cRLSign. |
issuer_name_mismatch | The issuer name of the CRL is not the name of the certificate that signed it, and no certificate of the same CSCA has that name. |
anchors_without_crl names the certificates of the master list that no CRL applies to, with the fields subject_name, issuer, serial_number, not_before, not_after and fingerprint_sha1. Documents under these certificates get cert_status: undetermined.
compromised_anchors names the CSCA certificates whose key a CRL revoked, with the fields subject_name, serial_number, revoking_crl_issuer and cause. cause says how the key became compromised:
cause | Meaning |
|---|---|
listed | The CRL lists the serial number of this certificate. |
same_key | The CRL lists another CSCA certificate with the same public key. |
cascaded | A link certificate for the key was signed with an already compromised key. |
serial_number is a decimal string in both lists. not_before and not_after use the same date format as the CRL entries.
Example JSON
json
{
"accepted_count": 1,
"rejected_count": 0,
"stale_count": 0,
"crls": [
{
"label": "utopia.crl",
"issuer": "CN=CSCA Utopia,O=Test Gov,C=UT",
"country": "UT",
"crl_number": "42",
"this_update": "2026-09-01T12:00:00Z",
"next_update": "2026-10-01T12:00:00Z",
"entry_count": 3,
"signing_anchor": "CN=CSCA Utopia,O=Test Gov,C=UT",
"state": "accepted",
"stale": false,
"reject_reason": null,
"detail": null
}
],
"anchors_without_crl": [],
"compromised_anchors": []
}Runbook
How urgent a problem is depends on the revocation policy. With hard-fail, all documents of the affected CSCA fail Passive Authentication when their CRL is missing, or when it is stale for longer than the grace period. With soft-fail, these documents still pass, but without a revocation check.
To find the cause of a DEGRADED status, show the health details or read the CRL endpoint. MANAGEMENT_ENDPOINT_HEALTH_SHOWCOMPONENTS=always alone shows whether crl causes the status.
- A CRL is stale, and its source has a newer CRL.
docval_crl_staleis above 0. Download the newer CRL and restart the DocVal Service. - A CRL is stale, and its source has nothing newer. The CSCA is late with its CRL, see CRLs That Nobody Renews. With
soft-fail, keep a stale CRL that lists certificates, because it still revokes them. A stale CRL that lists no certificate revokes nothing. You can remove its file. Withhard-fail, only a new CRL from the CSCA helps. - A CRL was rejected.
docval_crl_rejectedis above 0. WithREVOCATION_STALE_CRL_HEALTH=up, the health status changes fromDEGRADEDtoUPwhen thenextUpdateof the rejected CRL passes. The CRL is still rejected, anddocval_crl_rejecteddoes not change. The CRL endpoint shows thereject_reason:no_anchor_for_country: The master list has no certificate of that country. Without it, no document of that country passes Passive Authentication. Add the CSCA certificate to the master list.bad_signature: Make sure that the master list has the newest CSCA certificate of that country. One cause is a new CSCA certificate that is not yet in the master list. Another cause is an expired CRL of an old CSCA, for example from the ICAO PKD file. The master list no longer has the certificate of this CSCA. So no document of this CSCA passes Passive Authentication, and you do not need the CRL.- Any other reason: The CRL does not match the ICAO profile. Get a CRL that matches it.
- A CSCA has no CRL.
docval_crl_anchors_without_crlis above 0./actuator/crllists the CSCA certificate underanchors_without_crl. Get the CRL of that CSCA. For a gap after a name change, only the CSCA can close the gap. - No CRL is loaded.
docval_crl_acceptedis 0. Withhard-failthe DocVal Service does not start. Mount CRLs into/app/crlsand restart the DocVal Service. - A CSCA key is compromised.
/actuator/crllists the CSCA certificate undercompromised_anchors. Your installation has no defect. All documents under this key getrevokedand fail Passive Authentication, as intended.
