Coming up with this setup required a few steps and a few different guides. I thought it might be useful to put the whole setup online in one post for people to find. This post assumes you already have a GPG auth key set up on your YubiKey. YubiCo has a good writeup about it.

The steps to do this have been brought together from several sources, including a large part from this post from justyn.io however I have done some things differently after running into the odd problem or difference of opinion.

Yubikey SSH Auth in Windows using GPG

The first step involves getting SSH authentication working in windows. There’s a couple of gotchas I’ve encountered, mainly relating to the current version of GPG accessing the yubikey in Windows. This will provide ssh access to both putty and windows ssh client.

Installation

First step is to install GPG4Win. This can be downloaded from their website or using winget.

> winget install GnuPG.Gpg4win

Now start the gpg daemon with

> gpgconf --launch gpg-agent

Check that gpg can see your YubiKey by inserting it and running

> gpg --card-status

Some versions of GPG had trouble reading the YubiKey in which case you may need to configure scdaemon. YubiCo have documentation for this here. The current version I’m using (GPG4Win 3.1.16/GnuPG 2.3.3) doesn’t seem to need this.

You can then import your key to the local keyring. With the YubiKey inserted, run:

> gpg --card-edit
fetch
quit

Then you should set the trust of the key to ultimate. Run:

> gpg -k
> gpg --edit-key [keyID]
trust
5
y
quit

Configuration

YubiCo’s configuration instructions are available here however they include several options which are unnecessary. GPG on windows uses the path %appdata%\gnupg\ for its configuration files. Create a file gpg-agent.conf file and there’s only one line that needs to be added:

enable-putty-support

(Other options provided by YubiCo can be included however the SSH support by enable-ssh-support doesn’t seem to be usable by Windows and we’ll be using another tool to provide that socket from the putty/pageant support. The use-standard-socket option is enabled by default on Windows and the cache-ttl options are also set to the defaults in their list of options.)

Of course you can add any other configuration relevant to your setup.

Autostart GPG with Windows

This can be achieved in several different ways, however the simplest seems to be just to create a shortcut in the startup directory. Open the run windows (win+R) and type shell:startup and hit enter. Create a new shortcut and set the target to:

"C:\Program Files (x86)\GnuPG\bin\gpgconf.exe" --launch gpg-agent

Adjust the install path to the executable as necessary.

Windows Bridge

The Windows side of the bridge provides a standard SSH Auth socket using wsl-ssh-pageant and sets the SSH_AUTH_SOCK environment variable. I use the -gui version of wsl-ssh-pageant so that it can be launched in the background without blocking the console window. I then use a basic powershell script to be run at startup to set up the environment. (Update the script to set the path to the executable)

$winsshSockName = "ssh-pageant"
setx SSH_AUTH_SOCK \\.\pipe\$winsshSockName
& [path-to-executable]\wsl-ssh-pageant-amd64-gui.exe --systray -winssh $winsshSockName

Then in the startup directory (shell:startup) I create another shortcut. This I set the target to:

powershell.exe -windowstyle hidden -ExecutionPolicy RemoteSigned -File [path-to-script]\wsl-ssh-pageant.ps1

(Remember to set the path correctly.)

Linux Bridge

This step requires an extra binary in Windows, and socat installed in your WSL environment.

In Windows, you’ll need a copy of npiperelay from jstarks with support for libAssuan support (a version is available from here) or the fork wsl-relay from Lixcality.

(This information and tool was found from the post from justyn.io and this is the core tying WSL to Windows).

Then from your WSL environment, make sure you have socat available. Make sure you have a .gnupg directory in your home directory. Then in either your .profile or .bash_profile add the following, modifying the PATH_TO_PIPERELAY to point to where you have put the chosen pipe-relay binary in Windows:

PIPE_RELAY_BIN=/mnt/c/[windows path to pipe relay].exe

GPG_SOCKET=$HOME/.gnupg/S.gpg-agent

if [[ ! -S $GPG_SOCKET ]]; then
    socat UNIX-LISTEN:$GPG_SOCKET,fork EXEC:$PIPE_RELAY_BIN' -ei -ep -s -a "C:/Users/[username]/AppData/Local/gnupg/S.gpg-agent"',nofork &
fi

export SSH_AUTH_SOCK=$HOME/.misc/ssh-agent.sock
if [[ ! -S $SSH_AUTH_SOCK ]]; then
    socat UNIX-LISTEN:$SSH_AUTH_SOCK,fork EXEC:$PIPE_RELAY_BIN' -ei -s //./pipe/ssh-pageant',nofork &
fi

The path to the pipe relay is using the wsl mount to windows, however the argument to the pipe relay for both the gpg-agent socket file and the UNC path to the SSH Auth socket are in the Windows format (although with forward slashes in file paths) as it is a Windows binary.

GnuPG on windows used to place the sockets in the remote app data folder, but this seems to have changed recently, so this setup has used the Local appdata folder. Make sure you also update the username field to match your Windows home directory.

In this setup if the initial WSL instance is closed other bash instances won’t have launched the socat instances, although launching another instance then would launch them. I’ll update this post when I have a solution. Anything that launches them as a daemon outside of the bash instance should work, such as running them in tmux/screen.

You should finally copy your pubring.kbx and trustdb.gpg files from your Windows gnupg config folder to $HOME/.gnupg in your WSL environment