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.

Software

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.

Configuration

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

C:\Users\withinboredom>gpg –full-generate-key
gpg (GnuPG) 2.2.1; Copyright (C) 2017 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: First Last
Email address: example@example.com
Comment:
You selected this USER-ID:
"First Last <example@example.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key F2992F4953745E6F marked as ultimately trusted
gpg: revocation certificate stored as 'C:/Users/withinboredom/AppData/Roaming/gnupg/openpgp-revocs.d\7981B5F23994C9432EAFB921F2992F4953745E6F.rev'
public and secret key created and signed.
Note that this key cannot be used for encryption. You may want to use
the command "–edit-key" to generate a subkey for this purpose.
pub rsa4096 2017-11-18 [SC]
7981B5F23994C9432EAFB921F2992F4953745E6F
uid First Last <example@example.com>

view raw
primary-key
hosted with ❤ by GitHub

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:

C:\Users\withinboredom>gpg –expert –edit-key F2992F4953745E6F
gpg (GnuPG) 2.2.1; Copyright (C) 2017 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret key is available.
sec rsa4096/F2992F4953745E6F
created: 2017-11-18 expires: never usage: SC
trust: ultimate validity: ultimate
[ultimate] (1). First Last <example@example.com>
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa4096/F2992F4953745E6F
created: 2017-11-18 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/9308FE0C9C7B67A0
created: 2017-11-18 expires: never usage: S
[ultimate] (1). First Last <example@example.com>
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
Your selection? 6
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa4096/F2992F4953745E6F
created: 2017-11-18 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/9308FE0C9C7B67A0
created: 2017-11-18 expires: never usage: S
ssb rsa4096/5D020B5F8D073308
created: 2017-11-18 expires: never usage: E
[ultimate] (1). First Last <example@example.com>
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
Your selection? 8
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? S
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Encrypt
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? E
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions:
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? A
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? q
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa4096/F2992F4953745E6F
created: 2017-11-18 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/9308FE0C9C7B67A0
created: 2017-11-18 expires: never usage: S
ssb rsa4096/5D020B5F8D073308
created: 2017-11-18 expires: never usage: E
ssb rsa4096/9AE0643FE8DD7472
created: 2017-11-18 expires: never usage: A
[ultimate] (1). First Last <example@example.com>
gpg> save

view raw
subkeys
hosted with ❤ by GitHub

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:

 

gpg –card-edit
gpg/card> admin
// change the pins (don't forget them)
gpg/card> 3 <12345678 => (factory default admin pin)>
gpg/card> 1 <123456 => (factory default pin)>
gpg/card> q
gpg/card> name
gpg/card> lang (en)
gpg/card> sex
gpg/card> login
gpg/card> quit
gpg –edit-key F2992F4953745E6F
gpg> key 1
gpg> keytocard
1 (signature key)
gpg> key 1
gpg> key 2
gpg> keytocard
2 (encryption key)
gpg> key 2
gpg> key 3
gpg> keytocard
3 (authentication key)
gpg> save

view raw
cards
hosted with ❤ by GitHub

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.

SSH

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 <landers.robert@gmail.com>"
"hello"