$linuxjunkies
>

Use a YubiKey for SSH and GPG

Move SSH and GPG private keys onto a YubiKey using the OpenPGP applet with gpg-agent and FIDO2 resident keys, with touch policies and offline backups.

AdvancedUbuntuDebianFedoraArch12 min readUpdated June 7, 2026

Before you start

  • ▸YubiKey 5 series hardware token
  • ▸OpenSSH 8.2 or later (for FIDO2 support)
  • ▸Root or sudo access to install packages
  • ▸Basic familiarity with GPG key concepts (public/private keys, subkeys)

A YubiKey moves your SSH and GPG private keys off disk entirely. The key material is generated on the device and never exported — even if your laptop is compromised, the attacker cannot steal the key, only borrow it until you physically remove the token. This guide covers two complementary approaches: using the YubiKey's OpenPGP card applet with gpg-agent as an SSH agent, and the newer FIDO2 resident key method for SSH. Both are useful; many engineers run them together.

Prerequisites and Hardware Setup

You need a YubiKey 5 series (USB-A, USB-C, or NFC). The YubiKey 4 lacks FIDO2. Install the management tools before touching any applet configuration.

# Debian / Ubuntu
sudo apt install yubikey-manager scdaemon gnupg2 openssh-client

# Fedora / RHEL family
sudo dnf install yubikey-manager pcsc-lite opensc gnupg2 openssh

# Arch
sudo pacman -S yubikey-manager ccid opensc gnupg openssh
# Verify the key is detected
ykman info

Output will show firmware version, serial number, and enabled interfaces. If nothing appears, check that pcscd is running.

sudo systemctl enable --now pcscd

Method 1 — OpenPGP Applet with gpg-agent SSH

This method stores an Authentication subkey on the YubiKey's OpenPGP applet. gpg-agent exposes it as an SSH agent socket, so ssh authenticates using the card without any private key on disk.

Step 1 — Generate a GPG key with an Authentication subkey

Generate the master key offline on an air-gapped machine if possible. Here we do it inline for brevity, but consider the risk.

gpg --full-generate-key
# Choose: (1) RSA and RSA or (9) ECC — ECC (ed25519/cv25519) is recommended
# Key size: 4096 for RSA, or curve 25519 for ECC
# Expiry: 1y is a reasonable default
# Note your KEY_ID (long hex) from:
gpg --list-secret-keys --keyid-format LONG
# Add an Authentication subkey
gpg --expert --edit-key YOUR_KEY_ID
# At the gpg> prompt:
# addkey
# Choose (8) RSA set your own capabilities  OR  (11) ECC set your own
# Toggle so only Authenticate is selected, then: Q
# save

Step 2 — Move subkeys onto the YubiKey

This is destructive for on-disk copies — export a backup first.

# Export backup BEFORE moving to card
gpg --export-secret-keys --armor YOUR_KEY_ID > master_backup.asc
gpg --export-secret-subkeys --armor YOUR_KEY_ID > subkeys_backup.asc
# Store these encrypted, offline (e.g., on an encrypted USB drive)
gpg --edit-key YOUR_KEY_ID
# gpg> keytocard
# Move Authentication subkey: select it with 'key 2' (or whichever index)
# key 2
# keytocard
# Choose slot (3) Authentication
# save

Step 3 — Configure gpg-agent for SSH

mkdir -p ~/.gnupg
cat >> ~/.gnupg/gpg-agent.conf <<'EOF'
enable-ssh-support
default-cache-ttl 600
max-cache-ttl 7200
pinentry-program /usr/bin/pinentry-curses
EOF
# Restart the agent
gpgconf --kill gpg-agent
gpg-agent --daemon
# Add to your shell profile (~/.bashrc, ~/.zshrc, etc.)
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)

Source the profile, then test with ssh-add -L. You should see a public key line corresponding to your Authentication subkey. Copy that public key to your server's ~/.ssh/authorized_keys.

ssh-add -L | tee ~/.ssh/yubikey_auth.pub

Step 4 — Set touch policy (require physical touch)

# Require touch for OpenPGP authentication operations
ykman openpgp keys set-touch aut on
# Options: off | on | fixed (fixed cannot be changed without factory reset)
# 'cached' allows re-use within 15 s — good balance for frequent SSH use
ykman openpgp keys set-touch aut cached

Method 2 — FIDO2 Resident SSH Keys

OpenSSH 8.2+ supports FIDO2 hardware tokens natively. Resident keys are stored on the YubiKey itself, so you can re-derive the public key on any machine — useful when moving between workstations.

Step 1 — Set a FIDO2 PIN

ykman fido access change-pin
# You'll be prompted to set a new PIN (min 4 chars, 8+ recommended)

Step 2 — Generate a resident FIDO2 SSH key

ssh-keygen -t ed25519-sk -O resident -O application=ssh:my-laptop -O verify-required
# -t ed25519-sk   : FIDO2 with ed25519
# resident        : store key handle on the YubiKey
# application     : label visible in ykman
# verify-required : enforce PIN entry (user-verification)
# You will be prompted to touch the key and enter your FIDO2 PIN

This creates ~/.ssh/id_ed25519_sk and ~/.ssh/id_ed25519_sk.pub. The private key file is a key handle stub — it is useless without the physical YubiKey.

Step 3 — Copy the public key to servers

ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub user@server

Step 4 — Load the resident key on a new machine

# On a machine where you don't have the stub file:
ssh-keygen -K
# Downloads the resident key handles from the YubiKey
# Outputs id_ed25519_sk_rk and id_ed25519_sk_rk.pub in the current directory

Verification

# Test GPG card status
gpg --card-status

# Test SSH auth (verbose to see which key is offered)
ssh -v user@your-server 2>&1 | grep -E 'Offering|Authentications'

# Confirm touch is required — the YubiKey LED should blink waiting for you
ssh user@your-server echo ok

For FIDO2 keys, SSH will print a message that user presence confirmation is required, and the key will blink until you touch it.

Backup Key Strategy

Never rely on a single token. There are two practical approaches:

  • Second YubiKey: Generate the GPG key once, move the subkeys to both YubiKeys while still on disk. For FIDO2, generate a second resident key on the second YubiKey and add it to all authorized_keys files.
  • Encrypted offline backup: For GPG, the exported master_backup.asc is your fallback. Encrypt it with a passphrase using gpg --symmetric and store it on an encrypted offline USB. Rotate if the passphrase is ever exposed.
# Encrypt your backup before storing
gpg --symmetric --cipher-algo AES256 master_backup.asc
# Produces master_backup.asc.gpg — passphrase-protected

Troubleshooting

gpg --card-status returns "No card"

The pcscd service is usually the culprit. Also check that no other process (e.g., a browser with built-in CCID driver) is holding the device.

sudo systemctl restart pcscd
# If on Fedora/RHEL and using Flatpak Firefox, it may grab the CCID interface
# Disable the browser's built-in CCID: about:config → security.webauth.u2f

SSH_AUTH_SOCK not picked up in desktop sessions

Wayland compositors and display managers often start their own ssh-agent. Override it explicitly in your ~/.config/environment.d/gpg-agent.conf (systemd user environment):

mkdir -p ~/.config/environment.d
echo "SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)" \
  > ~/.config/environment.d/gpg-agent.conf
systemctl --user restart gpg-agent.socket 2>/dev/null || gpgconf --kill gpg-agent

FIDO2 key not found after plugging in

# List visible FIDO keys
ykman fido credentials list

# If OpenSSH can't see the token, check udev rules
# Most distros ship these via yubikey-manager or libfido2
sudo apt install libfido2-dev  # Debian/Ubuntu — installs udev rules
sudo dnf install libfido2      # Fedora
sudo pacman -S libfido2        # Arch
# Then: sudo udevadm control --reload-rules && sudo udevadm trigger

Pinentry fails in terminal

Set PINENTRY_USER_DATA or switch the pinentry binary. On headless servers use pinentry-tty; on graphical desktops use pinentry-gnome3 or pinentry-qt.

which pinentry-curses pinentry-tty pinentry-gnome3
# Update gpg-agent.conf pinentry-program to the correct path
gpgconf --kill gpg-agent
tested on:Ubuntu 24.04Fedora 40Arch rollingDebian 12

Frequently asked questions

What happens if my YubiKey is lost or stolen?
An attacker without your PIN cannot use the key. Revoke access immediately by removing your public key from authorized_keys on all servers and, for GPG, publishing a revocation certificate. This is why you generate and store a revocation certificate during key creation.
Can I use the same YubiKey for both GPG and FIDO2 SSH simultaneously?
Yes. The OpenPGP applet and the FIDO2 applet are independent. You can have GPG subkeys on the card and FIDO2 resident SSH keys at the same time, as long as the YubiKey 5 series is used.
What is the difference between ed25519-sk and ecdsa-sk for FIDO2 SSH keys?
ed25519-sk uses EdDSA on Curve25519, which is faster, smaller, and considered stronger. ecdsa-sk uses ECDSA on P-256 and exists mainly for compatibility with older OpenSSH versions. Use ed25519-sk unless a server forces ecdsa-sk.
Will this work on a Wayland desktop without extra configuration?
Mostly yes, but desktop sessions may launch a competing ssh-agent (e.g., GNOME Keyring). You need to override SSH_AUTH_SOCK via systemd user environment.d so gpg-agent wins. The guide covers this in the troubleshooting section.
Can I use a passphrase AND the YubiKey together for defense in depth?
For the OpenPGP applet, the YubiKey PIN acts as the passphrase — it is required before signing or authenticating. For FIDO2 SSH keys, using -O verify-required enforces PIN entry in addition to physical touch, giving you two factors.

Related guides