PKI Maintenance Runbook#
This is the operational runbook for the FalseBlue PKI. It documents the CLI procedures for the
operations that can be performed without XCA. XCA is still required for any operation that needs
the root key or a YubiKey-CA-resident key — the root key lives in the XCA database
(pki/db/FalseBlue-CA.xdb.sops) and on the YubiKeys, accessed via opensc-pkcs11. See
FalseBlue PKI for the XCA workflow.
PKI Hierarchy#
falseblue.com (Root CA)
├── expires 2032-04-10 — offline, ~6 years as of 2026
├── FalseBlue Intermediate CA (step-ca on ereshkigal, expires 2035-08-10)
│ └── issues all TLS/ACME/SSH certs for homelab infrastructure
├── YubiKey - Nano 5c (slot 9C on Nano, ECCP384, HIGH TRUST)
│ └── issues personal signing certs; keep this key at home/secured
└── YubiKey 5c NFC - Keychain - CA (slot 9C on NFC key, LOWER TRUST)
└── issues personal signing certs; kept on keychain, higher exposure risk
Important
The two YubiKey CAs are both signed directly by the root (flat hierarchy). Neither is an intermediate of the other. Do not re-introduce the old chained structure.
Hardware Reference#
| Key | Nickname | Trust Level | PIV Slot | Algorithm | Where |
|---|---|---|---|---|---|
| YubiKey Nano 5c | Nano | HIGH | 9C | ECCP384 | Home / safe |
| YubiKey 5c NFC | NFC | LOWER | 9C | check ykman piv info |
Keychain |
Root CA Facts#
- Certificate:
pki/authorities/falseblue.crt - Private key: held in the XCA database (
pki/db/FalseBlue-CA.xdb.sops) and on the YubiKeys; sops-decrypt the DB, open it in XCA, and access the key via opensc-pkcs11. The XCA DB passphrase is in Bitwarden:FalseBlue Root CA Key - Expires: 2032-04-10 — plan renewal by 2031 at the latest
- Fingerprint:
66b8732f7a4630403455da2ef05fef269ae63e390c6b5f50f82ecdf872585fe6
Note
The XCA database (pki/db/FalseBlue-CA.xdb.sops) holds the authoritative root key and is
the tool of record for root-key and YubiKey-CA-resident signing/CRL operations. See
Renewing a Certificate Manually using XCA
for that workflow.
A full sops-encrypted archive of the final XCA export (certs, CRLs, CSRs, public keys)
is in pki/db/recovery_files/ for reference.
Prerequisites#
All tools are available in nixpkgs. On ereshkigal they are already in PATH via environment.systemPackages.
Elsewhere, use:
nix shell nixpkgs#yubikey-manager nixpkgs#openssl nixpkgs#sops
step-cli is on ereshkigal’s PATH. For other hosts: nix run nixpkgs#step-cli -- <args>.
Renewal Schedule#
| Component | Expires | Next Action |
|---|---|---|
| Root CA | 2032-04-10 | Begin planning 2031-04 |
| step-ca intermediate | 2035-08-10 | Begin planning 2034-08 |
| YubiKey Nano 5c CA | 2031-06-05 | Begin planning 2030-06 |
| YubiKey NFC 5c CA | 2031-06-05 | Begin planning 2030-06 |
YubiKey CA certs should be issued for 5–10 years per rotation. The root CA lifetime sets the ceiling — no cert it signs can outlive it, so target 2031 or earlier.
Renewing a YubiKey CA Cert#
Repeat this procedure for each physical YubiKey. The private key never leaves the hardware; you export the public key, generate a signed CSR, sign it with the root, and import the new cert back.
Step 1 — Verify the slot#
ykman piv info
Confirm slot 9C has a key (Private key type listed) and note the algorithm. The Nano is ECCP384.
Step 2 — Export the public key from the YubiKey#
ykman piv keys export 9c /tmp/yk-pub.pem
If there is no cert in the slot yet, add --verify --pin <PIN> to confirm the key is present.
Step 3 — Generate a CSR (signed by the on-device private key)#
# For the Nano
ykman piv certificates request \
--subject "CN=YubiKey - Nano 5c,O=FalseBlue" \
9c /tmp/yk-pub.pem /tmp/yk.csr
# For the NFC key
ykman piv certificates request \
--subject "CN=YubiKey - 5cNFC,O=FalseBlue" \
9c /tmp/yk-pub.pem /tmp/yk.csr
The YubiKey will prompt for the PIN. The CSR is self-signed by the slot key to prove possession.
Step 4 — Open the XCA database (holds the root key)#
The root key is not available as a standalone PEM — it lives in the XCA database and on the YubiKeys, reached via opensc-pkcs11. Sops-decrypt the DB and open it in XCA:
sops -d pki/db/FalseBlue-CA.xdb.sops > /tmp/FalseBlue-CA.xdb
nix run nixpkgs#xca -- /tmp/FalseBlue-CA.xdb
# shred -u /tmp/FalseBlue-CA.xdb when finished
Plug in the root YubiKey and confirm the opensc library path in XCA settings
(/run/current-system/sw/lib/opensc-pkcs11.so on NixOS).
Step 5 — Sign the CSR with the root CA in XCA#
Import /tmp/yk.csr into the CSRs tab, right-click it and choose Sign, set
Sign with to the root CA (falseblue.com), and apply a CA extension with
basicConstraints = critical, CA:TRUE, pathlen:0 and
keyUsage = critical, keyCertSign, cRLSign, 3650-day validity. Then export the signed cert
from the Certificates tab to pki/authorities/nanoc5c-CA.crt (or nfc5c-CA.crt for the
NFC key). See Renewing a Certificate Manually using XCA.
Step 6 — Verify the new cert#
openssl verify -CAfile pki/authorities/falseblue.crt pki/authorities/nanoc5c-CA.crt
openssl x509 -in pki/authorities/nanoc5c-CA.crt -noout -subject -issuer -dates
Step 7 — Import the new cert back onto the YubiKey#
ykman piv certificates import 9c pki/authorities/nanoc5c-CA.crt
Step 8 — Update CRLs and publish#
Update the CRL for each renewed CA (see Updating CRLs). Then commit the new certs:
git add pki/authorities/nanoc5c-CA.crt pki/authorities/nfc5c-CA.crt
git commit -m "pki: renew YubiKey CA certs"
Rebuild to push the updated certs into system trust stores:
nixos-rebuild switch # on each NixOS host
darwin-rebuild --impure --flake .#<host> switch # on each macOS host (see scripts/darwin-flake-rebuild.sh)
just Recipe#
A renew-piv-ca recipe is planned for the justfile but is not yet present. Once added, usage will be:
just renew-piv-ca 9c "CN=YubiKey - Nano 5c,O=FalseBlue" pki/authorities/nanoc5c-CA.crt
just renew-piv-ca 9c "CN=YubiKey - 5cNFC,O=FalseBlue" pki/authorities/nfc5c-CA.crt
Until then, follow the manual steps above. The NFC YubiKey must be plugged in when renewing its cert.
Renewing the step-ca Intermediate#
Only needed ~2034. The intermediate lives in sops secrets on ereshkigal.
Because the root key is held in the XCA database / on the YubiKey (no standalone PEM exists),
generate the new intermediate key + CSR locally with step, then sign the CSR with the root in
XCA (see Renewing a Certificate Manually using XCA):
# Generate a new intermediate key and CSR
step certificate create \
"FalseBlue Intermediate CA" \
/tmp/intermediate.csr /tmp/intermediate.key \
--profile intermediate-ca \
--csr \
--not-after 87600h \
--kty EC --curve P-256
# Sign /tmp/intermediate.csr with the root CA in XCA, exporting /tmp/intermediate.crt.
# Encrypt and store the new key/cert
sops -e /tmp/intermediate.key > <new-sops-secret>
# Update sops.secrets in step-ca.nix to point to the new secret paths
# Rebuild ereshkigal
Updating CRLs After Renewal#
CRLs are published at https://falseblue.com/*.crl. After any renewal or revocation:
Root CA CRL#
The root key is held in the XCA database, so the root CRL is regenerated in XCA (the same flow as the YubiKey CA CRLs below):
sops -d pki/db/FalseBlue-CA.xdb.sops > /tmp/FalseBlue-CA.xdb
nix run nixpkgs#xca -- /tmp/FalseBlue-CA.xdb
# Certificates tab → right-click the root CA (falseblue.com) → Update CRL
# CRLs tab → Export → pki/authorities/falseblue.crl
shred -u /tmp/FalseBlue-CA.xdb
Verify the exported CRL:
openssl crl -in pki/authorities/falseblue.crl -noout -lastupdate -nextupdate
YubiKey CA CRLs#
Signing these CRLs requires the on-device private key via PKCS#11. Do this in XCA, which reaches the YubiKey CA keys through opensc-pkcs11:
sops -d pki/db/FalseBlue-CA.xdb.sops > /tmp/FalseBlue-CA.xdb
nix run nixpkgs#xca -- /tmp/FalseBlue-CA.xdb
# Certificates tab → right-click CA → Update CRL
# CRLs tab → Export → pki/authorities/nanoc5c-CA.crl and nfc5c-CA.crl
shred -u /tmp/FalseBlue-CA.xdb
Publish#
Copy updated .crl and .crt files to the static/ directory in the
personal-web repo and deploy.
Slot Reference (Verified 2026-06-06)#
YubiKey 5C Nano — Serial 23183421 (HIGH TRUST, keep at home)#
| Slot | Subject | Expires | Notes |
|---|---|---|---|
| 9A | CN=Yubico PIV Authentication | 2054-01-20 | Default auth cert |
| 9C | CN=YubiKey - Nano 5c,O=FalseBlue | 2031-06-05 | Signing CA ✅ renewed |
| 9D | CN=Yubico PIV Authentication | 2054-01-20 | Default key management cert |
| 9E | CN=benjamin.craton@gmail.com |
2025-01-18 (expired) | Self-signed — see issue #179 |
| 95 | CN=tsunami-test | 2025-02-02 (expired) | Stale test cert — see issue #179 |
YubiKey 5C NFC — Serial 24918246 (LOWER TRUST, keychain)#
| Slot | Subject | Expires | Notes |
|---|---|---|---|
| 9A | CN=Yubico PIV Authentication | 2054-03-12 | Default auth cert |
| 9C | CN=YubiKey - 5cNFC,O=FalseBlue | 2031-06-05 | Signing CA ✅ renewed |
| 9D | CN=Yubico PIV Authentication | 2054-03-12 | Default key management cert |
Slot 9E (Nano) can be renewed with a self-signed cert — no root key needed:
ykman piv keys export 9e /tmp/9e-pub.pem
ykman piv certificates generate --subject "CN=benjamin.craton@gmail.com" --valid-days 3650 9e /tmp/9e-pub.pem
If the Root Key is Lost#
The root key lives in the XCA database (pki/db/FalseBlue-CA.xdb.sops); the Bitwarden entry
FalseBlue Root CA Key is the passphrase that unlocks that database.
If the Bitwarden passphrase and the XCA database are both gone:
- Generate a new root CA with
step certificate create --profile root-ca - Re-sign the step-ca intermediate and both YubiKey CAs from the new root
- Distribute the new root cert across all hosts via
pki/authorities/falseblue.crt+ rebuild - Update the fingerprint in
pki.mdand the step-ca bootstrap command - Re-issue all leaf certs via ACME (they will auto-renew from step-ca once the new intermediate is in place)
Equipment using the old root (iDRAC, Synology, etc.) will need the new root cert installed manually.