This guide covers setting up FIDO2 SSH keys on YubiKeys (primary + backup) for GitHub, generating them on Windows and using them from WSL2.
Why generate on Windows? WSL2 runs in a lightweight VM without direct USB access. USB passthrough via usbipd-win is unreliable for FIDO2 devices and disables the YubiKey on the Windows host while attached. The Windows OpenSSH client has direct USB access, so we generate keys there and copy the resulting files into WSL2.
1. Enable FIDO2 Support in WSL
WSL does not directly access USB security keys. Instead, we reuse the Windows OpenSSH FIDO2 helper.
Add this to your ~/.bashrc:
# FIDO2 Support (use Windows OpenSSH security key helper)
export SSH_SK_HELPER="/mnt/c/Windows/System32/OpenSSH/ssh-sk-helper.exe"
Reload your shell:
source ~/.bashrc
This tells the SSH client in WSL to use the Windows FIDO2 handler (ssh-sk-helper.exe) to communicate with your hardware key.
2. Install Updated OpenSSH on Windows
Bugfix for A resident keyscoped to 'ssh:' with user id 'null' already exists.
The built-in Windows OpenSSH has a bug where generating multiple resident keys with the same application string silently overwrites the first key on the device (PowerShell/Win32-OpenSSH#2394). Install Win32-OpenSSH v10.0.0.0p2-Preview or later to fix this.
-
Download
OpenSSH-Win64-v10.x.x.x.msifrom: https://github.com/PowerShell/Win32-OpenSSH/releases -
Open an Administrator PowerShell and run:
msiexec /i OpenSSH-Win64-v10.x.x.x.msi
Reference: Upgrade in-box OpenSSH to the latest release
3. Generate SSH Keys on Windows
Open an Administrator PowerShell. You will create two keys — one for each YubiKey.
Primary YubiKey
Plug in your primary YubiKey, then run:
ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:github.com -O user=primary -C "primary-yubikey" -f .ssh\id_ed25519_sk_primary
You will be prompted for your YubiKey PIN and a physical touch.
Backup YubiKey
Unplug the primary, plug in your backup YubiKey, then run:
ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:github.com -O user=backup -C "backup-yubikey" -f .ssh\id_ed25519_sk_backup
Explanation of Flags
-t ed25519-sk— Ed25519 key backed by a FIDO2 security key (-sk).-O resident— Store the key as a discoverable (resident) credential on the YubiKey, allowing recovery later.-O verify-required— Require PIN or biometric verification before every use.-O application=ssh:github.com— Namespace the credential. Both keys share the same application string but are distinguished by-O user.-O user=primary/-O user=backup— Differentiates the two resident credentials on the device so they don't collide.-C "primary-yubikey"— Comment embedded in the public key (metadata only, helps identify which key is which).-f .ssh\id_ed25519_sk_primary— Output file path. Without this, both keys would default to the same filename and overwrite each other.
4. Copy Keys to WSL2
From WSL2, copy the four files (two private key stubs + two public keys) from your Windows home directory:
cp /mnt/c/Users/<WindowsUser>/.ssh/id_ed25519_sk_primary ~/.ssh/
cp /mnt/c/Users/<WindowsUser>/.ssh/id_ed25519_sk_primary.pub ~/.ssh/
cp /mnt/c/Users/<WindowsUser>/.ssh/id_ed25519_sk_backup ~/.ssh/
cp /mnt/c/Users/<WindowsUser>/.ssh/id_ed25519_sk_backup.pub ~/.ssh/
Fix permissions:
chmod 600 ~/.ssh/id_ed25519_sk_primary ~/.ssh/id_ed25519_sk_backup
chmod 644 ~/.ssh/id_ed25519_sk_primary.pub ~/.ssh/id_ed25519_sk_backup.pub
5. Configure SSH
Add the following to ~/.ssh/config:
Host github.com
IdentityFile ~/.ssh/id_ed25519_sk_primary
IdentityFile ~/.ssh/id_ed25519_sk_backup
IdentitiesOnly yes
User git
SSH tries keys in the order they appear in the config file. Put the YubiKey you use most often first — SSH will try it before falling back to the other.
6. Add Keys to GitHub
- Go to GitHub → Settings → SSH and GPG Keys.
- Click New SSH key.
- Paste the contents of
~/.ssh/id_ed25519_sk_primary.puband give it a recognizable title (e.g. "Primary YubiKey"). - Repeat for
~/.ssh/id_ed25519_sk_backup.pub(e.g. "Backup YubiKey").
Both keys are now authorized for your GitHub account.
7. Test
ssh -T git@github.com
SSH will try each key in config file order. Plug in the corresponding YubiKey — you will be prompted for a touch. On success:
Hi <username>! You've successfully authenticated, but GitHub does not provide shell access.
8. Important: The Private Key File Is Not the Real Private Key
After generation you will see files like:
~/.ssh/id_ed25519_sk_primary
~/.ssh/id_ed25519_sk_primary.pub
~/.ssh/id_ed25519_sk_backup
~/.ssh/id_ed25519_sk_backup.pub
The files id_ed25519_sk_primary and id_ed25519_sk_backup do not contain actual private keys.
The real private keys:
- Are generated inside the FIDO2 device
- Never leave the hardware
- Cannot be extracted
The files on disk only contain:
- A key handle
- Metadata
- A reference to the hardware key
Without the physical YubiKey (and your PIN), authentication is impossible. These stub files are safe to back up — they are useless without the corresponding YubiKey.
9. Recovery
If you lose the stub private key or public key files, you can recover them from the YubiKey.
Plug in the YubiKey and run in Administrator PowerShell:
ssh-keygen -K
This downloads all resident credentials from the connected YubiKey into the current directory. Copy the recovered files back into WSL2's ~/.ssh/ as described in Section 4.