Using a YubiKey 5C for SSH and Git Authentication on Linux
Your SSH private key sits on your disk, protected by (hopefully) a passphrase. But passphrase or not, it's still a file — and files can be stolen, copied, or exfiltrated by malware. A YubiKey changes the equation entirely: the private key lives on the hardware token and can never be extracted.
This guide walks through setting up a YubiKey 5C for SSH authentication and Git operations using the FIDO2 ed25519-sk key type, including commit signing and a practical dual-key setup for when your YubiKey isn't at hand.
Prerequisites
- A YubiKey 5C (which supports FIDO2 and
ed25519-sk) - OpenSSH
- libfido2 installed on your system
# On Fedora:
sudo dnf install -y openssh libfido2
# On Debian/Ubuntu
sudo apt install -y openssh-client libfido2-1Install libfido2
How It Works
Unlike traditional SSH keys where the private key is a file on disk, FIDO2-backed SSH keys work differently. When you generate an ed25519-sk key, the private key is created and stored inside the YubiKey. The file saved to your ~/.ssh/ directory is just a "key handle" — a reference that tells SSH to ask the YubiKey to perform the signing operation.
This means that even if someone copies your key handle file, it's useless without the physical YubiKey.
Step 1: Set a FIDO2 PIN
If you haven't already, set a PIN on your YubiKey's FIDO2 application. You can do this with the YubiKey Manager:
# On Fedora
sudo dnf install -y yubikey-manager
# Other distros
# check https://www.yubico.com/support/download/yubikey-manager/
# Let's create the PIN if you haven't already
ykman fido access change-pin
Set the PIN
This PIN will be required when generating keys and (if configured) when authenticating.
Step 2: Generate a FIDO2-backed SSH Key
Plug in your YubiKey and run:
ssh-keygen -t ed25519-sk \
-O resident \
-O verify-required \
-O application=ssh:work \
-C "yubikey-5c" \
-f ~/.ssh/id_ed25519_sk
generate the FIDO2 private key
Breaking down the flags:
-t ed25519-sk— generate an Ed25519 key backed by a security key-O resident— store the key handle on the YubiKey itself, making it portable across machines-O verify-required— require PIN verification on every use (not just physical touch)-O application=ssh=<name>— the passkey name inside the Yubikey. The Yubikey 5C supports up to 100 different passkeys-C "yubikey-5c"— a comment to identify this key-f ~/.ssh/id_ed25519_sk— output file path
You'll be prompted for your FIDO2 PIN and asked to touch the YubiKey.
This creates two files:
~/.ssh/id_ed25519_sk— the key handle (not the actual private key)~/.ssh/id_ed25519_sk.pub— the public key
Step 3: Add Your Public Key to GitHub / GitLab
Copy the public key:
cat ~/.ssh/id_ed25519_sk.pub
check your public key
Then add it to your Git host:
- GitHub: Settings → SSH and GPG keys → New SSH key
- GitLab: Preferences → SSH Keys
In this post, I'll consider that you will test it using GitHub but the same would apply to any other SSH connection.
Test the connection:
SSH_AUTH_SOCK="" ssh -vvv -i ~/.ssh/id_ed25519_sk -o IdentitiesOnly=yes -T git@github.com 2>&1 | grep -i "offering\|authenticated"
# Insider your PIN
# Touch the Yubikey to confirm
# and now you may see something like:
>> debug1: Offering public key: /home/user/.ssh/id_ed25519_sk ED25519-SK SHA256:xj/123456789qwertyuiosdfghjklxcvbnm explicit authenticator
>> Enter PIN for ED25519-SK key /home/user/.ssh/id_ed25519_sk:
>> Authenticated to github.com ([1.2.3.4]:22) using "publickey".
>> Hi thenets! You've successfully authenticated, but GitHub does not provide shell access.
test auth
Your YubiKey should prompt for a PIN and a touch. You should see a welcome message confirming authentication.
Step 4: Configure Gnome Keyring (maybe optional)
Note: I don't know what is needed for KDE. Maybe you will need a similar fix for it.
Check if you need this fix:
# Try to authenticate without any extra param
ssh -T git@github.com
# PROBLEM if you get the following, you need to continue with the step 4
sign_and_send_pubkey: signing failed for ED25519-SK "/home/user/.ssh/id_ed25519_sk" from agent: agent refused operation
git@github.com: Permission denied (publickey).
check if you need to fix the desktop env keyring
The problem is that GNOME Keyring's agent doesn't understand FIDO2/ed25519-sk keys at all. It intercepts the request, doesn't know what to do with it, and refuses the operation.
echo $XDG_CURRENT_DESKTOP
# this must return `gnome`make sure you are running Gnome
mkdir -p ~/.config/autostart
cp /etc/xdg/autostart/gnome-keyring-ssh.desktop ~/.config/autostart/
echo "Hidden=true" >> ~/.config/autostart/gnome-keyring-ssh.desktopdisable Gnome SSH keyring agent
Now, check if it worked. Log out of your desktop session and log back in (not just close the terminal — a full logout/login). After that, verify:
ssh -i ~/.ssh/id_ed25519_sk -T git@github.com
# If it worked, you will need to
# Add your PIN
# Touch the Yubikey to confirm your presencetest without the -o IdentitiesOnly=yes param
Step 5: Sign Your Git Commits
Git supports signing commits with SSH keys. Combined with a FIDO2-backed key, this gives you hardware-bound proof of authorship for every commit.
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519_sk.pub
git config --global commit.gpgsign true
To also sign tags by default:
git config --global tag.gpgsign true
Upload the same public key to GitHub under SSH and GPG keys, but this time select Signing Key as the key type. Commits signed with this key will show as "Verified" on GitHub.
For local verification, set up an allowed signers file:
echo "$(git config --get user.email) $(cat ~/.ssh/id_ed25519_sk.pub)" >> ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
Now you can verify commits locally with git log --show-signature.
Dual-Key Setup: YubiKey + Traditional Key Fallback
A practical concern: what happens when your YubiKey is at the office but you're on the couch with your laptop? The solution is to register two SSH keys with your Git host.
Generate a traditional key as a fallback:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "laptop-fallback"
# set a strong passphrase when prompted
Add this second public key to GitHub as well, then configure ~/.ssh/config to try both:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_sk
IdentityFile ~/.ssh/id_ed25519
~/.ssh/config
SSH tries keys top-down. If the YubiKey is plugged in, the FIDO2 key is used (PIN + touch). If it's not present, SSH falls through to the traditional key (passphrase).
To avoid retyping the passphrase for the fallback key, let ssh-agent cache it once per session:
ssh-add ~/.ssh/id_ed25519 # prompts for passphrase once
On GNOME or KDE desktops, the system keyring typically handles this automatically after your first login.
| Scenario | Key used | Experience |
|---|---|---|
| YubiKey plugged in | ed25519-sk |
PIN + touch per operation |
| YubiKey not available | ed25519 |
Passphrase (once per session via agent) |
Portable Keys: Using Your YubiKey on a New Machine
Because we generated the key with -O resident, the key handle is stored on the YubiKey itself. On a new machine, you can pull it down without any file transfer:
cd ~/.ssh
ssh-keygen -K
This downloads the resident key handles from the YubiKey. Rename the files if needed and you're ready to go — no copying files between machines.
Troubleshooting
"Key enrollment failed: requested feature not supported" Your YubiKey firmware is too old for ed25519-sk. Check with ykman info. If below 5.2.3, use ecdsa-sk instead:
ssh-keygen -t ecdsa-sk -O resident -f ~/.ssh/id_ecdsa_sk
SSH silently falls through to the wrong key Use ssh -vvv git@github.com to see which keys are being tried and why they fail.
"Permission denied" after adding key to GitHub Make sure the public key on GitHub matches exactly. Re-copy it from ~/.ssh/id_ed25519_sk.pub.
Security Considerations
- Always register a backup YubiKey. If you lose your only key with no fallback, you lose access to every service tied to it.
- Treat the FIDO2 PIN seriously. It's the knowledge factor that pairs with the physical possession factor.
- Avoid
no-touch-required. It defeats the purpose of the hardware key by removing the presence check. - Agent forwarding (
ssh -A) is risky. It exposes your key's usability to remote hosts. Avoid it unless absolutely necessary.
References
- Yubico — Securing SSH with FIDO2 https://developers.yubico.com/SSH/Securing_SSH_with_FIDO2.html
- Yubico — Git Commit Signing with YubiKey and SSH/FIDO2 https://developers.yubico.com/SSH/Securing_git_with_SSH_and_FIDO2.html
- Yubico — Securing SSH with the YubiKey (overview) https://developers.yubico.com/SSH/
- Yubico Blog — GitHub Now Supports SSH Security Keys https://www.yubico.com/blog/github-now-supports-ssh-security-keys/
- Fedora Magazine — How to use a YubiKey with Fedora Linux https://fedoramagazine.org/how-to-use-a-yubikey-with-fedora-linux/
- Fedora Magazine — Use FIDO U2F security keys with Fedora Linux https://fedoramagazine.org/use-fido-u2f-security-keys-with-fedora-linux/
- nixCraft — How To Set Up SSH Keys With YubiKey as 2FA (U2F/FIDO2) https://www.cyberciti.biz/security/how-to-set-up-ssh-keys-with-yubikey-as-two-factor-authentication-u2f-fido2/