A FIDO2 SSH key on a YubiKey is one command:
ssh-keygen -t ed25519-skThe -sk suffix is the whole story: it stands for "security key". When you run that, OpenSSH talks to the YubiKey, the key's LED starts blinking, you tap it, and you end up with a keypair where the private half physically cannot leave the hardware. What lands in ~/.ssh is not the private key, it is a handle that is useless without the YubiKey plugged in. Steal the laptop, copy the file, and you still cannot authenticate. That is the entire point: the secret lives in a chip you can pull out of the USB port.
This needs OpenSSH 8.2 or newer on both ends and a FIDO2 key. I cover the version traps below, because they are where people actually get stuck.
Generate the key
Plug in the YubiKey first, then run:
ssh-keygen -t ed25519-sk -C "you@example.com"A real run looks like this:
Generating public/private ed25519-sk key pair.
You may need to touch your authenticator to authorize key generation.
Enter file in which to save the key (/home/techearl/.ssh/id_ed25519_sk):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/techearl/.ssh/id_ed25519_sk
Your public key has been saved in /home/techearl/.ssh/id_ed25519_sk.pub
Between "touch your authenticator" and the saved paths, the YubiKey blinks and waits for a physical tap. No tap, no key. That tap is user presence: proof a human is at the machine, not a script.
You now have two files, but they are not what ssh-keygen normally produces. id_ed25519_sk is a key handle plus metadata, not a usable private key on its own. id_ed25519_sk.pub is the public key you install on servers, exactly like any other public key. The crucial difference from a software key: copying id_ed25519_sk to another machine gets an attacker nothing, because the actual private scalar never left the YubiKey's secure element.
Run ssh-keygen as your normal user. It writes to your own ~/.ssh, so no sudo. (Using sudo here makes the handle root-owned and unreadable to you, same mistake as with a regular ssh-keygen run.)
ed25519-sk or ecdsa-sk?
Two algorithms carry the -sk suffix. Pick ed25519-sk if your YubiKey supports it, fall back to ecdsa-sk if it does not.
ssh-keygen -t ed25519-sk # preferred
ssh-keygen -t ecdsa-sk # fallback for older keysThe catch is firmware. ed25519-sk needs a YubiKey on firmware 5.2.3 or later. Anything older only does ecdsa-sk. Check your firmware with:
ykman infoIf ssh-keygen -t ed25519-sk returns Key enrollment failed: requested feature not supported, that is almost always old firmware, and ecdsa-sk will work instead. YubiKey firmware is burned in at the factory and cannot be upgraded, so an older key is stuck on ecdsa-sk for life. That is not a real security problem; it is just a less modern curve.
| Algorithm | Curve | YubiKey firmware | When to pick it |
|---|---|---|---|
ed25519-sk | Ed25519 | 5.2.3 or later | Default. Modern curve, short keys, the one I reach for. |
ecdsa-sk | NIST P-256 | 5.1.x and later | Only when the key's firmware is too old for ed25519-sk. |
Both require OpenSSH 8.2+ on the client that generates and uses the key, and on the server you log in to (the server has to understand the sk key type in authorized_keys).
One trap that catches people on minimal Linux installs: OpenSSH talks to the YubiKey through a middleware library, libfido2, and the ssh-sk-helper binary that ships with OpenSSH. If ssh-keygen -t ed25519-sk fails with something like Couldn't get key types: error in libcrypto or a flat "provider" error even though ssh -V shows 8.2+, you are usually missing libfido2 (sudo apt install libfido2-1 on Debian/Ubuntu, libfido2 on Fedora/Arch). Distro-packaged OpenSSH normally pulls it in; hand-built or stripped-down images often do not.
Resident keys: surviving a fresh laptop
By default the handle file matters: lose id_ed25519_sk and you cannot use the key even with the YubiKey, because the handle is half of what is needed. A resident (discoverable) key changes that. It stores the handle on the YubiKey itself, so you can walk up to any machine, plug the key in, and pull the credential back down.
ssh-keygen -t ed25519-sk -O residentOn a fresh machine, retrieve the resident keys straight off the hardware:
ssh-keygen -KThat writes id_ed25519_sk_rk and its .pub into the current directory. The trade-off is real: resident keys consume one of the YubiKey's limited FIDO2 credential slots, and anyone who has both the unlocked YubiKey and (if set) its FIDO2 PIN can extract them. They also need firmware 5.2.3 or later; older 5.1.x keys do FIDO2 but not discoverable credentials. For most people the non-resident default is the right call. Reach for -O resident only when "I need to log in from a machine that has never seen my key handle" is a genuine requirement, for example a recovery scenario or a shared jump box.
Useful generation options
ssh-keygen exposes the FIDO2 options through -O:
| Option | Effect |
|---|---|
-O resident | Store the handle on the key itself (discoverable). |
-O verify-required | Require a PIN (or biometric) in addition to the touch. |
-O no-touch-required | Drop the touch requirement (weakens the guarantee; most servers reject it). |
-O application=ssh:work | Tag the credential with a custom application string to keep multiple keys apart. |
-O verify-required is the one I add for anything sensitive. It means a stolen-and-plugged-in YubiKey is not enough; the attacker also needs the FIDO2 PIN. The server can enforce it too, with verify-required in authorized_keys, so the requirement is checked on both ends. Note that this option needs OpenSSH 8.3 or newer (it landed one release after the base sk key types), and a YubiKey on firmware 5.2.3 or later; firmware 5.1.x does not support it.
Install the public key and log in
Identical to any other SSH key. Append id_ed25519_sk.pub to the server's authorized_keys:
ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub user@serverThen connect. The difference shows up at login: the YubiKey blinks and SSH waits for your tap before it will authenticate.
ssh user@server
# Confirm user presence for key ED25519-SK ...
# (LED blinks, tap the YubiKey)A few things to keep straight once it works:
- The handle file (
id_ed25519_sk) still needs600permissions like any private key, even though it is not secret on its own. SSH checks the mode regardless. - This pairs well with disabling password authentication on the server: once a hardware key is your login, password auth is pure attack surface.
- The passphrase prompt during generation encrypts the handle file, not the YubiKey secret. You can change or remove that passphrase later with
ssh-keygen -p, exactly as with a software key.
When not to bother
Hardware keys are not free in friction, and there are spots where a plain Ed25519 software key is the better tool:
- Unattended automation. CI runners, cron jobs, and deploy bots cannot tap a YubiKey. A FIDO2 key requiring user presence is the wrong choice for anything that must authenticate without a human present. Use a scoped software key (or short-lived certificates) there.
- Servers older than OpenSSH 8.2. The server side has to understand
skkey types. A box stuck on an ancient OpenSSH will reject the public key outright. Checkssh -Von the server before committing. - You only have one key. A single YubiKey is a single point of failure: lose it and you are locked out of everything that trusts it. Enroll a backup key on every account from day one, the same way you would with any FIDO2 setup.
For everyday interactive logins from a laptop you carry around, though, a FIDO2 SSH key is the strongest practical setup: the private key is in silicon you control, and every login needs a deliberate physical tap.
See also
- How to create an SSH key: the software-key baseline, picking Ed25519 over RSA, passphrases, and the file permissions SSH insists on.
- Harden sshd by disabling password authentication: the natural next step once a hardware key is your login, so passwords stop being an attack surface.
- Change an SSH key passphrase: how
ssh-keygen -pworks, which also covers the passphrase on the FIDO2 handle file.
Sources
Authoritative references this article was fact-checked against.
- ssh-keygen(1) manual page (OpenBSD)man.openbsd.org
- OpenSSH 8.2 release notes (FIDO/U2F support)openssh.com
- Securing SSH with FIDO2 (Yubico Developers)developers.yubico.com





