This guide explains how to use Windows Hello (backed by a TPM 2.0) or a YubiKey for SSH authentication and Git commit signing on a Windows machine, including integration with WSL2 and VS Code DevContainers.
Table of Contents:
- Prerequisites
- Key Generation
- Setting up Git Signing with SSH
- Using with WSL2
- Using with VS Code DevContainers
- Importing YubiKey Discoverable Keys on a New Machine
- Troubleshooting
- OpenSSH Client: Version 8.3 or later (check with
ssh -V). - YubiKey: A FIDO2-compliant YubiKey
- Windows Machine with TPM 2.0: For Windows Hello integration, you need a TPM 2.0 chip and Windows Hello set up
- Git: Version 2.34 or later (check with
git --version). - WSL2: Installed and configured.
npiperelay: Required for communication between WSL2 and the Windows OpenSSH agent.
Generate SSH keys using either a YubiKey or Windows Hello. These keys will be used for both SSH authentication and Git signing.
Choosing a Key Type:
ed25519-sk: Provides stronger security and is generally recommended. Use this if your YubiKey and SSH server support it.ecdsa-sk: An alternative ifed25519-skis not supported.
Discoverable vs. Non-Discoverable Credentials:
- Discoverable (Resident) Credentials: Stored on the YubiKey itself. This allows you to use the key on different machines without needing to export and import it, but limits the number of keys stored to around 25, depending on your YubiKey's storage capacity. Requires generating the key with the
-O residentflag. - Non-Discoverable Credentials: Not stored on the YubiKey. You'll need to back up and restore these keys manually when moving to a new machine.
Generating the Key:
-
Open a terminal (Command Prompt or PowerShell).
-
Run one of the following commands:
-
Discoverable Credential (Recommended):
ssh-keygen -t ed25519-sk -O resident -O application=ssh:<YourAppName> -O verify-required -C "<YourComment>"
or
ssh-keygen -t ecdsa-sk -O resident -O application=ssh:<YourAppName> -O verify-required -C "<YourComment>"
-t ed25519-skor-t ecdsa-sk: Specifies the key type (choose one).-O resident: Makes the key discoverable (stored on the YubiKey).-O application=ssh:<YourAppName>: Sets the application parameter tossh:followed by your app's name. This can be useful for organization, but it's optional.-O verify-required: Requires user verification (usually a PIN) for each use.-C "<YourComment>": Adds a comment to the key (e.g., your email address or a description).- The key will be stored in
~/.ssh/by default.
-
Non-Discoverable Credential:
ssh-keygen -t ed25519-sk -C "<YourComment>"or
ssh-keygen -t ecdsa-sk -C "<YourComment>"-t ed25519-skor-t ecdsa-sk: Specifies the key type.-C "<YourComment>": Adds a comment to the key.
-
-
Follow the prompts: You'll be asked to touch your YubiKey and potentially enter/create a PIN.
-
Open a terminal (Command Prompt or PowerShell).
-
Run the following command:
ssh-keygen -t ecdsa-sk -C "<YourComment>"-t ecdsa-sk: Specifies the key type (currently, onlyecdsa-skis supported with Windows Hello).-C "<YourComment>": Adds a comment to the key.
-
Follow the prompts: You'll be asked to authenticate using Windows Hello (PIN, fingerprint, etc.).
Configure Git to use your newly generated SSH key for signing commits.
-
Set the GPG signing format to SSH:
git config --global gpg.format ssh
-
Set your signing key:
git config --global user.signingKey ~/.ssh/<YourPublicKey>
-
Replace
<YourPublicKey>with the actual name of your public key file (e.g.,id_ed25519_sk.pub,id_ecdsa_sk.pub). -
For Windows Hello, I like to name the key
tpm_ecdsa_sk.pubon every machine. This means I can use the same Git config and DevContainer configuration across different machines.
-
-
Enable commit signing by default (Optional):
git config --global commit.gpgSign true- If you don't enable this, you'll need to use the
-Sflag every time you commit:git commit -S -m "Your commit message"
- If you don't enable this, you'll need to use the
Git uses an "allowed signers" file to verify SSH signatures. You need to create this file and add your public key to it.
-
Create the file:
mkdir -p ~/.ssh touch ~/.ssh/allowed_signers
-
Add your public key to the file:
echo "git@github.com $(cat ~/.ssh/<YourPublicKey>)" >> ~/.ssh/allowed_signers
- Replace
<YourPublicKey>with the name of your public key file (e.g.,id_ed25519_sk,id_ecdsa_sk). - Replace
git@github.comwith the appropriate entry for your Git provider (e.g.,git@gitlab.com,git@bitbucket.org, or your own server's address). You might need multiple lines if you use multiple providers.
- Replace
-
Tell Git about your allowed signers file:
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
To use your Windows-generated SSH key within WSL2, the Windows OpenSSH agent and npiperelay are needed to forward the authentication requests.
-
Build
npiperelay:- Go to the npiperelay GitHub repository.
- Follow the instructions in the
README - Place the compiled
npiperelay.exein a directory on your Windows system (e.g.,C:\npiperelay).
-
Enable and Start the OpenSSH Agent:
-
Open PowerShell as an administrator.
-
Run the following commands:
Set-Service ssh-agent -StartupType Automatic Start-Service ssh-agent Get-Service ssh-agent # Verify that the status is "Running"
-
-
Install
socat:-
Open your WSL2 terminal.
-
Run:
sudo apt update sudo apt install socat
-
-
Enable WSL Interop:
-
Run:
sudo sh -c 'echo :WSLInterop:M::MZ::/init:PF > /usr/lib/binfmt.d/WSLInterop.conf'This command allows WSL to execute Windows binaries.
-
-
Open your
.bashrcfile:nano ~/.bashrc -
Add these lines at the end of the file:
export SSH_AUTH_SOCK="$HOME/.ssh/ssh-auth.socket" ss -a | grep -q "$SSH_AUTH_SOCK" if [ $? -ne 0 ]; then rm -f "$SSH_AUTH_SOCK" (setsid socat UNIX-LISTEN:"$SSH_AUTH_SOCK",fork EXEC:"/mnt/c/npiperelay/npiperelay.exe -ei -s //./pipe/openssh-ssh-agent" &) >/dev/null 2>&1 fi
export SSH_AUTH_SOCK="$HOME/.ssh/ssh-auth.socket": Sets theSSH_AUTH_SOCKenvironment variable to a custom path. This variable tells SSH clients where to find the SSH agent's socket.ss -a | grep -q "$SSH_AUTH_SOCK": Checks if there is already a process listening on the socket specified bySSH_AUTH_SOCK. This prevents multiple instances from running.rm -f "$SSH_AUTH_SOCK": Removes the socket file if no process is listening on it, ensuring a clean start.setsid socat ...: This is the forwarding setup:setsid: Creates a new session for thesocatprocess, detaching it from the current terminal. This allows the forwarding to continue even after you close the terminal.socat: A powerful utility for bidirectional data transfer. Here, it's used to create a Unix socket and forward connections tonpiperelay.exe.UNIX-LISTEN:"$SSH_AUTH_SOCK",fork: Tellssocatto listen on a Unix socket at the path specified bySSH_AUTH_SOCKand to fork a new process for each incoming connection.EXEC:"/mnt/c/npiperelay/npiperelay.exe -ei -s //./pipe/openssh-ssh-agent"Specifies the command to execute for each connection. This runsnpiperelay.exe, which forwards the connection to the Windows OpenSSH agent's named pipe (//./pipe/openssh-ssh-agent).-ei: Forwards standard input and output.-s: Uses a named pipe.
-
Use the new
.bashrc:source ~/.bashrc
To use your key in VS Code DevContainers you need to enable SSH Agent Forwarding:
```json
"settings": {
"remote.SSH.enableAgentForwarding": true
}
```
If you used a discoverable (resident) key with your YubiKey, you can "import" it on a new machine:
-
Insert your YubiKey.
-
Open PowerShell as an administrator.
-
Navigate to your
.sshdirectory:cd ~/.ssh
-
Run:
ssh-keygen -K
This command will import the resident keys from your YubiKey, storing them as stubs in your
~/.sshdirectory.