GnuPG with Yubikey
The Yubikey 5 implements the OpenPGP card specification, giving it three dedicated slots for GPG key material: one for signing, one for encryption, and one for authentication. Once your subkeys are moved onto the card, all GPG operations use the hardware rather than key files on disk. The private key material never leaves the card. Signing a message, decrypting a file, authenticating an SSH connection, all of these require the Yubikey to be physically present.
This is a meaningful security improvement over software-only key storage. A compromised machine cannot exfiltrate a key stored on hardware. It can request operations from the card, but cannot read the key itself.
Before you start
This page assumes you have completed the GPG key generation section and have a key with three subkeys: signing [S], encryption [E], and authentication [A]. It also assumes you have a backup of your private key on encrypted offline storage. If you have not done this, do it before continuing.
The keytocard operation in GPG is destructive on disk. It moves the subkey to the card and replaces the on-disk key with a stub. Once moved, the subkey cannot be used without the Yubikey, and cannot be moved to a second Yubikey from the desktop. Your offline backup is the only way to load the key onto a backup card.
Read that paragraph again before proceeding.
Prerequisites
Install the required packages if not already in place:
sudo apt install scdaemon pcscd yubikey-manager
scdaemon handles communication between GPG and the card. pcscd is the PC/SC daemon for smartcard access. yubikey-manager provides the ykman command line tool for configuring the Yubikey.
scdaemon configuration
Create ~/.gnupg/scdaemon.conf to configure scdaemon to work reliably with the Yubikey:
#
# scdaemon configuration for Yubikey 5
#
# Use pcscd for card access rather than direct CCID
# This avoids conflicts when ykman or other tools also access the card
disable-ccid
# Path to the pcsclite library
pcsc-driver /usr/lib/x86_64-linux-gnu/libpcsclite.so.1
# Disconnect from card after each operation
# Prevents stale connections after suspend/resume
card-timeout 5
# Prefer the Yubikey when multiple readers are present
reader-port Yubico YubiKey
Verify the library path exists on your system:
dpkg -L libpcsclite1 | grep libpcsclite.so
If the path differs, update scdaemon.conf accordingly. Reload the agent after saving:
gpgconf --kill gpg-agent
The agent restarts automatically on the next GPG operation.
Verify the Yubikey is detected
Insert the Yubikey and check that GPG can see it:
gpg --card-status
A successful response shows the card details including the OpenPGP application version, serial number, and three empty key slots. If GPG cannot find the card, check that pcscd is running:
sudo systemctl start pcscd
sudo systemctl enable pcscd
Then kill and restart the scdaemon:
gpgconf --kill scdaemon
gpg --card-status
Configure the card
Before moving keys onto the card, set the PIN, Admin PIN, and card metadata. The default PINs on a new Yubikey are:
- User PIN:
123456 - Admin PIN:
12345678
These must be changed before use. Three incorrect User PIN entries locks the card. Three incorrect Admin PIN entries destroys the key material permanently. This is not recoverable.
Enter card edit mode:
gpg --edit-card
Enable admin commands and change the PINs:
gpg/card> admin
gpg/card> passwd
Select option 1 to change the User PIN. Use a six-digit numeric PIN or a longer alphanumeric passphrase. Store it in KeePassXC.
Select option 3 to change the Admin PIN. Use a different value from the User PIN. Store it in KeePassXC.
Set the reset code (option 4). This can be used to unblock a locked User PIN without knowing the Admin PIN. Store it in KeePassXC.
Set the cardholder name and public key URL while in admin mode:
gpg/card> name
Cardholder name (surname): Doe
Cardholder name (given name): John
gpg/card> url
URL to retrieve public key: https://keys.openpgp.org/vks/v1/by-fingerprint/<your-fingerprint>
The URL allows GPG to fetch your public key automatically when the card is inserted on a new machine. Use your actual fingerprint from gpg --fingerprint $GPGKEY.
Quit card edit mode:
gpg/card> quit
Move subkeys to the card
This is the point of no return. Make absolutely certain your offline backup exists and is readable before continuing.
Open the key for editing:
gpg --edit-key $GPGKEY
The key listing should show your primary key (sec) and three subkeys (ssb) with [S], [E], and [A] capabilities. If the primary key shows #, it is correctly offline. If it shows without #, verify you followed the offline primary key steps in the key generation section.
Move the signing subkey
Select the signing subkey. In most setups it is key 1:
gpg> key 1
An asterisk appears next to the selected key. Move it to the card:
gpg> keytocard
GPG asks which slot to use. Select the signature slot (option 1).
Deselect the key before selecting the next:
gpg> key 1
Move the encryption subkey
Select the encryption subkey:
gpg> key 2
gpg> keytocard
Select the encryption slot (option 2).
Deselect:
gpg> key 2
Move the authentication subkey
Select the authentication subkey:
gpg> key 3
gpg> keytocard
Select the authentication slot (option 3).
Save and exit
gpg> save
Verify the result
Check the key listing:
gpg --list-secret-keys $GPGKEY
Each subkey should now show ssb> rather than ssb. The > indicates the key is stored on a card rather than on disk. The primary key (sec) should show # indicating it is not available locally.
Verify the card contains the keys:
gpg --card-status
The three key slots should now show fingerprints rather than [not set].
Test a GPG operation
With the Yubikey inserted, test signing:
echo "test" | gpg --clearsign
GPG will prompt for the User PIN via the Qt pinentry dialog. Enter it and the signed output should appear. The Yubikey may also blink requiring a touch, depending on whether touch confirmation is enabled.
Test decryption by encrypting something to yourself and decrypting it:
echo "test" | gpg --encrypt --recipient $GPGKEY | gpg --decrypt
Enable touch confirmation
The Yubikey 5 supports requiring a physical touch for each cryptographic operation. This prevents a compromised machine from silently using the card without your knowledge.
Enable touch for all three operations using ykman:
ykman openpgp keys set-touch sig on
ykman openpgp keys set-touch enc on
ykman openpgp keys set-touch aut on
Each command prompts for the Admin PIN. After enabling touch, every GPG signing, decryption, or authentication operation will cause the Yubikey to blink, waiting for a physical touch before proceeding. This is a meaningful additional security layer.
To check the current touch policy:
ykman openpgp info
Using the card on a different machine
Moving subkeys to the Yubikey means they travel with the card, but GPG on other machines still needs the public key and a card stub to know how to use the card.
On the new machine, fetch your public key:
gpg --fetch-key https://keys.openpgp.org/vks/v1/by-fingerprint/<your-fingerprint>
Or import it directly from the card:
gpg --edit-card
gpg/card> fetch
gpg/card> quit
Set ultimate trust:
gpg --edit-key $GPGKEY
gpg> trust
# Select 5 (ultimate)
gpg> quit
GPG on the new machine can now use the card for all operations. The private key never left the hardware.
Suspend and resume issues
A known issue with scdaemon is that after a suspend/resume cycle, it can lose communication with the Yubikey. The card-timeout 5 setting in scdaemon.conf mitigates this by disconnecting after each operation, but if you encounter errors after waking from suspend:
gpgconf --kill scdaemon
Remove and reinsert the Yubikey. Scdaemon restarts automatically on the next GPG operation.
The
keytocardoperation is one-way from the perspective of your desktop. The key moves to the card and the on-disk copy becomes a stub. Your offline backup is the only source from which the key can be loaded onto a second card. Treat that backup accordingly.