0x01. Yubikey + ssh/git (Linux)
TableOfContents
0x00. Intro
From some point, YubiKeys (https://www.yubico.com/products/yubikey-5-overview/) have become an essential part of my essential device arsenal. Many services, such as Google, GitHub (web GUI), and others, support them out of the box. Additionally, it is possible to create a certificate on them for SSH authentication. However, this requires some extra effort, as neither Linux nor Windows support this functionality out of the box.
Since certain key configurations can be performed both on Windows and Linux, regardless of the OS where the key will be used later, this article will focus exclusively on the peculiarities of using YubiKey on Linux.
0x01. Yubikey
First, you need to configure the YubiKey itself - change the default PIN codes (in case the YubiKey falls into the wrong hands), then create a certificate and export the public key.
How this is done on Windows can be read here: 0x00. Yubikey + ssh/git (Windows)
Yubikey manager (Linux, GUI)
Essentially, it’s the same as on Windows, but first, you need to install YubiKey Manager (along with other packages that will be needed later):
sudo apt install -y ykcs11 yubico-piv-tool yubikey-manager scdaemon yubikey-personalization opensc libnss3-tools
To launch the GUI for YubiKey Manager, execute the command:
ykman-gui
PS: If YubiKey Manager shows the error “Failed connecting to the YubiKey. Make sure the application has the required permissions.” (this sometimes happens on recent Ubuntu releases), execute the following commands:
sudo apt install pcscd
sudo systemctl enable pcscd
sudo systemctl start pcscd
If this doesn’t help, try the solutions from
If you prefer to configure everything via the console (for instance, if your Linux system lacks a graphical interface), then it’s the way of the samurai:
ykman
YubiKey Manager CLI (ykman) User Manual
View connected YubiKey devices:
ykman list
Will output something like:
└─$ ykman list
YubiKey 5C NFC (5.4.3) [OTP+FIDO+CCID] Serial: 12345678
That is, the key model, firmware version, available APIs, and serial number.
Extended information:
└─$ ykman info
Device type: YubiKey 5C NFC
Serial number: 12345678
Firmware version: 5.4.3
Form factor: Keychain (USB-C)
Enabled USB interfaces: OTP, FIDO, CCID
NFC transport is enabled
Applications USB NFC
Yubico OTP Enabled Enabled
FIDO U2F Enabled Enabled
FIDO2 Enabled Enabled
OATH Enabled Enabled
PIV Enabled Enabled
OpenPGP Enabled Enabled
YubiHSM Auth Enabled Enabled
Complete reset of PIV settings/certificates (e.g., if you forgot the PIN/PUK or are reconfiguring from scratch):
ykman piv reset
Changing the PUK:
ykman piv access change-puk
Changing the PIN:
ykman piv access change-pin
Changing the management key (at the same time enabling its protection with a PIN - meaning it will ask for a PIN to confirm each time the management key is used):
ykman piv keys change-management-key --generate --protect
and save the generated key in a secure location, as you’ll need it later.
More here
Certificates
Similarly to how it’s done on Windows.
Or once again, the way of the samurai (here I’m generating it in slot 9c instead of 9a, so it asks for a PIN each time it’s accessed via SSH - paranoia is what it is).
- First, generate a private key.
- Create a self-signed certificate.
- Generate a public key for the created certificate.
All these actions can be performed either using yubico-piv-tool (which we installed at the very beginning) or with the console YubiKey Manager (ykman). In both cases, you’ll need the management key that we created and saved in a secure location earlier. If not—then you’ll have to use ykman exclusively. That’s because yubico-piv-tool, if no management key is specified, uses the default 0102… key, which we prudently replaced with our own. ykman, on the other hand, will simply ask for the PIN code (remember that we protected the management key with a PIN code? This is exactly why—so we don’t have to write it down or store it somewhere else %)).
1. Creating a private key
For example, for a certificate in slot 9c (I’ve already explained why I prefer 9c over 9a), using the 256-bit elliptic curve algorithm:
ykman piv keys generate --algorithm ECCP256 9c public92.pem
Enter PIN: ******
The private key is stored on the YubiKey device, and the public key is saved in public92.pem (default format is PEM).
You can also specify other options:
└─$ ykman piv keys generate --help
Usage: ykman piv keys generate [OPTIONS] SLOT PUBLIC-KEY
Generate an asymmetric key pair.
The private key is generated on the YubiKey, and written to one of the slots.
SLOT PIV slot of the private key
PUBLIC-KEY file containing the generated public key (use '-' to use stdout)
Options:
-m, --management-key TEXT the management key
-P, --pin TEXT PIN code
-a, --algorithm [RSA1024|RSA2048|RSA3072|RSA4096|ECCP256|ECCP384|ED25519|X25519]
algorithm to use in key generation [default: RSA2048]
-F, --format [PEM|DER] encoding format [default: PEM]
--pin-policy [DEFAULT|NEVER|ONCE|ALWAYS|MATCH-ONCE|MATCH-ALWAYS]
PIN policy for slot
--touch-policy [DEFAULT|NEVER|ALWAYS|CACHED]
touch policy for slot
-h, --help show this message and exit
2. Creating a certificate using the generated key
Create a certificate in slot 9c being valid for three years, with the subject “x-ssh” (the “CN=” prefix is mandatory!). public92.pem is the public key we saved earlier:
└─$ ykman piv certificates generate --valid-days=1095 --subject "CN=x-ssh" 9c public92.pem
Enter PIN: *******
Verify:
└─$ ykman piv info
PIV version: 5.4.3
PIN tries remaining: 3/3
PUK tries remaining: 3/3
Management key algorithm: AES256
Management key is stored on the YubiKey, protected by PIN.
CHUID: 3019d4e739da739ced39ce739d836858210842108421c84210c3eb3410ecff3c92f11bf17734ab8b6dc0e8f47d350832303330303130313e00fe00
CCC: No data available
Slot 9C (SIGNATURE):
Private key type: ECCP256
Public key type: ECCP256
Subject DN: CN=x-ssh
Issuer DN: CN=x-ssh
Serial: 161728660454137589467847298317485943567371955241
Fingerprint: 7d63fc02b49f58d7426519244440adec2cd557d84f9163b5f1fbb71f7b116953
Not before: 2024-12-15T07:38:34
Not after: 2027-12-15T07:38:34
0x02. OpenSSH
For OpenSSH, we first need to extract the public key in the OpenSSH format.
But first, we need to know the full path to the pkcs11.so library.
Depending on the distribution and package version, it may be located in different places and have different names, for example:
/usr/lib64/pkcs11/opensc-pkcs11.so
/usr/lib/x86_64-linux-gnu/libykcs11.so
/usr/lib64/libykcs11.so
But there is a common part - kcs11.so, so if none of the above-mentioned paths exist on your system, you can try to find it:
└─$ locate *kcs11.so
...
/usr/lib/jvm/java-17-openjdk-amd64/lib/libj2pkcs11.so
/usr/lib/x86_64-linux-gnu/libykcs11.so
/usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-pkcs11.so
...
In my case, it is:
/usr/lib/x86_64-linux-gnu/libykcs11.so
Therefore, execute:
└─$ ssh-keygen -D /usr/lib/x86_64-linux-gnu/libykcs11.so -e
...
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIn6Oc8Bnq+kKEAYCwUILth7Yeo+EqbxP3zXH0UNIT9EIINxhmyNriSF1uu8d3hDIXQvAp4CQr8elNCML85tX54= Public key for Digital Signature
...
For slot 9a, the key ends with Public key for PIV Authentication, and for 9c, it ends with Public key for Digital Signature.
This is the key that we then use on remote systems or GitHub to provide access via YubiKey.
For example, if using SSH, install it in ~/.ssh/authorized_keys on the remote system by any method, and then from the client machine, execute:
ssh -I /usr/lib/x86_64-linux-gnu/libykcs11.so user@host
The option -I /usr/lib/x86_64-linux-gnu/libykcs11.so means that if a YubiKey is connected and the corresponding private key exists in slots 9a/9c, it will prompt for a PIN code—and if the PIN is correct, the connection will proceed using the private key. Otherwise, it will fall back to the usual SSH method.
To avoid specifying this option every time, you can set it as the default:
sudo echo "PKCS11Provider /usr/lib/x86_64-linux-gnu/libykcs11.so" | sudo tee -a /etc/ssh/ssh_config
In this case, use SSH as usual. This is also very convenient if you’re using other programs that rely on SSH, such as git, scp, etc.
P.S. This post was pushed to GitHub using the YubiKey. The translation — courtesy of a great guy, ChatGPT %)
Refs
- https://developers.yubico.com/yubico-piv-tool/
- https://developers.yubico.com/PIV/Guides/SSH_with_PIV_and_PKCS11.html
- https://docs.yubico.com/software/yubikey/tools/ykman/Install_ykman.html#linux-installation
- https://support.yubico.com/hc/en-us/articles/360016648939-Troubleshooting-Failed-connecting-to-the-YubiKey-Make-sure-the-application-has-the-required-permissions-in-YubiKey-Manager
- https://debugging.works/blog/yubikey-cheatsheet/
- https://support.yubico.com/hc/en-us/articles/360016614940-YubiKey-Manager-CLI-ykman-User-Manual
- https://docs.yubico.com/software/yubikey/tools/ykman/PIV_Commands.html
- https://developers.yubico.com/PIV/Guides/PIV_Walk-Through.html
- https://developers.yubico.com/PIV/Guides/SSH_with_PIV_and_PKCS11.html
- https://developers.yubico.com/PIV/Guides/Device_setup.html