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.
Prerequisites ๐
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
Some notes:
- Changing the management key is optional. If you change it, you will need to add the
--key
flag to all futureyubico-piv-tool
commands. - The
ykman
command is bundled with the Yubikey Manager tool. On macOS you can find it in/Applications/YubiKey Manager.app/Contents/MacOS/ykman
.
Step 2: Generate and Import Private Key ๐
PIV mode supports only 3 kinds of keys: RSA2048
, ECCP256
, ECCP384
. As of 2023, RSA2048
is a bit dated and slow. So we’ll go with ECCP384
.
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 generate
or 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
:
$ 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 libykcs11.dylib
.
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 authorized_keys
file.
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 alias
.
$ ssh-add -e /usr/local/lib/libykcs11.dylib; ssh-add -s /usr/local/lib/libykcs11.dylib
In ~/.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