How to setup Signed Git Commits with a YubiKey NEO and GPG and Keybase on Windows
This week in obscure blog titles, I bring you the nightmare that is setting up Signed Git Commits with a YubiKey NEO and GPG and Keybase on Windows. This is one of those "it's good for you" things like diet and exercise and setting up 2 Factor Authentication. I just want to be able to sign my code commits to GitHub so I might avoid people impersonating my Git Commits (happens more than you'd think and has happened recently.) However, I also was hoping to make it more secure by using a YubiKey 4 or Yubikey NEO security key. They're happy to tell you that it supports a BUNCH of stuff that you have never heard of like Yubico OTP, OATH-TOTP, OATH-HOTP, FIDO U2F, OpenPGP, Challenge-Response. I am most concerned with it acting like a Smart Card that holds a PGP (Pretty Good Privacy) key since the YubiKey can look like a "PIV (Personal Identity Verification) Smart Card."
NOTE: I am not a security expert. Let me know if something here is wrong (be nice) and I'll update it. Note also that there are a LOT of guides out there. Some are complete and encyclopedic, some include recommendations and details that are "too much," but this one was my experience. This isn't The Bible On The Topic but rather what happened with me and what I ran into and how I got past it. Until this is Super Easy (TM) on Windows, there's gonna be guides like this.
As with all things security, there is a balance between Capital-S Secure with offline air-gapped what-nots, and Ease Of Use with tools like Keybase. It depends on your tolerance, patience, technical ability, and if you trust any online services. I like Keybase and trust them so I'm starting there with a Private Key. You can feel free to get/generate your key from wherever makes you happy and secure.
I use Windows and I like it, so if you want to use a Mac or Linux this blog post likely isn't for you. I love and support you and your choice though. ;)
Make sure you have a private PGP key that has your Git Commit Email Address associated with it
I download and installed (and optionally donated) a copy of Gpg4Win here.
Take your private key - either the one you got from Keybase or one you generated locally - and make sure that your UID (your email address that you use on GitHub) is a part of it. Here you can see mine is not, yet. That could be the main email or might be an alias or "uid" that you'll add.
If not - as in my case since I'm using a key from keybase - you'll need to add a new uid to your private key. You will know you got it right when you run this command and see your email address inside it.
> gpg --list-secret-keys --keyid-format LONG
------------------------------------------------
sec# rsa4096/MAINKEY 2015-02-09 [SCEA]
uid [ultimate] keybase.io/shanselman <shanselman@keybase.io>
You can adduid in the gpg command line or you can add it in the Kleopatra GUI.
List them again and you'll see the added uid.
> gpg --list-secret-keys --keyid-format LONG
------------------------------------------------
sec# rsa4096/MAINKEY 2015-02-09 [SCEA]
uid [ultimate] keybase.io/shanselman <shanselman@keybase.io>
uid [ unknown] Scott Hanselman <scott@hanselman.com>
When you make changes like this, you can export your public key and update it in Keybase.io (again, if you're using Keybase).
Plugin your YubiKey
When you plug your YubiKey in (assuming it's newer than 2015) it should get auto-detected and show up like this "Yubikey NEO OTP+U2F+CCID." You want it so show up as this kind of "combo" or composite device. If it's older or not in this combo mode, you may need to download the YubiKey NEO Manager and switch modes.
Test that your YubiKey can be seen as a Smart Card
Go to the command line and run this to confirm that your Yubikey can be see as a smart card by the GPG command line.
> gpg --card-status
Reader ...........: Yubico Yubikey NEO OTP U2F CCID 0
Version ..........: 2.0
....
IMPORTANT: Sometimes Windows machines and Corporate Laptops have multiple smart card readers, especially if they have Windows Hello installed like my SurfaceBook2! If you hit this, you'll want to create a text file at %appdata%\gnupg\scdaemon.conf and include a reader-port that points to your YubiKey. Mine is a NEO, yours might be a 4, etc, so be aware. You may need to reboot or at least restart/kill the GPG services/background apps for it to notice you made a change.
If you want to know what string should go in that file, go to Device Manager, then View | Show Hidden Devices and look under Software Devices. THAT is the string you want. Put this in scdaemon.conf:reader-port "Yubico Yubikey NEO OTP+U2F+CCID 0"
Yubikey NEO can hold keys up to 2048 bits and the Yubikey 4 can hold up to 4096 bits - that's MOAR bits! However, you might find yourself with a 4096 bit key that is too big for the Yubikey NEO. Lots of folks believe this is a limitation of the NEO that sucks and is unacceptable. Since I'm using Keybase and starting with a 4096 bit key, one solution is to make separate 2048 bit subkeys for Authentication and Signing, etc.
From the command line, edit your keys then "addkey"
> gpg --edit-key <scott@hanselman.com>
You'll make a 2048 bit Signing key and you'll want to decide if it ever expires. If it never does, also make a revocation certificate so you can revoke it at some future point.
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)
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 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)
Key does not expire at all
Save your changes, and then export the keys. You can do that with Kleopatra or with the command line:
--export-secret-keys --armor KEYID
Here's a GUI view. I have my main 4096 bit key and some 2048 bit subkeys for Signing or Encryption, etc. Make as many as you like
LEVEL SET - It will be the public version of the 2048 bit Signing Key that we'll tell GitHub about and we'll put the private part on the YubiKey, acting as a Smart Card.
Move the signing subkey over to the YubiKey
Now I'm going to take my keychain here, select the signing one (note the ASTERISK after I type "key 1" then "keytocard" to move/store it on the YubyKey's SmartCard Signature slot. I'm using my email as a way to get to my key, but if your email is used in multiple keys you'll want to use the unique Key Id/Signature. BACK UP YOUR KEYS.
> gpg --edit-key scott@hanselman.com
gpg (GnuPG) 2.2.6; Copyright (C) 2018 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.
sec rsa4096/MAINKEY
created: 2015-02-09 expires: never usage: SCEA
trust: ultimate validity: ultimate
ssb rsa2048/THEKEYIDFORTHE2048BITSIGNINGKEY
created: 2015-02-09 expires: 2023-02-07 usage: S
card-no: 0006
ssb rsa2048/KEY2
created: 2015-02-09 expires: 2023-02-07 usage: E
[ultimate] (1). keybase.io/shanselman <shanselman@keybase.io>
[ultimate] (2) Scott Hanselman <scott@hanselman.com>
gpg> toggle
gpg> key 1
sec rsa4096/MAINKEY
created: 2015-02-09 expires: never usage: SCEA
trust: ultimate validity: ultimate
ssb* rsa2048/THEKEYIDFORTHE2048BITSIGNINGKEY
created: 2015-02-09 expires: 2023-02-07 usage: S
card-no: 0006
ssb rsa2048/KEY2
created: 2015-02-09 expires: 2023-02-07 usage: E
[ultimate] (1). keybase.io/shanselman <shanselman@keybase.io>
[ultimate] (2) Scott Hanselman <scott@hanselman.com>
gpg> keytocard
Please select where to store the key:
(1) Signature key
(3) Authentication key
Your selection? 1
gpg> save
If you're storing thing on your Smart Card, it should have a pin to protect it. Also, make sure you have a backup of your primary key (if you like) because keytocard is a destructive action.
Have you set up PIN numbers for your Smart Card?
There's a PIN and an Admin PIN. The Admin PIN is the longer one. The default admin PIN is usually ‘12345678’ and the default PIN is usually ‘123456’. You'll want to set these up with either the Kleopatra GUI "Tools | Manage Smart Cards" or the gpg command line:
>gpg --card-edit
gpg/card> admin
Admin commands are allowed
gpg/card> passwd
*FOLLOW THE PROMPTS TO SET PINS, BOTH ADMIN AND STANDARD*
Tell Git about your Signing Key Globally
Be sure to tell Git on your machine some important configuration info like your signing key, but also WHERE the gpg.exe is. This is important because git ships its own older local copy of gpg.exe and you installed a newer one!
git config --global gpg.program "c:\Program Files (x86)\GnuPG\bin\gpg.exe"
git config --global commit.gpgsign true
git config --global user.signingkey THEKEYIDFORTHE2048BITSIGNINGKEY
If you don't want to set ALL commits to signed, you can skip the commit.gpgsign=true and just include -S as you commit your code:
git commit -S -m your commit message
Test that you can sign things
if you are running Kleopatra (the noob Windows GUI) when you run gpg --card-status you'll notice the cert will turn boldface and get marked as certified.
The goal here is for you to make sure GPG for Windows knows that there's a private key on the smart card, and associates a signing Key ID with that private key so when Git wants to sign a commit, you'll get a Smart Card PIN Prompt.
Advanced: If you make SubKeys for individual things so that they might also be later revoked without torching your main private key. Using the Kleopatra tool from GPG for Windows you can explore the keys and get their IDs. You'll use those Subkey IDs in your git config to remove to your signingkey.
At this point things should look kinda like this in the Kleopatra GUI:
Make sure to prove you can sign something by making a text file and signing it. If you get a Smart Card prompt (assuming a YubiKey) and a larger .gpg file appears, you're cool.
> gpg --sign .\quicktest.txt
> dir quic*
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/18/2018 3:29 PM 9 quicktest.txt
-a---- 4/18/2018 3:38 PM 360 quicktest.txt.gpg
Now, go up into GitHub to https://github.com/settings/keys at the bottom. Remember that's GPG Keys, not SSH Keys. Make a new one and paste in your public signing key or subkey.
Note the KeyID (or the SubKey ID) and remember that one of them (either the signing one or the primary one) should be the ID you used when you set up user.signingkey in git above.
The most important thing is that:
- the email address associated with the GPG Key
- is the same as the email address GitHub has verified for you
- is the same as the email in the Git Commit
- git config --global user.email "email@example.com"
If not, double check your email addresses and make sure they are the same everywhere.
Try a signed commit
If pressing enter pops a PIN Dialog then you're getting somewhere!
Commit and push and go over to GitHub and see if your commit is Verified or Unverified. Unverified means that the commit was signed but either had an email GitHub had never seen OR that you forgot to tell GitHub about your signing public key.
Yay!
Setting up to a second (or third) machine
Once you've told Git about your signing key and you've got your signing key stored in your YubiKey, you'll likely want to set up on another machine.
- Install GPG for Windows
- gpg --card-status
- Import your public key. If I'm setting up signing on another machine, I'll can import my PUBLIC certificates like this or graphically in Kleopatra.
>gpg --import "keybase public key.asc"
gpg: key *KEYID*: "keybase.io/shanselman <shanselman@keybase.io>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1You may also want to run gpg --expert --edit-key *KEYID* and type "trust" to certify your key as someone (yourself) that you trust.
- gpg --card-status
- Install Git (I assume you did this) and configure GPG
- git config --global gpg.program "c:\Program Files (x86)\GnuPG\bin\gpg.exe"
- git config --global commit.gpgsign true
- git config --global user.signingkey THEKEYIDFORTHE2048BITSIGNINGKEY
- Sign something with "gpg --sign" to test
- Do a test commit.
Finally, feel superior for 8 minutes, then realize you're really just lucky because you just followed the blog post of someone who ALSO has no clue, then go help a co-worker because this is TOO HARD.
Sponsor: Check out JetBrains Rider: a cross-platform .NET IDE. Edit, refactor, test and debug ASP.NET, .NET Framework, .NET Core, Xamarin or Unity applications. Learn more and download a 30-day trial!
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
I don't use Yubikey, so I can't confirm on that configuration.
Have to agree that until someone thinks I'm worth impersonating, this is a lot of things that can go wrong for me to willingly set up, which is a shame.
There are legitimate uses for it too. I recently migrated my team's source control from Subversion to Git and one of the goals was to keep all the commit metadata intact. (Otherwise, my GitHub activity graph would have been solid green going back eight or nine years.)
I've been hemming and hawing over whether to start signing my commits. Scott's experience has me thinking that even now, PGP/GPG still isn't ready for us mere mortals.
@Scott -- Great article. I'd quibble a bit over the reference to PIV. A lot of PIV has to do with the identity verification process and it also has to do with the formats, both of the certificate(s) on the card and the biometrics stored on the card. Best to just say that one of the things a Yubikey can do is to be a smart card authenticator (or a multifactor cryptographic device authenticator in the language of the most recent NIST Special Publication 800-63 (https://pages.nist.gov/800-63-3/). I'm @usethedata on twitter and my DMs are open to you if you'd like to discuss further. I'm an Enterprise Architect responsible for (among other things) authentication at a federal facility, so this is very much in my wheelhouse.
Comments are closed.