Hardware-Rooted Identity, Honestly: HSM, TPM, and Attestation
A CA Key That Never Becomes a File
Labs 2 and 3 left one thing on faith: the keys themselves. A subject binding is meaningless if the private key is a file anyone can copy, and short-lived renewal is only safe if the CA can refuse to renew for a thief. The endgame is to put the two most important keys in hardware that can use a key but never export it, and to gate signing on proof that the requester is genuinely the right machine. The design philosophy in one line: root trust in the smallest, hardest-to-copy thing, and make everything above it ephemeral.
Read this before you start. This playground has no real HSM or TPM, so we use SoftHSM2 and swtpm, software emulators. They reproduce the interfaces and protocols faithfully (PKCS#11 object semantics, the TPM credential-activation handshake), which is what this lab teaches. They do not reproduce the hardware guarantee: a software key can still be copied off disk, and there is no manufacturer key certificate to anchor real attestation. Treat what follows as "how the protocol works," not "this proves non-exportability." Every place that distinction matters is flagged.
Two keys, two hardware roots
There are two private keys in this model, living in two different roots, and keeping them separate is the whole basis of the trust graph:
- The CA's signing key lives in an HSM. There is exactly one, it signs everyone's certs, and its public face (the CA cert) is distributed to every verifier. It must be usable but never copyable.
- Each workload's key lives in that workload's own TPM. There are many, one per host. A workload's key never signs anyone else's cert; it only holds and proves that workload's own identity.
Step 1: A CA key that never becomes a file (HSM)
Install SoftHSM2, the PKCS#11 tooling, and the OpenSSL PKCS#11 engine:
sudo apt-get update && sudo apt-get install -y softhsm2 opensc libengine-pkcs11-openssl
mkdir -p ~/hsm/tokens && cd ~/hsm
echo "directories.tokendir = $HOME/hsm/tokens" > softhsm2.conf
export SOFTHSM2_CONF=$HOME/hsm/softhsm2.conf
export PKCS11_MODULE_PATH=/usr/lib/softhsm/libsofthsm2.so
Initialize a token and generate the CA keypair inside it (the private half is born in the token and never leaves):
softhsm2-util --init-token --free --label caHSM --pin 1234 --so-pin 5678
pkcs11-tool --module "$PKCS11_MODULE_PATH" -l --pin 1234 \
--keypairgen --key-type EC:prime256v1 --label caKey --id 01
Check it. Ask the token to hand you the private key. It must refuse, and the key's attributes must say so:
pkcs11-tool --module "$PKCS11_MODULE_PATH" -l --pin 1234 --list-objects | grep -A3 'Private Key'
You should see sensitive, always sensitive, never extractable. That is the property the whole model rests on: the key can sign, but it cannot be copied out. (On real hardware this is enforced by the chip. SoftHSM2 enforces it at the library boundary, which demonstrates the semantics but not the physical guarantee.)
Now build a CA certificate whose key is that token object, and sign a leaf with it, without the CA key ever existing as a file:
KEYURI="pkcs11:token=caHSM;object=caKey;type=private;pin-value=1234"
openssl req -new -x509 -engine pkcs11 -keyform engine -key "$KEYURI" \
-subj "/CN=HSM Root CA" -days 3650 -sha256 -out ca-hsm.pem
openssl req -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
-keyout app-key.pem -out app.csr -subj "/CN=app.internal"
openssl x509 -req -engine pkcs11 -CAkeyform engine -CAkey "$KEYURI" \
-CA ca-hsm.pem -in app.csr -CAcreateserial -days 2 -out app-hsm.pem
Check it. The leaf chains to a CA whose key is nowhere on disk:
openssl verify -CAfile ca-hsm.pem app-hsm.pem # expect: app-hsm.pem: OK
ls ca-hsm-key.pem 2>/dev/null && echo "BAD: a CA key file exists" || echo "OK: no CA key file exists"
Proving a Key Lives in a Chip
Step 2: Prove a key lives in a specific chip (TPM attestation)
Enrollment should not sign a CSR just because it arrived. It should first prove the requester is genuinely the expected machine. TPMs do this with credential activation: the CA seals a secret so that only the holder of a specific TPM's endorsement key can open it.
Install the TPM tooling and start a software TPM:
sudo apt-get install -y tpm2-tools swtpm
mkdir -p ~/tpm && cd ~/tpm
swtpm socket --tpm2 --tpmstate dir=$HOME/tpm \
--ctrl type=tcp,port=2322 --server type=tcp,port=2321 --flags not-need-init --daemon
export TPM2TOOLS_TCTI="swtpm:host=127.0.0.1,port=2321"
tpm2_startup -c
Create the endorsement key (the chip's built-in identity) and an attestation key bound to it, persisting both so the limited TPM memory does not fill up:
tpm2_createek -c 0x81010001 -G ecc -u ek.pub
tpm2_createak -C 0x81010001 -c ak.ctx -G ecc -u ak.pub -n ak.name
tpm2_evictcontrol -c ak.ctx 0x81010002
tpm2_flushcontext -t && tpm2_flushcontext -l
CA side: seal a random secret so only this TPM can open it (bound to the EK's public key and the AK's name):
NAME=$(od -An -v -tx1 ak.name | tr -d ' \n')
head -c 16 /dev/urandom > secret.bin
tpm2_makecredential -T none -e ek.pub -s secret.bin -n "$NAME" -o cred.out
Workload side: open the challenge. This only succeeds inside the TPM that owns the endorsement key:
tpm2_startauthsession --policy-session -S session.ctx
tpm2_policysecret -S session.ctx -c endorsement
tpm2_activatecredential -c 0x81010002 -C 0x81010001 -i cred.out -o recovered.bin -P "session:session.ctx"
tpm2_flushcontext session.ctx
Check it. The recovered secret must equal the original. That equality is the proof the CA wanted before signing:
cmp -s secret.bin recovered.bin && echo "ATTESTATION OK: this AK lives in the same TPM as the EK" || echo "FAILED"
What this proves and what it does not. Passing activation proves the AK and EK live in the same TPM, that is real and the handshake above is exactly what production attestation runs. What it cannot prove here: that the TPM is genuine hardware. Real attestation also checks the EK against a manufacturer-issued EK certificate chaining to the vendor's CA, which is what makes "this is a real chip, not an emulator" trustworthy. swtpm has no such certificate, and its state is just files you could copy. So this lab demonstrates the protocol end to end, not the hardware root of trust.
Step 3: Why this makes renewal safe
Connect this back to Lab 3. Renewal was the engine, but only if the CA can refuse a thief. Now you can see how: the CA re-runs credential activation on every renewal, demanding fresh proof that the requester is still genuinely the right chip before signing again.
- A short-lived cert copied off a box expires in minutes (Lab 3).
- The thief cannot renew it: from a different machine, activation fails because that machine's TPM holds a different endorsement key, so the sealed secret never opens.
- The CA signing key the thief would need to forge their own cert is in the HSM, non-exportable (Step 1).
Theft is therefore time-boxed to the duration of physical control of the original host, and revocation becomes nearly vestigial. That is the entire design closing on itself: the trust bottoms out at two hardened facts, the HSM and each TPM, and everything above them is ephemeral and re-derivable.
Recap
You moved the two keys that matter most into roots that resist copying, and gated signing on proof of identity:
- The CA key was generated inside a PKCS#11 token, marked never-extractable, and used to sign certs it never exported as a file.
- The TPM credential-activation handshake proved an attestation key lives in a specific chip, the check a CA should run before issuing or renewing.
- Together with short-lived certs, this time-boxes theft and concentrates all trust in two hardened facts.
Keep the honesty caveat in your pocket: software emulators teach these protocols faithfully but cannot stand in for the hardware guarantee. To make any of this real you add the one thing the playground cannot give you, a manufacturer EK certificate and a genuine chip behind it.
- Previous lesson
- Short-Lived Certificates: Renewal Is the Engine, Not the Tax