Privacy Tools Guide

How to Use YubiKey for SSH Authentication: Complete Guide

SSH key management remains one of the weakest links in security infrastructure. Keys are copied between machines, committed to repos accidentally, stored in unencrypted files, or compromised through developer laptops. In 2026, hardware security keys like YubiKey provide a practical solution: authentication happens on the physical device, private keys never leave the hardware, and each login requires physical confirmation.

This guide covers configuring YubiKey for SSH authentication using GPG mode and FIDO2, including key generation, server configuration, and operational best practices.

Why Hardware-Based SSH Keys Matter

Traditional SSH keys have vulnerabilities:

Hardware security keys solve these by:


YubiKey Hardware Options

YubiKey 5 Series

Cost: $45-60

Recommended for most users. Supports:

Best For: Linux engineers, macOS developers, Windows users with backward compatibility needs.


YubiKey 5 Nano

Cost: $50-65

Same functionality as YubiKey 5, smaller form factor (fits in USB slot without extending beyond laptop edge).

Best For: Users wanting minimal visual footprint, frequent travel.


YubiKey 5C

Cost: $55-70

USB-C only (no USB-A adapter needed). Same functionality as YubiKey 5.

Best For: Modern USB-C only laptops (MacBook Pro, newer Linux machines), tablet authentication.


YubiKey 5C NFC

Cost: $65-80

USB-C with NFC support. Authentication via USB-C or near-field communication (iPhone, Android with NFC support).

Best For: Mobile-first workflows, iOS/Android authentication, USB-C devices.


SSH Authentication Modes

YubiKey supports multiple SSH authentication methods:

How it works:

  1. SSH server stores FIDO2 credential
  2. SSH client requests authentication from YubiKey
  3. YubiKey challenges client to prove possession of private key
  4. YubiKey signs challenge with private key (key never leaves device)
  5. SSH server verifies signature using public key

Advantages:

Disadvantages:


Mode 2: GPG-Based SSH

How it works:

  1. YubiKey stores OpenPGP private keys
  2. SSH client uses GPG agent as SSH agent
  3. GPG agent requests sign operation from YubiKey
  4. YubiKey signs authentication request
  5. SSH server verifies signature

Advantages:

Disadvantages:


Mode 3: Backup FIDO2 Keys

Best Practice: Register both FIDO2 primary key AND a backup hardware key on all SSH servers. This prevents lockout if primary key is lost/broken.


Part 1: Setup FIDO2-Based SSH

Step 1: Check OpenSSH Version

ssh -V
# Should be 8.2 or higher
# If lower, upgrade OpenSSH first

Upgrade on macOS:

brew install openssh
# Then update your shell to use brew's ssh
echo /usr/local/bin/ssh >> ~/.zprofile

Upgrade on Ubuntu/Debian:

sudo apt update
sudo apt upgrade openssh-client openssh-server

Step 2: Generate FIDO2 Key on YubiKey

# Generate new ED25519-SK key on YubiKey
# -SK = stored in security key
# -C = comment (your email or identifier)
ssh-keygen -t ed25519-sk \
  -O resident \
  -O verify-required \
  -C "your.email@example.com" \
  -f ~/.ssh/id_ed25519_sk_yubikey

# When prompted:
# - Enter PIN for YubiKey (8+ characters, set if not done)
# - Touch YubiKey when prompted
# - Leave passphrase empty (key is already on hardware)

What this command does:

Output:

Generating public/private ed25519-sk key pair.
You may need to touch your security key to authorize key generation.
[Touch YubiKey]

Enter passphrase (empty for no passphrase): [Press Enter]
Your identification has been saved in ~/.ssh/id_ed25519_sk_yubikey
Your public key has been saved in ~/.ssh/id_ed25519_sk_yubikey.pub

Step 3: Verify Key Was Created

cat ~/.ssh/id_ed25519_sk_yubikey.pub
# Output should look like:
# ssh-ed25519-cert-v01@openssh.com AAAAC3NzaC1lZDI...

ssh-keygen -l -f ~/.ssh/id_ed25519_sk_yubikey
# Verify fingerprint

Step 4: Add Public Key to SSH Server

# Option A: Use ssh-copy-id
ssh-copy-id -i ~/.ssh/id_ed25519_sk_yubikey user@server.example.com

# Option B: Manual append
cat ~/.ssh/id_ed25519_sk_yubikey.pub | \
  ssh user@server.example.com \
  "cat >> ~/.ssh/authorized_keys"

# Verify
ssh -i ~/.ssh/id_ed25519_sk_yubikey user@server.example.com
# [Touch YubiKey]
# [Touch YubiKey again for confirmation, if verify-required set]
# Welcome to server.example.com

Step 5: Configure SSH Client (Optional)

For convenience, configure SSH to automatically use YubiKey:

# Edit ~/.ssh/config
Host *.example.com
  HostName %h
  User your_username
  IdentityFile ~/.ssh/id_ed25519_sk_yubikey
  IdentitiesOnly yes
  PubkeyAuthentication yes

# Now you can ssh user@server.example.com
# and it automatically uses YubiKey

Step 6: Test and Verify

ssh user@server.example.com
# [1] Touch YubiKey to authenticate
# [2] If verify-required: Touch again to confirm
# [3] SSH session established

# View server logs to confirm
ssh user@server.example.com "tail -20 /var/log/auth.log | grep sshd"
# Should show: sshd: Accepted publickey for user from X.X.X.X

Part 2: Backup Key Strategy

Register Backup YubiKey

Best practice: Always have a backup hardware key (same model or different brand).

Generate on second YubiKey:

ssh-keygen -t ed25519-sk \
  -O resident \
  -O verify-required \
  -C "your.email@example.com (backup)" \
  -f ~/.ssh/id_ed25519_sk_yubikey_backup

# This creates a different key on the backup YubiKey

Add backup key to servers:

cat ~/.ssh/id_ed25519_sk_yubikey_backup.pub | \
  ssh user@server.example.com \
  "cat >> ~/.ssh/authorized_keys"

Verify backup works:

# Unplug primary YubiKey
ssh -i ~/.ssh/id_ed25519_sk_yubikey_backup \
  user@server.example.com
# [Touch backup YubiKey]
# Should successfully authenticate

Emergency Recovery Key

Also maintain one traditional SSH key in secure storage for complete lockout recovery:

# Generate traditional SSH key (no hardware)
ssh-keygen -t ed25519 \
  -C "your.email@example.com (emergency)" \
  -f ~/.ssh/id_ed25519_emergency

# Encrypt it
openssl enc -aes-256-cbc -in ~/.ssh/id_ed25519_emergency \
  -out ~/.ssh/id_ed25519_emergency.enc
rm ~/.ssh/id_ed25519_emergency

# Store in password manager or offline storage
# Add public key to servers:
cat ~/.ssh/id_ed25519_emergency.pub | \
  ssh user@server.example.com \
  "cat >> ~/.ssh/authorized_keys"

Retrieve emergency key only when needed:

openssl dec -aes-256-cbc -in ~/.ssh/id_ed25519_emergency.enc \
  -out /tmp/id_ed25519_emergency
ssh -i /tmp/id_ed25519_emergency user@server.example.com
# Use it
rm /tmp/id_ed25519_emergency

Part 3: GPG-Based SSH (Alternative)

For users who also use GPG for signing commits and email encryption:

Step 1: Initialize YubiKey for GPG

# Install GPG
brew install gnupg  # macOS
# or apt install gnupg2  # Ubuntu

# Check YubiKey is recognized
gpg --card-status
# Should show: Name, Serial Number, PIN status, etc.

# Change PIN (default: 123456)
gpg --card-edit
# Type: admin
# Type: passwd
# Change User PIN: option 1
# Change Admin PIN: option 3
# quit

Step 2: Generate Master Key

gpg --full-generate-key
# Select: RSA and RSA
# Key size: 4096
# Expiry: 1 year
# Name: Your Name
# Email: your.email@example.com

# Copy key ID (40-char hex string)
gpg --list-keys --keyid-format long
# Key ID is shown as: rsa4096/XXXXXXXXXXXXXXXX 2026-03-21

Step 3: Generate Subkeys on YubiKey

gpg --edit-key YOUR_KEY_ID
# Type: addkey
# Select: RSA (4096)
# For key usage: Encryption
# Repeat for another RSA subkey with Sign usage
# Repeat for RSA (3072) for Authentication usage
# Type: save

Step 4: Configure GPG for SSH

# Enable SSH support in gpg-agent
# Edit ~/.gnupg/gpg-agent.conf
enable-ssh-support
default-cache-ttl 600
max-cache-ttl 7200

# Configure GPG SSH key
gpg --export-ssh-key YOUR_KEY_ID >> ~/.ssh/authorized_keys

Step 5: Test GPG-Based SSH

# Start gpg-agent
gpg-connect-agent updatestartuptty /bye

# Test SSH with GPG authentication
ssh-add -l
# Should list your GPG key

# SSH to server
ssh user@server.example.com
# [Enter GPG PIN]
# [Touch YubiKey]
# Welcome to server.example.com

Server-Side Configuration

Best Practice: Force Key-Based Auth Only

On servers, disable password authentication:

# Edit /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
MaxSessions 5

# Restart SSH daemon
sudo systemctl restart sshd

# Test before disconnecting!
# In another terminal:
ssh user@server.example.com

Audit Logging

Enable detailed SSH logging:

# In /etc/ssh/sshd_config
LogLevel VERBOSE

# View logs
tail -f /var/log/auth.log | grep sshd
# Shows: sshd: Accepted publickey for user from 203.0.113.45 port 54321

Certificate-Based SSH (Advanced)

For large teams, use SSH certificates instead of individually managed keys:

# Generate CA key on secure machine
ssh-keygen -t ed25519 -C "our-org-ca" -N "" -f our_org_ca

# On YubiKey, generate signing key
ssh-keygen -t ed25519-sk -O resident -f user_key

# Authority signs YubiKey public key
ssh-keygen -s our_org_ca -n user -V +52w user_key.pub

# Result: user_key-cert.pub (certificate)
# No need to add individual keys to authorized_keys
# Just trust the CA: echo 'cert-authority ... ' >> authorized_keys

Troubleshooting

Issue: “Agent refused operation”

# Problem: gpg-agent not running or socket error
# Solution: Restart agent
gpg-connect-agent killagent /bye
gpg-connect-agent /bye

# Or check if agent socket exists
ls -la ~/.gnupg/S.gpg-agent*

Issue: “No such file or directory” on SSH

# Problem: SSH key file permissions wrong
# Solution: Fix permissions
chmod 600 ~/.ssh/id_ed25519_sk_yubikey
chmod 644 ~/.ssh/id_ed25519_sk_yubikey.pub

Issue: “Permission denied (publickey)”

# Problem: YubiKey key not registered on server
# Solution: Verify key is in authorized_keys
ssh user@server.example.com cat ~/.ssh/authorized_keys | grep ed25519

# If not present, add it:
cat ~/.ssh/id_ed25519_sk_yubikey.pub | \
  ssh user@server.example.com "cat >> ~/.ssh/authorized_keys"

Issue: YubiKey Not Recognized

# Check if YubiKey is visible
lsusb | grep Yubico
# or on macOS
system_profiler SPUSBDataType | grep -i yubi

# Reinstall udev rules (Linux)
sudo wget https://raw.githubusercontent.com/Yubico/yubikey-manager/main/libudev/70-u2f.rules \
  -O /etc/udev/rules.d/70-u2f.rules
sudo udevadm control --reload-rules

Security Best Practices

1. Use PIN Protection

# Always set a strong PIN
gpg --card-edit
# admin
# passwd
# option 1: Change User PIN
# Set 8-12 character PIN

2. Use Resident Credentials

# Store key on YubiKey itself
ssh-keygen -t ed25519-sk -O resident ...
# This allows using YubiKey on any machine

3. Require Physical Touch

# Add verify-required flag
ssh-keygen -t ed25519-sk -O verify-required ...
# User must physically touch YubiKey twice per auth

4. Short Lived Credentials

# Use SSH certificates with short validity
ssh-keygen -s ca_key -V +8h -n user signing_key.pub
# Certificate valid for 8 hours only

5. Rotate Keys Periodically

# Generate new key yearly
ssh-keygen -t ed25519-sk -O resident -O verify-required \
  -C "your.email@example.com (2026)" \
  -f ~/.ssh/id_ed25519_sk_2026

# Add to servers
# Remove old key from authorized_keys when confident

6. Monitor SSH Usage

# Set up alerts for unusual SSH patterns
grep "Accepted publickey" /var/log/auth.log | \
  awk '{print $1, $2, $3, $11}' | \
  uniq -c | sort -rn

# Investigate any unexpected users/IPs


Built by theluckystrike — More at zovo.one