A normal SSH private key is a file. Malware, a stolen laptop backup or a careless copy can take it. With a hardware security key (YubiKey, SoloKey, Nitrokey, Google Titan or any FIDO2 key), the private key never leaves the device. Every login needs the physical key plugged in, and usually a touch. OpenSSH supports this natively since version 8.2.
Requirements
- Client: OpenSSH 8.2+ (Linux, macOS 13+ with the system OpenSSH or Homebrew, Windows OpenSSH 8.9+ or WSL), with
libfido2. - Server: OpenSSH 8.2+ (Ubuntu 20.04+, Debian 11+, RHEL 9 and every current distribution).
- Key: any FIDO2/U2F security key. Ed25519 support needs FIDO2 firmware with Ed25519 (most current keys). Otherwise use ECDSA.
ssh -V
1. Create a hardware-backed key
Plug in the security key, then run:
ssh-keygen -t ed25519-sk -C "alice yubikey"
# fallback for keys without Ed25519: ssh-keygen -t ecdsa-sk
Touch the key when it blinks. This creates:
~/.ssh/id_ed25519_sk— not the real private key, but a “key handle” that only works together with the physical device;~/.ssh/id_ed25519_sk.pub— the public key, which goes on servers as usual.
Even if someone copies the id_ed25519_sk file, it’s useless without your hardware key.
Options
ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:work -C "alice yubikey"
-O resident— store the key handle on the device. On a new computer, restore it withssh-keygen -K. That’s handy, but anyone with the device (and its PIN) can also extract the handle.-O verify-required— require the FIDO2 PIN as well as touch. That gives two factors: something you have plus something you know.-O no-touch-required— skip the touch, for automation. This weakens protection, and the server must allow it explicitly.
2. Install the public key on servers
ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub user@server
Or append the .pub content to ~/.ssh/authorized_keys on the server. Then log in:
ssh user@server
# Confirm user presence for key ED25519-SK … → touch the key
3. Have a backup key
If your only security key is lost or broken, you’re locked out. Register at least two hardware keys: create an -sk key with each, and put both public keys on every server. Keep the backup key somewhere safe.
Keep an emergency path too: your provider’s VNC console, plus a strong password for console login.
4. Optionally require hardware keys on the server
To allow only security-key logins for certain users, restrict the accepted key types:
# /etc/ssh/sshd_config.d/30-sk.conf
Match User admin
PubkeyAcceptedAlgorithms [email protected],[email protected]
Require PIN verification for keys in authorized_keys:
verify-required [email protected] AAAA… alice yubikey
Reload sshd, and test in a new session before closing the old one.
Using a new computer: resident keys
If you created the key with -O resident:
cd ~/.ssh
ssh-keygen -K # downloads key handles from the device (asks for PIN)
mv id_ed25519_sk_rk_* id_ed25519_sk # rename if you like
Git and other tools
- GitHub and GitLab accept
sk-ssh-ed25519keys for Git over SSH. Add the.pubas usual, and every push or pull needs a touch. See Git SSH keys. - Git commit signing with SSH keys works with
-skkeys too. - Agent forwarding works, but every use requires a touch on your device, which is a nice safety property.
Alternative: YubiKey PIV or OpenPGP
Before FIDO2 support, YubiKeys were used for SSH through PIV (PKCS#11) or the OpenPGP applet with gpg-agent. These still work, and support RSA keys for very old servers, but setup is more involved. For any OpenSSH 8.2+ server, -sk keys are simpler.
Troubleshooting
Key enrollment failed: requested feature not supported— the key’s firmware lacks Ed25519. Useecdsa-sk.No FIDO SecurityKeyProvider specified, or the device isn’t found — libfido2 is missing, or (on Linux) udev rules don’t give your user access. Installlibfido2-1or your distribution’s yubikey udev package. In WSL, USB pass-through needs extra setup.- The server rejects the key — the server’s OpenSSH is older than 8.2, or the algorithm isn’t allowed. Check
ssh -voutput. - Touch prompt doesn’t appear in GUI tools — use a terminal, or configure
SSH_ASKPASS.
Quick reference
| Task | Command |
|---|---|
| Create key | ssh-keygen -t ed25519-sk |
| With PIN + resident | -O verify-required -O resident |
| Install on server | ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub user@host |
| Restore resident keys | ssh-keygen -K |
| Require PIN (server) | verify-required option in authorized_keys |
Related: SSH certificates and SSH two-factor authentication.