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).
This is a follow-up to Using TPM-backed Client Certificates in Chrome on Linux.
Sooner or later, your SSH key will open the door to a lot of servers, including sensitive ones. The default setup, a key file sitting in ~/.ssh/, means that anyone who can read that file can impersonate you. A compromised workstation, a careless backup, a stolen laptop: any of these can leak your private key without you ever noticing.
In the previous post, we set up TPM-backed client certificates for Chrome. The same principle applies to SSH: move the private key into the TPM, and it can never be extracted. The key can be used, but not copied.
This guide shows how to:
- Create an SSH key pair inside the TPM
- Export the public key
- Use the TPM-backed key for SSH login
Prerequisites
This guide assumes you already have a working TPM PKCS#11 setup from the previous post. Specifically:
tpm2-pkcs11tools are installed- Your user is in the
tssgroup - The
TPM2_PKCS11_STOREenvironment variable is set - A token has been initialized (we used the label
PKI)
If you haven’t done this yet, follow the initialization steps in that post first.
Find Your PKCS#11 Module
In the previous post, we used /usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so as the PKCS#11 module for the TPM.
You can verify which modules are available on your system using p11-kit:
p11-kit list-modules
If
p11-kitis not installed:sudo apt install p11-kit
This will output something like:
module: tpm2_pkcs11
path: /usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so
uri: pkcs11:library-description=TPM2.0%20Cryptoki;library-manufacturer=tpm2-software.github.io
library-description: TPM2.0 Cryptoki
library-manufacturer: tpm2-software.github.io
library-version: 1.9
token: PKI
uri: pkcs11:model=Intel;manufacturer=Intel;serial=0000000000000000;token=PKI
manufacturer: Intel
model: Intel
serial-number: 0000000000000000
hardware-version: 1.38
firmware-version: 147.1
flags:
rng
login-required
user-pin-initialized
token-initialized
The path: line tells you which shared object to use with SSH. We’ll need this later.
If you have a YubiKey or another PIV-capable token plugged in, you may see an additional module via OpenSC:
module: opensc-pkcs11
path: /usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so
uri: pkcs11:library-description=OpenSC%20smartcard%20framework;library-manufacturer=OpenSC%20Project
library-description: OpenSC smartcard framework
library-manufacturer: OpenSC Project
library-version: 0.26
token: Your Name
uri: pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=01de4dbe3fc0ff33;token=Your%20Name
manufacturer: piv_II
model: PKCS#15 emulated
serial-number: 9b57ad4279c01423
flags:
rng
login-required
user-pin-initialized
token-initialized
You can use any PKCS#11 module that exposes hardware-backed keys. Good practice is never to reuse a key for multiple purposes, so we’ll generate a new one specifically for SSH.
Create an SSH Key on the TPM
Add a new key to the existing PKI token. The key is distinguished by its key label (ssh), separate from the certificate key created in the previous post:
USER_PIN="<your user pin here>"
tpm2_ptool addkey \
--algorithm ecc256 \
--label PKI \
--key-label ssh \
--userpin "$USER_PIN"
This creates an ECDSA P-256 key pair inside the TPM. The private key never leaves the hardware.
We’re using P-256 here rather than the P-384 from the previous post. P-256 has broader support across SSH implementations and is the most widely deployed elliptic curve for SSH. P-384 would also work if your servers support it.
Export the Public Key
SSH needs the public half of your key. The ssh-keygen tool can extract it directly from a PKCS#11 module:
MODULE=/usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so
ssh-keygen -D "$MODULE"
This lists all keys available through the module. The output looks like:
ecdsa-sha2-nistp256 AAAAE2VjZH...EkHlCg= ssh
The label at the end (ssh) identifies the key you just created. If you have multiple keys on the token, look for the one matching your key label.
Copy that entire line into ~/.ssh/authorized_keys on every server you want to access with this key.
SSH is strict about file permissions. Make sure
~/.ssh/is mode700and~/.ssh/authorized_keysis mode600, both owned by your user. SSH will silently refuse to use the file otherwise.
Configure SSH to Use the TPM Key
Tell SSH to use the PKCS#11 module when connecting to your server. Add this to ~/.ssh/config:
Host your-server
PKCS11Provider /usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so
Replace your-server with the actual hostname or alias.
Now connect:
ssh your-server
SSH will prompt for your TPM user PIN - the same one you set during token initialization. After entering it, authentication proceeds using the hardware-backed key.
If you want to use the TPM key for all SSH connections, you can set the provider globally:
Host *
PKCS11Provider /usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so
Be aware that SSH will then attempt PKCS#11 authentication on every connection, even to hosts that don’t use this key. You can avoid unnecessary PIN prompts by being explicit about which hosts use the provider.
Final Thoughts
Between this post and the previous one, your TPM now holds keys for two different purposes: browser-based client authentication and SSH. The private keys for both are locked inside the hardware, immune to file-system-level theft.
This is a meaningful improvement over key files on disk. A compromised workstation can still use your keys while it is compromised, but a remote attacker cannot silently copy them for later use. An old backup cannot leak them. A stolen disk image is useless without the physical machine.
The trade-off is convenience. You need to enter a PIN. You cannot easily move the key to a different machine - which is exactly the point, but it also means you need a plan for key distribution across your workstations. One key per machine, registered on the servers that machine needs access to, is a clean model.
Hardware-backed keys don’t solve everything, but they eliminate an entire class of key theft scenarios. Combined with the habits from the previous post (lock your screen, keep your system patched, use strong PINs) they make your authentication stack considerably harder to compromise.