YubiKey - hpaluch/hpaluch.github.io GitHub Wiki
Yubico YuibiKey
Recently I bought Yubico's YubiKey 5 NFC
- "USB key" that can be used
for authentication.
I know nothing about it (there are SOO MANY shorctuts) so I decided to start with: https://docs.fedoraproject.org/en-US/quick-docs/using-yubikeys/
Test OS: Fedora 43 Workstation (GNOME/Wayland) Beta in VM (ISO: Fedora-Workstation-Live-43-20251004.n.0.x86_64.iso
)
Local Login with U2F
I want to avoid Cloud of any kind so I will skip pam_yubico
and
rather use pam_u2f
, where U2F stands
for Universal 2nd Factor.
I started with install (from docs):
# for PAM authentication
sudo dnf install pam-u2f pamu2fcfg
# for Key management: (not required for U2F, because it uses unique private key
# that never leaves YubiKey)
sudo dnf install yubikey-manager
Next I created /etc/pam.d/u2f-required
with contents:
#%PAM-1.0
auth required pam_u2f.so
And also /etc/pam.d/u2f-sufficient
with contents:
#%PAM-1.0
auth sufficient pam_u2f.so
Now insert your Yubikey to USB. If you are using VM like me you have to do this in Virt-manager:
- select menu Virtual Machine -> Redirect USB device
- check on line with
Yubico.com Yubikey 4/5 OTP+U2F+CCID
- invoke
lusb
inside guest, in my case there is:
$ lsusb | grep Yubi
Bus 001 Device 003: ID 1050:0407 Yubico.com Yubikey 4/5 OTP+U2F+CCID
To list detected YubiKey(s) (redacted SN):
$ sudo ykman list | sed 's/[0-9]*$/X/'
YubiKey 5 NFC (5.7.4) [OTP+FIDO+CCID] Serial: X
More detailed info (redacted SN):
$ sudo ykman info | sed '/Serial number:/s/[0-9]/X/g'
Device type: YubiKey 5 NFC
Serial number: XXXXXXXX
Firmware version: 5.7.4
Form factor: Keychain (USB-A)
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
Generating configuration for for PAM:
-
do this as your target user that you want to authenticate with YubiKey
-
run:
mkdir -p ~/.config/Yubico pamu2fcfg > ~/.config/Yubico/u2f_keys # content is not sensitive, but it is better to tighten permissions chmod 600 ~/.config/Yubico/u2f_keys
-
WARNING! Later command seems to freeze, but it is only because it want you to touch sensor on key (to verify that you knowingly want to authenticate)
-
verify that sensor on your Yubico is flashing (green LED) and touch it
-
this should make
pamu2cfg ...
command to finish. -
verify that your generated file is non-0 length with:
$ ls -lo ~/.config/Yubico/u2f_keys -rw-r--r--. 1 X 196 Oct 7 16:27 /home/X/.config/Yubico/u2f_keys
As final thing we have to include our prepared PAM configuration to specific service.
We will use sufficient
which means that in case of failure user can
authenticate with another module (for example plain password).
I strongly suggest to backup your current /etc/
with trivial but really handy:
sudo tar -cvzf /root/etc-`date '+%s'`.tar.gz -C / etc
I will start with local console login:
- make backup:
sudo cp -v /etc/pam.d/login{,.orig}
- edit your
/etc/pam.d/login
- insert
auth include u2f-sufficient
right BEFORE line:auth substack system-auth
How it works:
- switch to local console with Ctrl-Alt-F4 (try other Fx key if there is no login: prompt)
- login with your username (user that created
~/.config/Yubico/u2f_keys
- once you press ENTER after specifying login name you should again see blinking green LED on you YubiKey. Touch sensor.
- right after touching sensor you should be logged in
- tip: if you login with user that has no valid file
~/.config/Yubico/u2f_keys
this authentication will fail, but PAM will allow to specify plain user's password (it will try next PAM module in chain) in such case thanks tosufficient
keyword.
Using YubiKey with GDM (GNOME Login Manager):
- in my case I edited
/etc/pam.d/gdm-password
- again, insert
auth include u2f-sufficient
right BEFORE line:auth substack system-auth
- and try login in GDM as your user - instead of asking for password you should see blank screen and your YubiKey should start blinking - touch sensor to login without password...
Changing PINs and PUK
Before using YubiKey to storea any kind of private key we should change its PINs and PUK as described on: https://docs.fedoraproject.org/en-US/quick-docs/using-yubikeys/#_update_the_pins_of_the_piv_module
When you try following command it will warng you that you are using trivial defaults:
# ykman piv info
PIV version: 5.7.4
PIN tries remaining: 3/3
PUK tries remaining: 3/3
Management key algorithm: AES192
WARNING: Using default PIN!
WARNING: Using default PUK!
WARNING: Using default Management key!
CHUID: No data available
CCC: No data available
WARNING! Store PINs and especially PUK reliably! You will loose all private key access without them!
To change PIN: use:
$ ykman piv access change-pin
Enter the current PIN: #ENTER: 123456 (will be not echoed)
Enter the new PIN: #ENTER 8-digits
Repeat for confirmation:
New PIN set.
$ ykman piv info
(there should be no longer warning on "default PIN", but other remains)
Same for PUK (store reliably!!!):
$ ykman piv access change-puk
Enter the current PUK: # ENTER 12345678
Enter the new PUK: # use 8 digits
Repeat for confirmation:
New PUK set.
Finally management key:
$ ykman piv access change-management-key --generate --protect
Enter the current management key [blank to use default key]: # just press ENTER
Enter PIN: # ENTER your New PIN from above
New management key set.
Verification: now there should be any WARNING! in info output:
$ ykman piv info
PIV version: 5.7.4
PIN tries remaining: 3/3
PUK tries remaining: 3/3
Management key algorithm: AES192
Management key is stored on the YubiKey, protected by PIN.
CHUID: No data available
CCC: No data available
Finally we should also change PIN for FIDO (WARNING! There is typo
in Fedora docs - replace piv
with fido
in command!).
$ ykman fido access change-pin
### specify pin that is easy to remember (at least 4 digits) you will need it often:
Enter your new PIN:
Repeat for confirmation:
FIDO PIN updated.
$ ykman fido info
AAGUID: d7781e5d-e353-46aa-afe2-3ca49f13332a
PIN: 8 attempt(s) remaining
Minimum PIN length: 4
Always Require UV: Off
Credential storage remaining: 100
Finally we should set OATH password, but beware!
- at least on
Fedora 43 Beta
- in my case second command crashed (run as root):
# this is OK:
$ ykman oath access change
Enter the new password: # used complex 20 char password generated by keepass
Repeat for confirmation:
Password updated.
$ ykman oath access remember
ERROR: An unexpected error has occurredTraceback (most recent call last):
File "/usr/lib/python3.14/site-packages/ykman/_cli/__main__.py", line 639, in main
...
gi.repository.GLib.GError: g-dbus-error-quark: Object does not exist at path “/org/freedesktop/secrets/collection/login” (19)
- fix is trivial: run later command from GUI, it will work, because now GNOME will create appropriate d-bus object:
# under GUI as non-root user:
$ ykman oath access remember
Enter the password: # your OATH password from above
Password remembered.
Password remembered.
$ ykman oath info
OATH version: 5.7.4
Password protection: enabled
The password for this YubiKey is remembered by ykman.
Anyway I also found really interesting article on PC/SC (= Personal Computer/Smart Card, see https://en.wikipedia.org/wiki/PC/SC):
- if you are NOT using GUI - you are in troubles
- because PolKit is used to restrict access to smart-cards and it does not permit non-local user to access it
- see https://blog.apdu.fr/posts/2023/11/pcsc-lite-and-polkit/
- under GUI you can try above commands:
$ pcsc_scan -r
0: Yubico YubiKey OTP+FIDO+CCID 00 00
There is also mentioned scriptor
but it is currently too difficult
for me (I just want to make such damn card to work :-)
Storing SSH private key on YubiKey
There are two main ways:
- FIDO2 - integrated with OpenSSH 8.2+ - easy
- PIV using PKCS#11 container (some PKCS containers are very popular as binary format for X.509 certificates)
SSH + FIDO2
Using Fedora guide, SSH+FIDO2 is trivial:
$ ssh-keygen -t ed25519-sk -O resident -O application=ssh:fedora -O verify-required
Generating public/private ed25519-sk key pair.
You may need to touch your authenticator to authorize key generation.
Enter PIN for authenticator: ### your FIDO PIN
You may need to touch your authenticator again to authorize key generation. ### touch sensor
Enter file in which to save the key (/home/uu/.ssh/id_ed25519_sk): /home/uu/.ssh/id_ed25519_sk_fido2
### I used empty passphrase
Enter passphrase for "/home/uu/.ssh/id_ed25519_sk_fido2" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/uu/.ssh/id_ed25519_sk_fido2
Your public key has been saved in /home/uu/.ssh/id_ed25519_sk_fido2.pub
The key fingerprint is:
...
Now you have to use standard ssh-copy-id
to copy .pub certificate to remote server (in my case
localhost
with another user called test2
):
ssh-copy-id -i ~/.ssh/id_ed25519_sk_fido2.pub test2@localhost
And finally test login via private key + YubiKey:
$ ssh -i ~/.ssh/id_ed25519_sk_fido2 test2@localhost
### touch sensor
Confirm user presence for key ED25519-SK SHA256:FQD3Oxz1ssieTHtsf0aM0vE5CmA8TjO13dv1XoDpNnM
### or enter your FIDO PIN
Enter PIN for ED25519-SK key /home/uu/.ssh/id_ed25519_sk_fido2:
Confirm user presence for key ED25519-SK SHA256:FQD3Oxz1ssieTHtsf0aM0vE5CmA8TjO13dv1XoDpNnM
User presence confirmed
$ id
uid=1002(test2) gid=1002(test2) ...
Note, it is easy to dump information on public key:
$ ssh-keygen -lf ~/.ssh/id_ed25519_sk_fido2.pub
256 SHA256:FQD3Oxz1ssieTHtsf0aM0vE5CmA8TjO13dv1XoDpNnM uu@fed43-ubi (ED25519-SK)
- but it is not case of private keys, some info on private key format is on: https://peterlyons.com/problog/2017/12/openssh-ed25519-private-key-file-format/
SSH + PIV
We will follow:
- https://docs.fedoraproject.org/en-US/quick-docs/using-yubikeys/#_update_the_pins_of_the_piv_module
- also official vendor guide is on https://developers.yubico.com/PIV/Guides/SSH_with_PIV_and_PKCS11.html
Before you start you should decide with "slot" you will use. Fedora guide uses 9a
, but fortunately
according to: https://gist.github.com/daemonhorn/bdd77a7bc0ff5842e5a31d999b96e1f1#yubikey-piv-slot-names-and-numbers
9a, 9c, 9d, 9e, 82-95 can all be used by OpenSSH pkcs11 support (and most other PIV consumers)
So if you need to store more than 1 SSH PIV keypair (very likely) you can use another slots.
Undocumented:
- to have installed stable 'library.so' (without number) suffix we can cheat that wit installing development version of package (that is normally required for linking only) with:
sudo dnf install yubico-piv-tool-devel
Verify that you have generated CCC and CHUID:
$ ykman piv info
PIV version: 5.7.4
PIN tries remaining: 3/3
PUK tries remaining: 3/3
Management key algorithm: AES192
Management key is stored on the YubiKey, protected by PIN.
CHUID: xxx
CCC: No data available
Note: I had missing CCC
so I followed
https://gist.github.com/daemonhorn/bdd77a7bc0ff5842e5a31d999b96e1f1#initial-yubikey-piv-key-generation-if-not-already-done
to generate it:
$ ykman piv objects generate ccc
Enter PIN: ### your PIV PIN
Object generated.
$ ykman piv info
PIV version: 5.7.4
PIN tries remaining: 3/3
PUK tries remaining: 3/3
Management key algorithm: AES192
Management key is stored on the YubiKey, protected by PIN.
CHUID: 30...
CCC: f0...
Slot 9A (AUTHENTICATION):
Private key type: ED25519
Public key type: ED25519
Subject DN: CN=OpenSSH
Issuer DN: CN=OpenSSH
...
So try (modified) instructions from Fedora Guide using slot 9a
:
$ mkdir ~/.ssh/piv1
$ ykman piv keys generate --algorithm ED25519 \
--pin-policy ONCE --touch-policy ALWAYS 9a ~/.ssh/piv1/public.pem
Enter PIN: ### your PIV PIN
Private key generated in slot 9A (AUTHENTICATION), public key written to /home/uu/.ssh/piv1/public.pem.
$ ykman piv certificates generate --subject "CN=OpenSSH" --hash-algorithm SHA384 9a ~/.ssh/piv1/public.pem
Enter PIN: ### your PIV PIN
Touch your YubiKey... ### touch sensor
Certificate generated in slot AUTHENTICATION.
Listing PIV keys:
$ ykman piv info
PIV version: 5.7.4
PIN tries remaining: 3/3
PUK tries remaining: 3/3
Management key algorithm: AES192
Management key is stored on the YubiKey, protected by PIN.
CHUID: xxx
CCC: No data available
Slot 9A (AUTHENTICATION):
Private key type: ED25519
Public key type: ED25519
Subject DN: CN=OpenSSH
Issuer DN: CN=OpenSSH
Serial: 54:...
Fingerprint: f7....
Not before: 2025-10-10T18:31:10+00:00
Not after: 2026-10-10T18:31:10+00:00
Now it looks better...
Exporting public key:
$ ssh-keygen -D /usr/lib64/libykcs11.so -e
skipping unsupported key type
failed to fetch key
unknown certificate key type
failed to fetch key
unknown certificate key type
failed to fetch key
ssh-rsa AAAAB... Public key for PIV Attestation pkcs11:id=%19;\
object=Public%20key%20for%20PIV%20Attestation;\
token=YubiKey%20PIV%20%23xxxxxx;\
manufacturer=Yubico%20(www.yubico.com);serial=xxxxxx?module-path=/usr/lib64/libykcs11.so
(to see just key - redirect error messages to /dev/null):
$ ssh-keygen -D /usr/lib64/libykcs11.so -e 2> /dev/null
ssh-rsa AAAAB... Public key for PIV Attestation pkcs11:id=%19;\
object=Public%20key%20for%20PIV%20Attestation;\
token=YubiKey%20PIV%20%23xxxxxx;\
manufacturer=Yubico%20(www.yubico.com);serial=xxxxxx?module-path=/usr/lib64/libykcs11.so
So here we can extract public key (to be copied to remote system):
$ ssh-keygen -D /usr/lib64/libykcs11.so -e 2> /dev/null > ~/.ssh/piv1/yubi-piv1.pub
$ file ~/.ssh/piv1/yubi-piv1.pub
/home/uu/.ssh/piv1/yubi-piv1.pub: OpenSSH RSA public key
To copy extracted key we have to add (-f) options, because ssh-copy-id
tries to also
open private key (without .pub
suffix), but that is actually on YubiKey only:
ssh-copy-id -f -i ~/.ssh/piv1/yubi-piv1.pub testpiv1@localhost
Finally we can login (should, but) with:
$ ssh -I /usr/lib64/libykcs11.so testpiv1@localhost
skipping unsupported key type
failed to fetch key
unknown certificate key type
failed to fetch key
unknown certificate key type
failed to fetch key
Enter PIN for 'YubiKey PIV #xxxxxx': # your PIV pin
C_Sign failed: 7
C_Sign failed: 7
sign_and_send_pubkey: signing failed for RSA "pkcs11:?module-path=/usr/lib64/libykcs11.so": error in libcrypto
So far only found this debug switch:
YKCS11_DBG=1 ssh -I /usr/lib64/libykcs11.so testpiv1@localhost
Trying create key in slot 82 from FreeBSD guide:
$ mkdir ~/.ssh/piv82
$ cd ~/.ssh/piv82
$ ykman piv keys generate -a eccp384 --pin-policy once --touch-policy cached 82 82_pub.pem
Enter PIN:
Private key generated in slot 82 (RETIRED1), public key written to 82_pub.pem.
$ ykman piv certificates generate --valid-days 730 --subject "Y5C_82" 82 82_pub.pem
Enter PIN:
Touch your YubiKey...
Certificate generated in slot RETIRED1.
$ ykman piv info
...
Slot 82 (RETIRED1):
Private key type: ECCP384
Public key type: ECCP384
Subject DN: CN=Y5C_82
Issuer DN: CN=Y5C_82
Not before: 2025-10-10T19:46:59+00:00
Not after: 2027-10-10T19:46:59+00:00
...
Exporting key:
# note: 'grep Retired' is crude way to select right public key (now I have 2 keys in PIV and
# both are exported)
$ ssh-keygen -D /usr/lib64/libykcs11.so -e | grep Retired > exported.pub
$ scp exported.pub testpiv82@localhost:.ssh/authorized_keys
$ ssh -I /usr/lib64/libykcs11.so testpiv82@localhost
Testing:
$ ssh -I /usr/lib64/libykcs11.so testpiv82@localhost
skipping unsupported key type
failed to fetch key
unknown certificate key type
failed to fetch key
unknown certificate key type
failed to fetch key
Enter PIN for 'YubiKey PIV #33089891': ### your PIV key
testpiv82@fed43-ubi:~$ id
...
Huh! It works...
Resources
TODO: Study material
- https://docs.fedoraproject.org/en-US/quick-docs/using-yubikeys/#_using_the_yubikey_to_authenticate_against_openssh_servers
- or: https://docs.fedoraproject.org/en-US/quick-docs/using-yubikeys/#_alternative_for_keys_without_fido2_support
- https://developers.yubico.com/SSH/Securing_SSH_with_FIDO2.html
- really good and comprehensive guide for FreeBSD: https://gist.github.com/daemonhorn/bdd77a7bc0ff5842e5a31d999b96e1f1