A relatively unknown and underused feature of Git is the ability to cryptographically sign commits. It is an optional feature that provides a way for the author of a commit to prove ownership. It uses the author’s GPG key to leave a signature in the commit that can be checked later. If you’re a Keybase user, it’s pretty easy to use your Keybase GPG key to sign your Git commits. Then, once you’ve signed your commits, GitHub provides a nice interface for verifying commits have been signed and by whom.

This tutorial walks you though the process I took to set up Git commit signing with my Keybase GPG key. I went from not having a GPG key installed locally through to seeing my commits marked as Verified on GitHub.

Why should we sign Git commits?

A few days ago, I was at NDC Security and saw a talk by Phil Haack where he spoofed a “malicious” commit to look like it was made by Troy Hunt (who was also speaking). It may sound difficult, but it’s actually a very trivial process. Git will accept any name and email address as the commit author and so will GitHub. So if you set the author on the commit to be a valid email address, it will look like they made the commit. This is due to the distributed nature of Git, which allows anyone to push anyone else’s commits around. However, from a security point of view, it’s a problem.

Unfortunately, there isn’t a way to stop someone from spoofing a commit with your name and email. However, Git does support cryptographically signing commits using a GPG key. This allows GitHub to mark your commits as Verified when it can match your verified email to your GPG key. This won’t stop someone trying to spoof your commits, but it will provide assurance of your real commits so they can be properly verified.

GitHub and Keybase

GitHub provides a settings page for setting your GPG key, however if you upload your raw GPG key from Keybase, it will likely contain a Keybase user reference: [email protected]. This isn’t a live email address and therefore GitHub will be unable to verify it. It also won’t match the email address in your commits. As a result, we need to do a few more things to get everything working.

I tried looking through the Keybase options, but couldn’t find any default way to modify the key and add email addresses. This seems like an oversight to me, but my understanding of this is limited, so there may be a good reason. Ultimately, I needed to export my key from Keybase into GPG so I could modify it directly. It turns out that Git needed it in there anyway, so it all works out nicely.

Updating the GPG key

Before you begin, I’m assuming you have Keybase installed and working via command line, and you have a GPG key already in your Keybase account. I used Git Bash on Windows 10, because I find it easier than CMD or PowerShell, but can use your preferred terminal.

First, export your public and private keys from Keybase using the keybase pgp command:

keybase pgp export > keybase-public.key
keybase pgp export --secret > keybase-private.key

During the export process, Keybase will ask for your account password and prompt to set a new password for the private key file.

Keybase password prompt
Keybase password prompt
New password for private key prompt
New password for private key prompt

Next, you need to import the keys into GPG using the gpg command:

gpg --allow-secret-key-import --import keybase-private.key
gpg --import keybase-public.key

The import process will ask for the password you just assigned to your private key, for obvious reasons.

GPG private key password prompt
GPG private key password prompt

Modifying the key (adding a new user)

Now that you’ve imported the key into GPG, you need to modify the key to include your email address. This is done by invoking the gpg --edit-key command, with a unique identifier for your key. I found using the <username>@keybase.io address worked nicely.

gpg --edit-key <username>@keybase.io

This command will get you into the gpg> prompt, and from there you need to run the adduid command. It will prompt for your Real name and Email address (feel free to leave Comment empty).

gpg> adduid
Real name: <your name>
Email address: <your email>
You selected this USER-ID:
    "<your name> <your email>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?

Once you’ve provided your name and email, confirm using the O and then save to close the gpg> prompt.

The entire key updating process should look something like this. Note that since I had already added my personal email address prior to saving this log, it shows me adding my work email address as a third user onto the key.

$ gpg --edit-key [email protected]
gpg (GnuPG) 2.2.13-unknown; Copyright (C) 2019 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/C1C4BEBF0442284B
     created: 2014-09-18  expires: 2024-09-15  usage: SCA
     trust: unknown       validity: unknown
ssb  rsa4096/3A829C5805933134
     created: 2014-09-18  expires: 2024-09-15  usage: E
[ unknown] (1). Stephen Rees-Carter <[email protected]>
[ unknown] (2)  keybase.io/valorin <[email protected]>

gpg> adduid
Real name: Stephen Rees-Carter
Email address: [email protected]*********.com
You selected this USER-ID:
    "Stephen Rees-Carter <[email protected]*********.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

sec  rsa4096/C1C4BEBF0442284B
     created: 2014-09-18  expires: 2024-09-15  usage: SCA
     trust: unknown       validity: unknown
ssb  rsa4096/3A829C5805933134
     created: 2014-09-18  expires: 2024-09-15  usage: E
[ unknown] (1)  Stephen Rees-Carter <[email protected]>
[ unknown] (2)  keybase.io/valorin <[email protected]>
[ unknown] (3). Stephen Rees-Carter <[email protected]*********.com>

gpg> save

Once that’s done, you can push your updated key back into Keybase.

keybase pgp update

Adding the key into GitHub

The easiest way I found to get the public key is to go to your Keybase profile (i.e. https://keybase.io/<username>) and click on the public key in the identity list. A dialog will appear which includes a text box containing your public key. Copy this into the clipboard.

Then go to Settings > SSH and GPG keys in GitHub. Click the New GPG Key button and paste your public key in.

GPG key in GitHub account used to sign git commits
GPG Key in GitHub

Signing Commits

The final piece of the puzzle is to tell your local git command to sign the commits using your key. This can be set either per-repository, or globally across all repositories in your computer’s current user with the --global flag.

Use the git config user.signingkey option to specify the Key ID for git to use. You can get this from the GitHub GPG keys page if you’re unsure what it is. For example, mine is C1C4BEBF0442284B.

You can also require Git to sign all commits with the commit.gpgsign option.

git config --global user.signingkey <Key ID>
git config --global commit.gpgsign true

Note, you will need to add your key to any computer/login that you use for commits to be signed.

Now, when you make any commits, Git will attempt to sign them with your key. It may ask you to unlock your key if you haven’t used it recently.

Unlock key to sign git commits
Unlock key prompt

Verified Commits

Once you’ve set it everything up correctly, you will see the friendly green Verified indicator next to your new commits in GitHub.

Verified signed git commit on GitHub
Verified commit on GitHub

That’s basically it. 🙂

Any future commits you from that computer/user will be verified, proving you are the author. Signing git commits won’t stop spoofing, but it will provide assurance of your real commits so they can be properly verified. I think that’s all we can do in Git for now.