Signing Commits, SSH with Yubikey and Windows

I have a Yubikey and Windows 10 on a Macbook Pro. It turns out all the tutorials out there are either for OSX or Linux. There’s some tidbits here and there for Windows users, but it took several hours of chasing down. Here’s a little tutorial and some gotchas I ran into while getting it up and running on my machine.


You’ll need Git for Windows (I had it installed with the Github extension for Visual Studio).

PuTTY of course (use the installer, we need plink and putty, mostly)

gpg4win for all the signing stuff… Install kleopatra.

That literally covers it.


Once you get everything all installed (your favorite defaults are fine), open up Kleopatra and go to Settings -> Configure Kleopatra. Select GnuPG System and Enable Putty Support.

Go ahead and save those settings and exit Kleopatra (but leave it running in the tray).

In Cortana, search for “Edit environment variables for your account” and add a new environment variable: GIT_SSH => C:\Program Files\PuTTY\plink.exe

Open up cmd and make sure gpg is the right gpg. When typing gpg --version and make sure it’s 2.2.1 or greater. If it’s less, you’re likely using the one from Git For Windows, which is bad. You’ll need to correct that, which I’ll leave as an exercise for the reader.

It took me forever to figure out why gpg --card-edit wasn’t working for me until I realized I was using the wrong gpg

Plug in your yubikey and make sure you see no errors, after typing gpg --card-status.

Now, let’s generate some keys.

Generating Keys

The following will all be in cmd

It will prompt you for a password and you’re good to go. Now would be a good time to create a folder somewhere and cd to that folder. Also, you may want to copy that revocation certificate it created for you, in the event you lose your Yubikey and need to revoke it. Store that guy somewhere safe, like OneDrive. OneDrive is safe, right?

We’ll need the key id a lot, so go ahead and copy it, in the example above, it’s F2992F4953745E6F.

Creating Subkeys

Now, let’s generate our subkeys by typing gpg --expert --edit-key F2992F4953745E6F, naturally replacing the id with the one you generated in your terminal.

I’m just going to dump the whole thing here, follow along in your terminal:

Moving To Hardware

Once we move the keys to hardware (the Yubikey), you won’t be able to get them back. I don’t advise backing them up, but if you must:

gpg --armor --export-secret-keys F2992F4953745E6F > master.key

gpg --armor --export-secret-subkeys F2992F4953745E6F > sub.key

So, let’s move these keys over to hardware…

Follow along in your own terminal, after typing gpg --card-edit:

Now, let’s get the public key all lined up: gpg --armor --export F2992F4953745E6F > pubkey.txt.

And, probably a good idea to store it out in the world: gpg --send-key F2992F4953745E6F

Cleaning Up

Now, go ahead and start up Kleopatra and delete all the keys we generated. How fun!

Finally, import the public key we just saved: gpg --import < pubkey.txt

Unplug and replug in the Yubikey and let’s trust the private key on the Yubikey.

gpg --edit-key F2992F4953745E6F

And type: trust

Tell it that you want to trust it ultimately (5) and you’re sure (y) then quit.


To get the string for your authorized_keys file, just run gpg --export-ssh-key <email used to create key> and copy that to your authorized_keys file on the remote host. Then, connect with PuTTy and it should “just work.”

Where to go from here

Check out Weasel Pageant for getting ssh-agent forwarding in WSL using your Yubikey.

Turn on touch to use the open-gpg key:

Require touch to sign:

"c:\Program Files (x86)\Yubico\YubiKey Manager\ykman.exe" openpgp touch sig on

Require touch to ssh:

"c:\Program Files (x86)\Yubico\YubiKey Manager\ykman.exe" openpgp touch aut on

Require touch to encrypt:

"c:\Program Files (x86)\Yubico\YubiKey Manager\ykman.exe" openpgp touch enc on

Using on a new computer

Make sure gpg/kleopatra is installed along with putty, git, etc.

Then plug in your yubikey, from the console run gpg --card-edit then fetch and finally, quit. That will import the card’s public key. You can test with the following command: echo "hello" | gpg --encrypt --armor -r KEYID | gpg

Naturally replace KEYID from the output of the id from fetch. It should prompt you for the PIN and/or touch and output something like:

gpg: WARNING: no command supplied. Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID 7E3131DF2A89BA35, created 2017-11-17
"Robert Landers <>"

Want an inside scoop?

Check out PHP Shenanigans for only €5/mo or €30/yr