Categories
Snippets Technology

Setting up a YubiKey for Git commit signing

I’ve been using a YubiKey as a 2-Factor Authentication key and to sign my Git commits. Until a few days ago I’ve been using a YubiKey 4. Since my current computer doesn’t have any USB-A ports, I’ve been juggling a USB-A-to-USB-C adapters in my backpack and using the key has been a hassle for the last year. That’s why I decided it’s time to move to my “new” YubiKey 5C to gain the benefit of not having to juggle those adapters.

I’ve actually had the key for a while, as I ordered it together with a replacement YubiKey 4 that I got because of the Infineon TPM issue they identified, but only now I decided it’s time to move to the new key.

The other reason to do it now is because my old GPG keys expired and I had to reissue them, so I can use the key to sign my Git commits.

GPG madness

If you’ve dealt with GPG keys in the past (or currently) you know that it can be pretty hard to get it up and running properly and safely for someone who’s never done it before. Keeping master keys offline, shuffling public keys, reissuing keys after they expire, possibly signing them with previous keys to verify them, revoking keys… It’s not easy to follow the best practices even in theory. When it comes to following them in practice, things get extremely complicated.

Most tutorials online refer to using the CLI to generate your keys, navigating menus with a non-common design pattern and are general pain to get them up and running. Most tutorials say use an offline machine like a Raspberry PI and/or generate the master keys and sub-keys while offline and keep the master key off your computer.

🤯

I’ve been dealing with this each start of January for the past almost 4 years. I dread the moment when I get the following error, noting that my signing keys have expired.

error: gpg failed to sign the data
fatal: failed to write commit object

This year I decided it’s time to try to make this process much easier and to use the in-built certificate generation and avoid the whole hassle with dealing with GPG key generations and such.

YubiKey on-key keys generation

With this we still have to use the CLI UI, but it’s a bit more obvious as it has fewer steps.

First we have to set up our card/device.

Card/device setup

Run gpg --card-status in the terminal and you should see something like this:

Reader ………..: Yubico Yubikey 4 OTP U2F CCID
Application ID …: <Serial number>
Version ……….: 2.1
Manufacturer …..: Yubico
Serial number ….: <Device serial number>
Name of cardholder: unspecified
Language prefs …: en
Sex …………..: unspecified
URL of public key : [not set]
Login data …….: <e-mail or nothing>
Signature PIN ….: not forced
Key attributes …: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 9

If you see something similar and no errors show up, you should be fine to continue.

Run gpg --card-edit

You will land in a CLI UI, showing the same information at the start and then a command prompt at the bottom gpg/card>.

Type admin and press return/enter.

You can now type help to figure out how to change the owner information and PINs for the card.

One thing that you MUST do is change the default PINs for the card. There are two of those. The first one is the PIN key you use to access the keys on the card. The second one is the “Admin” PIN or PUK (Personal Unblocking Code).

To do this type passwd and you will see a menu:

gpg/card> passwd
gpg: OpenPGP card no. <Serial number> detected

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Type 1 and follow the prompts to update your pin. The default PIN is 123456.

After completing that, choose 3 to change the Admin PIN. The default is 12345678.

After having both pins changed, go back to the main menu to finish updating the key information.

The commands you can use are:

  • name – to change the cardholder name
  • login – to change the cardholder e-mail address or login

After that we can move on to the actual key generation.

Key generation

Now it’s time to actually generate the keys.

To do that, type generate.

You will be asked Make off-card backup of encryption key? (Y/n), but as mentioned in YubiKey’s documentation, this is only a shim backup and it will not create a full backup of the secret keys, so it doesn’t matter if you reply Yes or No. I reply with No(n).

If there are stored keys on the card you will be asked if you want to replace them:

gpg: Note: keys are already stored on the card!

Replace existing keys? (y/N)

Make sure you’re not going to replace something important that you don’t have backup of and continue with Y. If there’s something important, better to quit the whole process with Ctrl+C.

You will be asked for your card PIN you set earlier.

Then you will have to chose how long the keys will be valid for:

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)

If you want the keys to be valid for only 1 year, type 1y.

You will be asked to confirm the expiration date. Check if everything is correct and confirm.

Then you will be asked to enter information about the key owner:

Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: <Your name>
Email address: <Your e-mail>
Comment: <Comment is optional>
 You selected this USER-ID:
     "<Your name> <comment> <your e-mail>
 Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?

Type O to confirm if everything seems correct.

Then you will be asked for your Admin PIN or PUK. This is required as it will perform a write action on the card.

Then if everything is correct, the YubiKey will start blinking and continue to do so for a while (for me it takes about five minutes or so).

After it’s done you will see the following message

gpg: key <Short ID> marked as ultimately trusted
gpg: revocation certificate stored as '/Users/bisko/.gnupg/openpgp-revocs.d/<LONG ID>.rev' public and secret key created and signed.

Take a note of the <Short ID> as we’ll be using this later.

At this point you can quit the GPG prompt with quit or Ctrl+C.

Now it’s time to save the public key, so we can reuse the key on another computer.

To do so, run the following command:

gpg --armor --export <Short ID> > <Short ID>.asc

This will export the public key to a file named <Short ID>.asc. This will be used in the future if you want to be able to use the YubiKey on another computer or if you lose your GPG configuration.

To restore the public key in your keyring, you need to do:

gpg --import < <Short ID>.asc
gpg: key <Short ID>: public key "Your Name <your@email>" imported
gpg: Total number processed: 1
gpg: imported: 1

A very important note here. If this is a new GPG install or another computer, GPG won’t recognize the keys on the card by itself. You need to run gpg --card-status so it can read the card and pick up the keys on the card.

Setting up Git for signing

To set up Git to sign your commits you need to add the following to your ~/.gitconfig.

Under the [user] section, you need to add (or update) the following line:

signingkey = 0x<Short ID>

Then if you want to sign all your commits, add the following at the end of the file:

[gpg]
  program = /usr/local/bin/gpg
[commit]
  gpgsign = true

This will make Git sign all your commits.

To set up GitHub, you need to add your public key to your profile. To do so, go to GitHub -> Settings -> SSH and GPG keys -> New GPG key.

Then you will see a text box, where you need to paste the whole content of the <Short ID>.asc file we generated above.

If you want to copy the contents of the file (on macOS) you can do:

cat <Short ID>.asc | pbcopy

This will copy the contents of the whole file in the clipboard.

Paste that into the text box on GitHub and click the Add GPG key button.

After that you can try to commit and push to GitHub and you should see the pretty Verified label.

Debugging

Sometimes things don’t work outright. To be able to gather more information you can try the following:

Signing with GPG

First try to sign something with the key:

gpg -s <file>

If this fails, try to figure out why it fails, I’m no expert to help there.

Check out Git’s output

GIT_TRACE=1 git commit -am "test"

This would give you the exact things Git does while it tries to perform the commit and sign it. It would give you a hint why the signing might fail.

From this you can also see that Git executes the following command:

/usr/local/bin/gpg --status-fd=2 -bsau <Key ID>

You can try running this manually to see if it will give you a better error.

Credits:

Header image from Yubico – https://www.yubico.com/press/images/

By Biser Perchinkov

Look, a coder!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s