Private keys don’t belong in files on disk.
This is a post for linux admins or security engineers. Tested on Ubuntu 25.10 (Questing Quokka).
Storing private keys in a Trusted Platform Module (TPM) has one simple advantage: the key never leaves the hardware. The operating system can ask the TPM to perform cryptographic operations, but it cannot extract the key itself.
For client authentication this is useful. If a certificate’s private key lives inside the TPM, key theft becomes significantly harder compared to storing the key in a file on disk.
This guide shows how to:
- Initialize a TPM-backed PKCS#11 store
- Generate an RSA key inside the TPM
- Create a CSR from that key
- Import the signed certificate
- Use the certificate in Chrome on Linux
Store a certificate on TPM
This setup uses tpm2-pkcs11, which exposes TPM keys through the PKCS#11 interface that many applications, including Chrome, already understand.
Initialize the TPM token store
This only needs to be done once.
Install the required packages:
USER_PIN="<your user pin here>"
SO_PIN="<your so pin here>"
sudo apt install libtpm2-pkcs11-tools libtpm2-pkcs11-1 tpm2-tools libengine-pkcs11-openssl opensc
Add your user to the tss group so it can access the TPM:
sudo usermod -aG tss $USER # You need to logout/in for this to take effect
Log out and back in afterwards so the group change takes effect.
Define the token store location:
TPM2_PKCS11_STORE=$HOME/.tpm2-pkcs11
echo "export TPM2_PKCS11_STORE=$TPM2_PKCS11_STORE" >> ~/.profile
echo "export TPM2_PKCS11_STORE=$TPM2_PKCS11_STORE" >> ~/.bashrc
Initialize the store and create a token:
MODULE=/usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so
tpm2_ptool init
tpm2_ptool addtoken --pid 1 --label PKI --userpin "$USER_PIN" --sopin "$SO_PIN"
Next, register the TPM PKCS#11 module with the NSS database used by Chrome and Chromium:
modutil -dbdir "sql:$HOME/.pki/nssdb" -add "TPM2 via tpm2-pkcs11" -libfile $MODULE
modutil -dbdir "sql:$HOME/.pki/nssdb" -list
At this point Chrome is capable of seeing TPM-backed keys.
Create RSA key pair inside the TPM
Create RSA key pair:
USER_PIN="<your user pin here>"
KEY_LABEL="PKI-$(date +%y%m%d)"
tpm2_ptool addkey \
--algorithm rsa4096 \
--label PKI \
--key-label "$KEY_LABEL" \
--userpin "$USER_PIN"
Verify that the key exists, from the TPM tool or through the PKCS#11 interface:
tpm2_ptool listobjects --label PKI
MODULE=/usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so
pkcs11-tool \
--module $MODULE \
--list-objects \
--type privkey \
--login --pin "$USER_PIN"
Create a certificate signing request
The CSR is generated using OpenSSL, but the private key operation is delegated to the TPM through PKCS#11.
MY_NAME="Firstname Lastname"
KEY_URI="pkcs11:object=$KEY_LABEL;type=private"
openssl req \
-new \
-engine pkcs11 \
-keyform engine \
-key "$KEY_URI" \
-out cert.csr \
-subj "/CN=$MY_NAME/OU=$(hostname) TPM"
OpenSSL will prompt for your TPM user PIN.
This also puts your computer’s hostname into the Organizational Unit field of the certificate. This makes it easier to distinguish certificates later, should you need to issue multiple to the same user (e.g. for a second workstation or a YubiKey).
Submit cert.csr to your internal PKI or certificate authority and obtain a signed certificate.
Note on OpenSSL 3.x: the -keyform engine flag is deprecated. The preferred approach is the tpm2-openssl provider with openssl -provider tpm2. However, that provider expects a TPM handle or TSS2 key file instead of a PKCS#11 URI, so the workflow looks slightly different if you use it. I’m sticking with the legacy approach mainly for compatibility reasons.
Import the signed certificate
Once your PKI returns the signed certificate:
tpm2_ptool addcert --label PKI --key-label "$KEY_LABEL" cert.pem
The certificate is now associated with the TPM.
Make sure that you specify --key-label and do not use --key-id here, otherwise you will need to add a label manually. Chrome will not work with certificates that have no label set.
For Chrome to trust the certificate chain, install all intermediate CAs up to the root CA into the browser’s certificate store.
Open chrome://settings/certificates and import the required CA certificates there.
Restart Chrome. The TPM-backed certificate should now appear under Your Certificates.
Alternative: Using a YubiKey
If you prefer a removable hardware token, a YubiKey can provide similar functionality through its PIV smart card interface.
The workflow is simpler because the device already exposes a standard smart card interface.
Install dependencies
sudo apt install yubikey-manager opensc pcscd
sudo systemctl enable --now pcscd
Generate key and CSR
You can use the YubiKey Manager GUI, which is usually the easiest approach.
Using the CLI:
ykman piv keys generate -a RSA4096 9a pubkey.pem
Create a CSR:
MY_NAME="Firstname Lastname"
ykman piv certificates request \
-s "CN=$MY_NAME,OU=YubiKey" \
9a \
pubkey.pem \
cert.csr
Submit the CSR to your PKI as before.
Import signed certificate
ykman piv certificates import 9a cert.pem
Chrome integration
Chrome on Linux typically discovers YubiKey certificates automatically via pcscd.
If not, manually register the OpenSC PKCS#11 module:
modutil -dbdir "sql:$HOME/.pki/nssdb" \
-add "OpenSC" \
-libfile /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
As with TPM-backed certificates, make sure the full CA chain is installed in the browser’s certificate store:
chrome://settings/certificates
Once the intermediates are present, Chrome can use the hardware-backed certificate for client authentication.
Final Thoughts
Certificate-based authentication is the strongest form of authentication available on the web, but it is rarely used because certificates are harder to deploy. As you’ve seen, it is not as straightforward as a password, but some applications genuinely need the added security - a PKI management interface, for example.
Storing the private key in hardware makes it impossible to extract from the TPM or the hardware token. You no longer have to worry about backups containing a key file, or a remote attacker being able to copy the private key.
That said, a remote attacker can still use the key if they manage to compromise your system. Hardware-backed keys do not help much if your workstation is left unlocked.
With a YubiKey, you might even make it easier for an attacker: why break into your system when they can just grab your token from your desk? Without PIN authentication, or a PIN that begins with 123 and ends with 456, a hardware token may actually be weaker. A stolen laptop gets noticed immediately, a missing USB token might take a while.
Hardware key storage is a strong measure, but it is not a substitute for good operational discipline. Lock your screen, remove the token, keep your system patched. Strong authentication works best when backed by strong habits.