๐๏ธ Air-gapped Root CA
The Root CA is the one key everything else depends on. If it leaks, every certificate below it is suspect and the whole hierarchy has to be rebuilt. So the usual practice is to keep the Root offline: it signs a few subordinate CAs, then it is switched off and put away. It comes back only to sign a new subordinate, re-certify one, or publish a fresh revocation list. Day-to-day issuing happens on the subordinates.
CryptOS is built for this. A Root node only signs CAs unless you tell it otherwise, and a two-tier hierarchy (an offline-style Root with an Intermediate below it) has been run on VMware with vTPMs, including a vCenter VMCA chained underneath.
- A node in the root role runs the first-boot ceremony, creates its key and self-signs its certificate.
- A node in the intermediate or issuing role gets its CA certificate from the Root by CSR:
ca get-subordinate-csron the child,ca sign-subordinateon the Root,ca submit-subordinate-certback on the child. - A Root refuses to issue leaf certificates unless its config sets
root_leaf_issuance: acknowledged-irreversible. - A subordinate can be re-certified on the same key later (
ca get-renewal-csr,ca sign-subordinate,ca submit-renewed-cert), so nothing below it has to re-pin. - Between signings the Root can be shut down cleanly with
cryptosctl reboot --confirm "<Root CA CN>" --power-off.
CryptOS has no locked-down mode for a Root. While it runs, a Root serves its management API like any other node, there is no hardware presence check before its key unseals, and one admin certificate authorizes every operation. "Offline" means the Root node is powered off, or cut off from the network, between signings.
Choosing where the Root key livesโ
The node's state-key mode (state_key.mode in the machine config, or the default the image was built with) decides where the CA key is kept. Choose it before the ceremony, because the key is created where the mode says. There are two very different options today.
tpm mode | nodeid or kms mode | |
|---|---|---|
| Where the key lives | Inside the TPM (a vTPM on a VM) | In software, on the LUKS-encrypted state partition |
| Key algorithm | ECDSA P-384, or RSA-3072 or RSA-4096 if the TPM implements that size | ECDSA P-384, RSA-3072 or RSA-4096 |
| Can it be backed up? | No. ca export-key is refused | Yes, with ca export-key to a passphrase-sealed file |
| Protected by hardware? | Yes | No |
If something you must chain to only accepts RSA, the whole chain above it has to be RSA, the Root included. A TPM can hold an RSA Root key, but only at a size it implements.
The TPM 2.0 spec only requires RSA-2048, and many TPMs and vTPMs stop there, below the RSA-3072 floor. On such a TPM the ceremony stops with FailedPrecondition and an error containing tpm: key algorithm not supported by this TPM: RSA-3072, and no key is created. It never falls back to a smaller size or a software key. Check the TPM's datasheet or your hypervisor's vTPM, run the ceremony again with an algorithm it has, or use a software mode.
In tpm mode the Root key never leaves the TPM, by design. cryptosctl ca export-key answers this node's CA key is non-exportable (TPM-backed). If that TPM or vTPM is lost, for example when the VM is deleted or its encryption keys are gone, the Root is gone. Every subordinate then has to be re-issued under a new Root, and every client has to trust the new one. Plan for that before the ceremony. Keep the VM, its vTPM and the host keys that protect it backed up by your platform.
In nodeid or kms mode, cryptosctl ca export-key --out <file> writes the CA key and chain to a file sealed with a passphrase you type. On a Root it asks you to type the Root CA's common name first. Anyone with that file and the passphrase holds your Root. Store the file offline, the passphrase in a privileged access management (PAM) solution apart from it, and never on the machine you run cryptosctl from. ca import-key --backup <file> restores it onto a fresh node.
The shape of a two-tier hierarchyโ
-
Root. Its machine config sets
role.kind: root, the key algorithm (pki.root_key_alg), the subject (pki.root_subject) and the lifetime (pki.root_validity_years, 1 to 30). It also holds a CA profile for signing subordinates:basic_constraints.is_ca: true, apath_len, andkey_usage: [digital_signature, cert_sign, crl_sign]. See The first-boot ceremony for how the Root key is made. -
Intermediate or issuing CA. Its machine config sets
role.kind: intermediateorissuing, and pins the Root it expects inpki.parent, as the Root's certificate (ca_cert_pem) or its SHA-256 (ca_cert_sha256). After its own ceremony it waits for a signed certificate. -
Signing. Bring the Root online, fetch the child's CSR, sign it, and hand the chain back:
cryptosctl --endpoint pki-intermediate.example.org:443 ca get-subordinate-csr > intermediate.csrcryptosctl --endpoint pki-root.example.org:443 ca sign-subordinate \--csr intermediate.csr --profile intermediate-ca > intermediate-chain.pemcryptosctl --endpoint pki-intermediate.example.org:443 ca submit-subordinate-cert \--chain intermediate-chain.pemThe child checks the chain against the Root it pinned before it accepts it.
cryptosctlruns on Linux and macOS. -
Offline. Power the Root off until it is needed again.
A CA profile's path_len is clamped to what the signing CA's own certificate leaves (a Root has no limit of its own), and a subordinate's certificate never outlives the Root: it is capped at the Root's notAfter, or refused if the profile sets validity_policy: reject.
Revocation with an offline Rootโ
Relying parties that check revocation will look up the subordinate CA certificates too, and those point at the Root's CRL and OCSP when the Root has pki.revocation_base_url set.
The Root's /crl, /ocsp and /ca.cer are served by the Root node itself, and only while it runs. The CRL it signs is valid for crl_next_update_hours (168 hours, one week, by default). CryptOS has no built-in way to publish the Root's CRL from somewhere else while the Root is off. Before you rely on an offline Root, decide how clients will reach a current CRL for your subordinates, and bring the Root up often enough to renew it.
With revocation_base_url set, the Root refuses to sign while its revocation check fails, with revocation preflight failing for configured revocation_base_url; issuance blocked. When you bring the Root up to sign, make sure the base URL resolves to it and its /crl, /ocsp and /ca.cer answer there. allow_unverified_revocation_url skips the check, but on a Root every subordinate signed while it is set carries the unchecked pointer permanently.
Relatedโ
- CA roles explains root, intermediate and issuing nodes.
- TPM-sealed keys explains what the TPM protects.
- Re-certifying a subordinate walks through a renewal on the same key.
- vCenter VMCA as a CryptOS subordinate is a worked example of a third tier below the Intermediate.