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:
- Keys stored on disk as files (vulnerable to malware)
- SSH agent holds keys in memory (vulnerable to key extraction)
- Copy keys between machines (increases exposure)
- Accidental commits (keys exposed in version control)
- No audit trail (who logged in, when?)
Hardware security keys solve these by:
- Private keys stored on tamper-resistant chip (never exported)
- Physical presence required for authentication (prevents remote compromise)
- Key derivation per-operation (no fixed key material to steal)
- Audit logs on server (who logged in from where, when)
- Resistant to phishing (even if attacker has password)
YubiKey Hardware Options
YubiKey 5 Series
Cost: $45-60
Recommended for most users. Supports:
- FIDO2 (U2F)
- FIDO U2F
- Yubico OTP
- Smart Card (CCID with OpenPGP)
- Supports USB-A only (need USB-C adapter for modern laptops)
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:
Mode 1: FIDO2 (Recommended for SSH)
How it works:
- SSH server stores FIDO2 credential
- SSH client requests authentication from YubiKey
- YubiKey challenges client to prove possession of private key
- YubiKey signs challenge with private key (key never leaves device)
- SSH server verifies signature using public key
Advantages:
- No PIN required (modern approach)
- Resistant to phishing
- Works with OpenSSH 8.2+
- Resident credentials (key stored on YubiKey itself)
Disadvantages:
- Requires recent OpenSSH version
- Older servers may not support FIDO2
- Requires compatible SSH client
Mode 2: GPG-Based SSH
How it works:
- YubiKey stores OpenPGP private keys
- SSH client uses GPG agent as SSH agent
- GPG agent requests sign operation from YubiKey
- YubiKey signs authentication request
- SSH server verifies signature
Advantages:
- Works with very old SSH servers
- Single key for both GPG and SSH
- PIN-protected (can’t sign without PIN)
- Good for hybrid GPG+SSH setups
Disadvantages:
- More complex setup
- Requires GPG/gpg-agent configuration
- Slower (additional agent layer)
- Less modern than FIDO2
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:
-t ed25519-sk: ED25519 key, stored on security key-O resident: Store credential on YubiKey (not just reference)-O verify-required: Require physical touch before each use-f ~/.ssh/id_ed25519_sk_yubikey: Save to file
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
Related Reading
- SSH Hardening Best Practices
- GPG and Email Encryption Guide
- Hardware Security Keys Comparison
- Secrets Management for DevOps
Built by theluckystrike — More at zovo.one