Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CRL and OCSP

Akāmu supports both Certificate Revocation List (CRL) and Online Certificate Status Protocol (OCSP) to communicate revocation status to relying parties. Both protocols are served directly by Akāmu at built-in endpoints.

CRL — GET /ca/{ca_id}/crl and GET /ca/crl

Akāmu generates and serves a signed v2 CRL (RFC 5280) for each configured CA. In multi-CA deployments each CA has its own CRL endpoint:

GET /ca/{ca_id}/crl   # per-CA CRL
GET /ca/crl           # backward-compatible alias → default CA

The CRL is built on each request from the current revocation database. No caching or pre-generation is required for typical issuance volumes. The response uses Content-Type: application/pkix-crl.

Configuring the CRL URL

Set crl_url in each [[ca]] (or [ca]) entry to the public URL of that CA’s CRL endpoint. This URL is embedded in every issued end-entity certificate in the CRLDistributionPoints extension.

For a single-CA deployment the URL is typically the /ca/crl alias:

[ca]
crl_url = "http://acme.example.com/ca/crl"

For multi-CA deployments use the per-CA path so each CA’s certificates point to the correct revocation list:

[[ca]]
id      = "rsa"
crl_url = "http://acme.example.com/ca/rsa/crl"

[[ca]]
id      = "ec"
crl_url = "http://acme.example.com/ca/ec/crl"

Clients that check CRL status fetch this URL and verify the certificate’s serial number against the revocation list.

CRL validity window

The nextUpdate field in the CRL is set to the current time plus crl_next_update_secs (default: 86400 seconds, i.e. one day):

[ca]
crl_next_update_secs = 86400   # one day (default)

Adjust this value to match how frequently clients are expected to re-fetch the CRL.

What a CRL contains

Each CRL entry carries the certificate’s serial number, the revocation timestamp, and the reason code (if one was provided at revocation time). The CRL also includes a cRLNumber extension (RFC 5280 §5.2.3) derived from the current Unix timestamp.

CRL reason codes

CodeCRL reason string
0Unspecified
1Key Compromise
2CA Compromise
3Affiliation Changed
4Superseded
5Cessation of Operation
6Certificate Hold
8Remove From CRL
9Privilege Withdrawn
10AA Compromise

Verifying the CRL manually

curl http://acme.example.com/ca/crl | openssl crl -inform DER -text -noout

OCSP — GET /ca/{ca_id}/ocsp/{request} and POST /ca/{ca_id}/ocsp

Akāmu includes a built-in OCSP responder (RFC 6960) for each configured CA. In multi-CA deployments each CA has its own OCSP endpoint:

POST /ca/{ca_id}/ocsp                  # per-CA OCSP (POST)
GET  /ca/{ca_id}/ocsp/{request}        # per-CA OCSP (GET)
POST /ca/ocsp                          # backward-compatible alias → default CA
GET  /ca/ocsp/{request}                # backward-compatible alias → default CA

The {request} path segment is the base64url-encoded DER OCSPRequest (RFC 6960 §A.1). Both endpoints return a signed OCSPResponse with Content-Type: application/ocsp-response. No authentication is required.

Both endpoints return a signed OCSPResponse with Content-Type: application/ocsp-response. No authentication is required — OCSP is a public protocol.

Configuring the OCSP URL

Set ocsp_url in each [[ca]] (or [ca]) entry to the public base URL of that CA’s OCSP endpoint. This URL is embedded in every issued end-entity certificate in the AuthorityInfoAccess extension.

For a single-CA deployment:

[ca]
ocsp_url = "http://acme.example.com/ca/ocsp"

For multi-CA deployments use the per-CA path:

[[ca]]
id       = "rsa"
ocsp_url = "http://acme.example.com/ca/rsa/ocsp"

[[ca]]
id       = "ec"
ocsp_url = "http://acme.example.com/ca/ec/ocsp"

Clients sending GET requests append the base64url-encoded request to this URL. Clients sending POST requests target the URL directly.

OCSP response behaviour

For each serial number in an OCSPRequest:

DB stateOCSP CertStatus
Certificate not foundunknown (2)
Certificate found, status = "revoked"revoked (1)
Certificate found, any other statusgood (0)

The response is signed with the CA key. The responder identity is set to byName using the CA’s subject DER.

The nextUpdate field in each SingleResponse is fixed at 24 hours after the response is produced.

Verifying OCSP manually

openssl ocsp -issuer ca.pem -cert issued.pem -url http://acme.example.com/ca/ocsp -text

Cross-certificates — GET /ca/{ca_id}/cross-certs

When cross-signing is used, the resulting cross-certificates are available at a public (unauthenticated) endpoint:

GET /ca/{ca_id}/cross-certs

This returns a JSON list of cross-certificates where {ca_id} is the subject (the CA whose public key was cross-signed). Relying parties and ACME clients can use this endpoint to discover cross-certificates for path building.

{
  "cross_certs": [
    {
      "id": "a1b2c3d4-…",
      "issuer_ca_id": "rsa",
      "not_before": "2026-05-06T12:00:00Z",
      "not_after": "2031-05-06T12:00:00Z"
    }
  ]
}

To download the cross-certificate PEM, use the admin API: GET /admin/cross-certs/{id} (see Admin API — CA management endpoints) or akamuctl cross-cert download <id>.

Checking revocation status from the database

To verify whether a specific certificate is currently marked as revoked in Akāmu’s database, query the certificates table by serial number. The serial number is printed in hex by most certificate inspection tools (for example, openssl x509 -serial -noout -in cert.pem):

SELECT serial_number, status, revoked_at, revocation_reason
FROM certificates
WHERE serial_number = '<hex-serial>';

A status value of revoked indicates the certificate has been revoked. revoked_at is a Unix timestamp of when revocation occurred, and revocation_reason is the numeric CRL reason code (or NULL if no reason was specified).