Use diceware to generate a strong passphrase for your SSH CA keys:
$ sudo apt install diceware
$ diceware -d ' '
First, clone this repo to the server system:
$ git clone https://github.com/openavr/sshca-yk.git
$ cd sshca-yk
To generate the User and Host SSH CA keys and load them into a Yubikey:
Usage: ./ca-create.sh <org> <common-name>
For example, for the example.com domain, with a common name of TestExample
(this will prompt you to enter the passphrase for the keys):
$ ./ca-create.sh example.com TestExample
The generated private keys are now on-disk and in the Yubikey. The on-disk copies must be protected and not shared.
Verify the contents of the Yubikey:
$ ykman piv info
...
Slot 84 (RETIRED3):
Private key type: ED25519
Public key type: ED25519
Subject DN: CN=TestExample User SSH CA,O=example.com
Issuer DN: CN=TestExample User SSH CA,O=example.com
Serial: 52:1e:44:b0:ca:80:5c:11:75:6e:48:c7:1a:d8:a4:ce:83:d8:5c:1c
Fingerprint: 77348c2ff32ecc7b5ea8820a0a22509bb1a97c02c67ea8f02af582bd06ac12bc
Not before: 2026-07-26T19:21:16+00:00
Not after: 2026-10-24T19:21:16+00:00
...
Generate an authorized_keys_ca file that has entries you can copy to your
~/.ssh/authorized_keys file on the server that you want to allow users to log
into using certificates:
$ ./generate-auth-keys.sh example.org
The generated authorized_keys_ca can be copied to any server as it does not
contain any sensitive material.
Copy the User SSH CA line from the generated authorized_keys_ca file into
your ~/.authorized_keys file on the server(s).
Alternatively, you could install the User SSH CA public key globally so that
it can be used for all user accounts by adding it to the TrustedUserCAKeys
entry in the /etc/ssh/sshd_config file. See the sshd_config(5) man page for
more details.
$ sudo cp keys-ca/example.com/id_ed25519_user_ca.pub /etc/ssh
$ echo "TrustedUserCAKeys /etc/ssh/id_ed25519_user_ca.pub" \
| sudo tee /etc/ssh/sshd_config.d/user_ca.conf
$ sudo systemctl restart sshd
The target user account name on the server should be at least one of the principals in the certificate file.
The server side user account is now setup to accept logins from clients using certificates signed by the SSH User CA.
Clone this repo to the client system:
$ git clone https://github.com/openavr/sshca-yk.git
$ cd sshca-yk
Plug the Yubikey into the client system.
Extract the CA public key from the Yubikey:
$ ykman piv info
$ ssh-keygen -D /usr/lib/x86_64-linux-gnu/libykcs11.so \
| grep 'Retired Key 3' > keys-ca/example.com/id_ed25519_user_ca.pub
Generate and sign a user ssh key pair and certificate:
$ ./ca-sign-user.sh example.com ${USER}
You should now be able to login to the server as ${USER}:
$ ssh -i ./keys-user/id_ed25519_${USER} <server>
You can also create the key and sign the cert in your ~/.ssh directory:
$ ./ca-sign-user.sh example.com ${USER} ~/.ssh/id_ed25519_${USER}
The format of the private generated by openssh can not be imported directly
into a Yubikey, it must be converted first.
You must use openssh >= 10.3p1 in order to be able to generate ed25519 keys
and convert them to PEM or PKCS8 format that can be loaded into Yubikey.
For Debian-13 (Trixie), you need to install the openssh tools using debian backports. Setup debian backports with the instructions here:
Once backports is setup, install the newer version of openssh:
$ sudo apt update
$ sudo apt install \
openssh-client/trixie-backports \
openssh-server/trixie-backports \
openssh-sftp-server/trixie-backports
You only need openssh >= 10.3p1 if you are loading the CA or user keys into
the Yubikey, and do not need the newer version if you are only using the
Yubikey to sign user or host certificates.
NOTE: The -N '' will generate a key without a passphrase which is
probably not what you want.
Generate a ed25519 key pair:
$ ssh-keygen -t ed25519 -N '' -f id_ed25519_foo
Convert the private key to a format that can be imported into a yubikey:
$ cp id_ed25519_foo id_ed25519_foo.key
$ ssh-keygen -p -m PKCS8 -N '' -f id_ed25519_foo_key.pem
Import the private key into a yubikey:
$ ykman piv keys import 85 id_ed25519_foo_key.pem
$ ykman piv info
To verify is a private key and a public match, use this:
# match if no differences and exit code of 0
$ diff <(ssh-keygen -l -v -f $sshkeyfile) <(ssh-keygen -l -v -f $sshpubfile)
To see if a public key matches the private key stored in a Yubikey slot, you first need to dump all of the public keys stored in the yubikey. This can be done with the following command:
$ ssh-keygen -D /usr/lib/x86_64-linux-gnu/libykcs11.so
Then you can compare against another public key, but this is probably not needed since now you have the public keys of the private keys in the Yubikey and can just use them.
Run the following command to install basic tool dependencies needed for these scripts:
$ sudo apt install ykcs11 yubico-piv-tool yubikey-manager libyubikey-udev