Just received my updated Yubikeys and thought I’d document the process of using them with OpenSSH in PIV mode. Again, but better. 😀
For some context, with a Yubikey, you have 3 options to use them with OpenSSH:
- OpenPGP mode: Very complicated in terms of tooling. Maybe worth it, if you already use OpenPGP or GPG.
- FIDO2 mode: Needs a relatively new OpenSSH version on the server and client. The simplest option, if you don’t have older servers to manage. See this other article
- PIV mode: Moderately complicated to set up and only supports a limited set of approved ciphers.
So we’ll be using option 3 in this article.
Obviously you’ll need one or two Yubikeys. Two are recommended if one breaks. Or you add some kind of backup key on the server.
Next we need 2 packages installed: The Yubico PIV Tool to manage the key’s PIV mode and Yubikey Manager, a simple GUI tool to view and adjust key settings.
To install them via Homebrew on macOS:
$ brew install yubico-piv-tool yubico-yubikey-manager
Step 1: Secure Key 🔗
If you just received the key, it’s worth changing the default PIN and PUK. This can be done in the GUI via Yubikey Manager or in the terminal:
$ ykman piv access change-pin $ ykman piv access change-puk $ ykman piv access change-management-key --generate --protect
- Changing the management key is optional. If you change it, you will need to add the
--keyflag to all future
ykmancommand is bundled with the Yubikey Manager tool. On macOS you can find it in
Step 2: Generate and Import Private Key 🔗
PIV mode supports only 3 kinds of keys:
ECCP384. As of 2023,
RSA2048 is a bit dated and slow. So we’ll go with
At this step you have 2 options on where to generate the private key:
Option 1: Generate Private Key ON Yubikey 🔗
First you can generate the key on the Yubikey. Benefit is that it can never be extracted from there and will be lost if the key is damaged. This is also the downside: You can’t have a spare key. To generate the private key on the Yubikey:
$ yubico-piv-tool -s 9a -AECCP384 -a generate --pin-policy=once --touch-policy=always -o public.pem
When running the
import-key action, you can also customize the security policy. I.e. if and when a touch and PIN entry will be requested.
Option 2: Generate Private Key OFF Yubikey 🔗
Second option is to generate the key on your own machine and then copy it to as many Yubikeys as you like. This is slightly less secure, but also safer, in case a Yubikey gets damaged. To generate a private and public key in ECC 384 with
$ openssl ecparam -genkey -name secp384r1 -noout -out ec384-key.pem $ openssl pkey -in ec384-key.pem -pubout > ec384-pub.pem
After this you will have 2 files, which correspond to your private and public key. Import them onto as many Yubikeys as needed using the commands below. Then be sure to safely delete the private key.
$ yubico-piv-tool -s 9a --pin-policy=once --touch-policy=always -a import-key -i ec384-key.pem
Step 3: Generate PIV Certificate 🔗
Now we’re ready to import the PIV certificate onto the Yubikey. This certificate is only needed for the PIV specification and isn’t used by SSH. Depending on where you generated your private key, it will be slightly different:
If you generated the private key on the Yubikey before, you only need one step to create a PIV certificate:
$ yubico-piv-tool -a verify-pin -a selfsign-certificate -s 9a --subject "/CN=SSH key/" --valid-days=3650 -i public.pem -o cert.pem
If you chose to generate the private key off the Yubikey:
$ yubico-piv-tool -a verify-pin -a selfsign-certificate -s 9a --subject="/CN=SSH key/" --valid-days=3650 -i ec384-pub.pem -o cert.pem
The final step of importing it is the same:
$ yubico-piv-tool -a import-certificate -s 9a -i cert.pem
Step 4: Export OpenSSH Public Key 🔗
With our private key stored safely on the key and PIV mode set up, we can verify that it all worked. If you see some key details listed for slot
9a, then everything worked as expected.
$ yubico-piv-tool -a status
Now we’re ready to grab the public OpenSSH key to be used on remote machines. Note that on macOS the file
/usr/local/lib/libykcs11.dylib is often a symlink, which won’t work due to security restrictions. So you will need to remove the symlink and replace it with an actual copy of
View the public key corresponding to the private key stored on the Yubikey like this:
$ ssh-keygen -D /usr/local/lib/libykcs11.dylib -e
You will see a long string starting with
ecdsa-sha2-nistp384. This is what you add to a remote server’s
Step 5: Practical Tips 🔗
Finally, some helpful tricks to make it work better with OpenSSH:
To (re)add the Yubikey to
ssh-agent and cache the key. A good command to
$ ssh-add -e /usr/local/lib/libykcs11.dylib; ssh-add -s /usr/local/lib/libykcs11.dylib
~/.ssh/config add this to always offer the Yubikey for authentication:
Host * PKCS11Provider /usr/local/lib/libykcs11.dylib
And to cache connections for a few minutes without asking for a touch:
Host * ControlMaster auto ControlPath ~/.ssh/S.%r@%h:%p ControlPersist 10m