Storing SSH Certificates on a FIDO security key

SSH Certificates

Although using public key authentication to access SSH servers is more secure than using passwords, and storing SSH private keys on FIDO security keys is more secure than storing them in files, a key management issue can arise when dealing with many users and many servers. To make public key authentication scale better in this situation, one can use SSH certificates. An SSH certificate is somewhat similar to an X.509 certificate, but much simpler: a raw SSH signing key called the CA key is used to sign user’s SSH public keys. Instead of provisioning all user public keys to all servers, servers are simply configured with the CA public verification key, and anyone with a certificate signed by the corresponding CA signing key can use that to logon to the server (provided they have an account there).

Note that SSH certificates were introduced with OpenSSH version 5.4 and should be available in all current OpenSSH distributions.

For more information on what is possible with SSH certificates, see the ssh-keygen manual

Also note that not all vendors of SSH software will implement SSH certificates, so check their manuals to see if SSH certificates are supported.

Large blobs

When SSH keys are stored on a FIDO security key (as a FIDO discoverable credential, also known as a resident key), we can retrieve the SSH public key and the SSH key file containing the credential ID of the corresponding private key from the FIDO security key. This makes it easy to move to another SSH client without copying key files around. See Securing SSH with FIDO2. An SSH certificate is stored in a separate file however, so you may want to store the certificate file on a FIDO security key as well. Fortunately, there exists a FIDO feature for storing arbitrary data on a FIDO security key, called largeBlobs. We will show below how to use SSH certificates and store them on FIDO security keys leveraging largeBlobs.

Note however that largeBlobs are a relatively new addition to the FIDO CTAP standard, and will only be available on FIDO security keys with recent firmware. When using a YubiKey or a Security Key by Yubico make sure you have firmware 5.5.1 or later.

To check if your FIDO security key supports largeBlobs, you can use the libfido tools (version 1.7 or later), in particular fido2-token as follows.

Insert your FIDO security key, and list all connected authenticators:

fido2-token -L
/dev/hidraw0: vendor=0x1050, product=0x0407 (Yubico Yubikey OTP+FIDO+CCID)

The /dev/hidraw0 device file is specific to linux systems, and device files are typically numbered starting from 0.

On macos systems, the output shows something like this:

fido2-token -L
ioreg://1234567890: vendor=0x1050, product=0x0407 (Yubico YubiKey OTP+FIDO+CCID)

In the rest of this document, we’ll assume the device ID is /dev/hidraw0.

Next, check for largeBlob support by inspecting supported options:

fido2-token -I /dev/hidraw0 | grep options
options: rk, up, noplat, noalwaysUv, credMgmt, authnrCfg, clientPin, largeBlobs, pinUvAuthToken, setMinPINLength, makeCredUvNotRqd, credentialMgmtPreview

Note that largeBlobs is listed as an option.

Creating the CA

As SSH certificates need to be signed using a CA signing key, we need to generate this CA signing key first:

ssh-keygen -t ecdsa -f id_userca -N '' -C
Generating public/private ecdsa key pair.
Your identification has been saved in id_userca
Your public key has been saved in
The key fingerprint is:
The key's randomart image is:
+---[ECDSA 256]---+
|   +=+o.o        |
|. ++.oo. .       |
|o=oooo+          |
|o++..= =  E      |
|=+    @ S  .     |
|ooo  + =o o      |
| +. o ...=       |
|o  o    o..      |
| ..    .....     |

Here, the CA signing key is stored in a file but, depending on the use-case, it may make sense to generate and store the key in an Hardware Security Module (HSM), such as the YubiHSM

Generating user keys on a FIDO security key

If you don’t have a user SSH public key to sign yet, you first need to generate an SSH key pair on a FIDO security key. This is already discussed in Securing SSH with FIDO2, but for completeness we will repeat instructions here:

ssh-keygen -t ecdsa-sk -f ./id_ecdsa -N '' -O resident -O application=ssh:demo -O user=johndoe -C
Generating public/private ecdsa-sk key pair.
You may need to touch your authenticator to authorize key generation.
Enter PIN for authenticator: ********
You may need to touch your authenticator again to authorize key generation.
Your identification has been saved in ./id_ecdsa
Your public key has been saved in ./
The key fingerprint is:
The key's randomart image is:
+-[ECDSA-SK 256]--+
|^X=.             |
|#&+..            |
|@O== ..          |
|*=+.o .o         |
|+.o.  ..S        |
|.o .o..          |
|.   .+           |
|E.  .            |
|.                |

Note that we are generating an ECDSA key pair stored on a FIDO security key here (a discoverable credential, also called a "resident key", of type ecdsa-sk). The resulting public key is stored in the file ./ This public key now needs to be signed by the CA.

Signing the user certificate

Once the CA receives the user’s public key, verified the user’s identity, and determined the user’s eligibility for obtaining a certificate (exactly how all this is done is up to the CA to decide), the user’s public key can be signed using the CA signing key:

ssh-keygen -s ./id_userca -I -V +52w -n johndoe
Signed user key id "" serial 0 for johndoe valid from 2024-04-03T18:39:00 to 2025-04-02T18:40:47

Note that the certificate can have a limited validity (52 weeks in this example). Other restrictions are also possible. See the OpenSSH documentation for all possible options.

The resulting SSH certificate is stored in the file, and is returned to the user.

To view the SSH certificate contents:

ssh-keygen -L -f ./
        Type: user certificate
        Public key: ECDSA-SK-CERT SHA256:sAohY3k592oMYCwGfBJJEIIoxiZIzIPuWJHCZBapti4
        Signing CA: ECDSA SHA256:yA0+BNSmSO4arV8CkyxxWW6Bi3BdVKM7w1re+0NqYRc (using ecdsa-sha2-nistp256)
        Key ID: ""
        Serial: 0
        Valid: from 2024-04-03T18:39:00 to 2025-04-02T18:40:47
        Critical Options: (none)

Store an ssh certificate on a FIDO security token

Once the user has received their SSH certificate from the CA, the certificate can be stored as a "large blob" on the FIDO security key using the fido2-token command-line tool:

fido2-token -S -b -n ssh:demo /dev/hidraw0
Enter PIN for /dev/hidraw0: ********

To verify that your SSH certificate is stored, again use fido2-token to list all stored large blobs:

fido2-token -L -b /dev/hidraw0
Enter PIN for /dev/hidraw0: ********
total map size: 1219 bytes
00:  591  893 <unknown> <unknown>
01:  581  889 dhx715WNlF36vMvo5hV0SBOPfWqS1ncj2P2BgW2513a/rLvg64Tl4f0/uDrs1LsE ssh:demo

Retrieving key files and certificates from a FIDO security key

When logging in on a server, you will need your FIDO security key, your key file (containing a reference to the key stored on your security key), and your SSH certificate. When using a new local system to sign in from, you can choose to copy these files to the new system, but more conveniently, you can regenerate them from your security key.

To retrieve the SSH key file (the "key handle") from your FIDO security key, use ssh-keygen:

ssh-keygen -K
Enter PIN for authenticator: ********
You may need to touch your authenticator to authorize key download.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Saved ECDSA-SK key ssh:demo to id_ecdsa_sk_rk_demo_johndoe

Here, your key handle is saved to the file id_ecdsa_sk_rk_demo_johndoe. Note that the name of this file is generated on the key type (id_ecdsa_sk), the application ID (demo), and the username (johndoe) used when generating the FIDO credential.

Next, retrieve the SSH certificate stored as a large blob on token:

fido2-token -G -b -n ssh:demo /dev/hidraw0
Enter PIN for /dev/hidraw0: ********

Here, we specify as the name of the certificate, to match the key file in the previous step.

Signing in to a server

Now that we have our SSH certificate, we should be able to sign in on any server that uses the CA public key to verify users. When using OpenSSH, the CA signing key can be configured globally in the file /etc/ssh/sshd_config by including the directive:

TrustedUserCAKeys /etc/ssh/

For more information on configuring OpenSSH servers, see the sshd_config manual

Assuming the user account johndoe exists,

test signing in using ssh certificate

ssh -i ././id_ecdsa_sk_rk_demo_johndoe -l johndoe
Confirm user presence for key ECDSA-SK SHA256:sAohY3k592oMYCwGfBJJEIIoxiZIzIPuWJHCZBapti4
User presence confirmed
[]$ whoami
[]$ exit

Deleting an SSH certificate from a FIDO security key

To delete the large blob (i.e. the certificate) from a FIDO security key, for instance when that certificate has expired:

fido2-token -D -b -n ssh:demo /dev/hidraw0
Enter PIN for /dev/hidraw0: ********

Here, the application ID is used to refer to the credential associated with the large blob.

In case there are multiple credentials enrolled with that application ID, the credential ID must also be specified. In that case, lookup the credential ID first:

fido2-token -Lk ssh:demo /dev/hidraw0
Enter PIN for /dev/hidraw0: ********
00: dhx715WNlF36vMvo5hV0SBOPfWqS1ncj2P2BgW2513a/rLvg64Tl4f0/uDrs1LsE openssh am9obmRvZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= es256 uvopt+id

The credential ID is listed in base64-encoded format in the second column.

Then, delete the large blog specifying the credential ID.

fido2-token -D -b -n ssh:demo -i dhx715WNlF36vMvo5hV0SBOPfWqS1ncj2P2BgW2513a/rLvg64Tl4f0/uDrs1LsE /dev/hidraw0
Enter PIN for /dev/hidraw0: ********

To also delete the resident credential:

fido2-token -D -i dhx715WNlF36vMvo5hV0SBOPfWqS1ncj2P2BgW2513a/rLvg64Tl4f0/uDrs1LsE /dev/hidraw0
Enter PIN for /dev/hidraw0: ********

Final notes

SSH certificates are usually small, but can grow large when for instance many principal names are added. FIDO large blobs have a maximum size that depends on the specific FIDO security key used.

To inspect the large blobs maximum size for you security key, look for the maxlargeblob value reported by your security key:

fido2-token -I ioreg://4296903100 | grep maxlargeblob
maxlargeblob: 4096

Also note that maxlargeblob is reported starting with version 1.11 of fido2-token. Use (fido2-token -V) to check what version is installed.