Revocation
Revoke a leaf by adding it to its intermediate's certificate revocation list, then give the CRL files to the servers that check them.
Revoke a leaf
homepki revoke adds a leaf's serial number to its intermediate's certificate revocation list (CRL) and re-signs the CRLs. Name the certificate with exactly one of --server, --client or --cert:
$ homepki revoke -d runlocal.dev -i bu1 --client my-client
$ homepki revoke -d runlocal.dev -i bu1 --server kong-gateway --reason keyCompromise
$ homepki revoke -d runlocal.dev -i bu1 --cert ./my-service.crt # e.g. written by sign --out
$ homepki revoke -d runlocal.dev -i bu1 -c cli1 --reason keyCompromise
Revoking cli1.bu1.runlocal.dev (serial bd8e8dda8f064b8ec14bcea02c35f62b) under runlocal.dev/bu1
Wrote ~/.homepki/runlocal-dev/bu1/bu1-intermediate-ca.crl (1 revoked, next update 2027-09-26 12:21)
Wrote ~/.homepki/runlocal-dev/ca/runlocal-dev-root-ca.crl
Wrote ~/.homepki/runlocal-dev/bu1/bu1-crl-chain.crl
Certificate revoked. Reload servers that read the CRL.
- The certificate must have been issued by that intermediate; a certificate from another CA is refused.
- Revoking a certificate twice prints
… is already revoked (since <date>)and exits 0 without writing anything. - The certificate and key files stay in place. Re-issuing the name with
--forcegives it a new serial number that is not revoked.
Revocation reasons
--reason takes an RFC 5280 reason name, case-insensitive. The default is unspecified, which omits the reason from the CRL entry.
--reason | Code |
|---|---|
unspecified | 0 |
keyCompromise | 1 |
cACompromise | 2 |
affiliationChanged | 3 |
superseded | 4 |
cessationOfOperation | 5 |
certificateHold | 6 |
privilegeWithdrawn | 9 |
CRL files
Both revoke and crl write three PEM files:
| File | Content | Give it to |
|---|---|---|
<int>/<int>-intermediate-ca.crl | the intermediate's CRL, with every revoked leaf | a verifier that checks the leaf only |
ca/<root>-root-ca.crl | the root's CRL, always empty | a verifier that wants one CRL file per CA |
<int>/<int>-crl-chain.crl | both, intermediate first | nginx ssl_crl, openssl verify -crl_check_all: anything that checks every tier |
The CRL is the only record of revocations. There is no CA database: revoke reads the existing intermediate CRL, checks that the intermediate signed it, appends the entry and increments the CRL number.
Write CRLs before revoking anything
A server configured with a CRL file refuses to start when the file is missing. homepki crl writes the three files with every revocation already recorded, or empty ones on a fresh intermediate:
$ homepki crl -d runlocal.dev -i bu1
$ homepki crl -d runlocal.dev -i bu1 --validity 7d
CRL lifetime
Each CRL carries a nextUpdate time. OpenSSL-based servers reject a CRL past it with CRL has expired, which then rejects every client. The default is 365 days; --validity on revoke or crl changes it, as days (90d) or a duration (24h). It is capped at the issuing CA's expiry.
Re-run homepki crl before nextUpdate to re-sign the CRLs with a new date. Revocations are kept.
Revoked leaves in list output
server-cert list and client-cert list read the intermediate's CRL and report revoked leaves as invalid:
$ homepki client-cert list -d runlocal.dev -i bu1
Client Certificates for runlocal.dev/bu1:
NAME EXPIRES DAYS LEFT CHAIN
──────── ────────── ───────── ──────────
cli1.crt 2027-09-26 364 ✗ INVALID: revoked on 2026-09-26 (keyCompromise)
In JSON the row has "chain_valid": false and "chain_error": "revoked on 2026-09-26 (keyCompromise)". See Listing and verification.
Using the CRLs
OpenSSL
$ openssl verify -crl_check_all \
-CAfile ~/.homepki/runlocal-dev/bu1/bu1-intermediate-ca-chain.crt \
-CRLfile ~/.homepki/runlocal-dev/bu1/bu1-crl-chain.crl \
-purpose sslclient ~/.homepki/runlocal-dev/bu1/client-tls/cli1.crt
O=runlocal-dev, OU=bu1, CN=cli1.bu1.runlocal.dev
error 23 at 0 depth lookup: certificate revoked
error ~/.homepki/runlocal-dev/bu1/client-tls/cli1.crt: verification failed
-crl_check_all needs a CRL for every CA in the chain, which is why the chain file carries the root's CRL too.
nginx mTLS
ssl_client_certificate /etc/nginx/pki/bu1-intermediate-ca-chain.crt;
ssl_verify_client on;
ssl_verify_depth 2;
ssl_crl /etc/nginx/pki/bu1-crl-chain.crl;
nginx checks the CRL of every CA in the chain, so give it the chain file. It reads the CRL at start-up: reload nginx after each revoke or crl.
Limits
- No CRL distribution point. Certificates do not carry a URL to fetch the CRL from. Hand the file to the server directly.
- No OCSP.
- Intermediates cannot be revoked. The root CRL is always empty. Replace an intermediate with
intermediate-ca --forceinstead. - Deleting a CRL un-revokes everything in it. The next
revokeorcrlstarts from an empty list. intermediate-ca --forcedeletes that intermediate's CRLs, since the replaced key signed them.root-ca --forcedeletes the root CRL.