Intro
You might already know about using public key authentication for SSH. But did you know that you can also use a GNU Privacy Guard (GPG) keypair for SSH authentication? In this tutorial, we'll look at configuring this on a Windows client. This guide assumes you already have a basic understanding of GPG and SSH.
Why bother, if a plain SSH key already works? You end up with one keypair for everything - signing commits, encrypting files and logging into servers - instead of a separate key sitting in ~/.ssh for each purpose. And if your GPG key lives on a hardware token like a YubiKey or Nitrokey, the private key never leaves the device, so it can't be copied off your machine even if the machine is compromised.
Install Gpg4win
Download Gpg4win and install it. Then, create or import your key if you haven't already. Make sure that your key allows authorization or has a subkey that permits it.
Export SSH key and put it on the server
To export the SSH key from your GPG key, open the command line and run:
gpg --export-ssh-key KEY
Where KEY is your GPG key name, email or ID. You can find it with:
gpg --list-keys
The output should look something like this:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDbDzdth1SOQ793iDV1bhSEabDXj52r4Q6bYpGo27a72VSaqzBEaL7mllL2qteIInWe4yHQxX5kwKxEsB4w657GIkWzTAMMu+tCCRf3EgiZawuICXSpCcCjWu4NjQm6i1N2wWu7LHyKHTKkmyz6F48ti0vWIGv3h78am9aCZSgWyXERPyov9VxVAFKZYJMxNpTpgDx4gCoqKYlTbUqEBYabsk5atpxtRxsS27vsEpaB1Ib2KKWmKhFpLdv0EAkKQrSZlJf15gexnt8o7agGKB2c+kdHhYWYkkTTpZPlaIRejDLFpB6JTFppmgaMaKrN65+dH7Q2FXasZp0Shret17IZ9xmPdRUELSPwDb4OceapjtvHBMB3Ot+NXnSMKFT6/mJa/TB+/nZ9h7gv9vy59wE+LLdNONicWxH9EZyCloIMfM9OVXWgDE+7St18dr4Pd9QIX8bNGWSpGnad5LPCFRV5RdHWfQL4Ag4P63X5qfxrqWFynrfzDlHiXV/hnBykO7yihVOy590wnTkSvsgFT6+6wdVztmX2BJfDOp4EnYDoNDXrKxG4muizHJ79CjvE9Fx+5hU9aCC4WwDsLRa1YNtLDfCJByOZWmcyldHfu21MMgZV6JdkmSj8v8ojQf1myx4rMPIEENNeurGK/iQ6ObJtp92pwOIRP6Zsxnm1WChxbQ== openpgp:0x900AF684
Then copy the entire output (including ssh-rsa and the openpgp:0xXXXXXXXX at the end) and add it to the ~/.ssh/authorized_keys file on your server.
Set up client configuration
Make sure OpenSSH Authentication Agent is disabled
Open services.msc, find OpenSSH Authentication Agent and make sure it is turned off, if not then turn it off. If Windows ssh-agent is left running it will conflict with gpg-agent.
Create environmental variable
Run SystemPropertiesAdvanced and go to Environmental variables. Next create new user variable:
Name: SSH_AUTH_SOCK
Value: //./pipe/openssh-ssh-agent
Then close both windows to save.
Set up gpg-agent config
Go to %appdata%\gnupg, create file gpg-agent.conf with following content:
enable-ssh-support
enable-putty-support
enable-win32-openssh-support
Set up keygrip
Execute:
gpg --list-keys --with-keygrip
Copy the keygrip for your key, then go to %appdata%\gnupg, create file named sshcontrol and paste your keygrip in it. Leave one blank line at the end.
Prepare for run
Close all your command line/powershell tasks. Go to Task manager and kill app processes from GnuPG and ssh-agent.exe. Then open new terminal and run gpg-connect-agent /bye. Wait for it to turn on.
Next run ssh-add -L. If it returned your key, that means it's working.
Now you can login to your SSH server via ssh user@example.com. Instead of traditionally asking you for your password you will be asked to authenticate with a key (such as pinentry).
Make gpg-agent run automatically
To avoid running gpg-connect-agent /bye after every system startup, add it to the autostart.
Go to C:\Program Files (x86)\GnuPG\bin (or where you have GnuPG installed), find gpg-connect-agent.exe, create a shortcut and move it to %appdata%\Microsoft\Windows\Start Menu\Programs\Startup.
Troubleshooting
ssh-add -L returns nothing
Almost always the sshcontrol file. Check that it contains the keygrip of a subkey with the authentication capability ([A] in gpg --list-keys --with-keygrip), not the signing or encryption one, and that the file ends with a blank line — without it the last entry is ignored.
If the file looks right, the agent is probably still running with the old configuration. Kill it and start again:
gpgconf --kill gpg-agent
gpg-connect-agent /bye
sign_and_send_pubkey: signing failed: agent refused operation
The agent found the key but refused to use it. Usually the subkey has no authentication capability, or the keygrip in sshcontrol belongs to a different subkey. Verify with gpg --list-keys --with-keygrip and look for the line marked [A].
Windows ssh-agent keeps taking over
Two things to check. OpenSSH Authentication Agent in services.msc has to be stopped and set to disabled - otherwise Windows restarts it on the next boot. And SSH_AUTH_SOCK only applies to processes started after the variable was created, so close every terminal (and your IDE) and open a fresh one.
pinentry never appears
The agent was started from a different session than the one you're authenticating from (or isn't running at all). Kill it with gpgconf --kill gpg-agent, then run gpg-connect-agent /bye from the same terminal you use for SSH.
What do you think about it?