Sign your commits on GitHub with GPG

Last April, GitHub added support for validating commits signed using GPG. As knowledge of this security feature grows, more and more developers are recommending it. However, setting it up can be confusing. In this article, I’ll cover how to set up signing commits with GPG and verifying those signatures on GitHub.

What is GPG?

GPG stands for GNU Privacy Guard. It is a form of encryption much like SSH–in fact, GNUPG version 2 includes support for SSH. It can be used to verify the identify of a sender–in this case, committer.

Install the necessary tools

You’ll need some way to manage GPG keys. GitHub recommends some GUIs (GUI Suite for Mac; Gpg4win for Windows). I recommend the GUI for windows, but the command line for Mac. According to the GPG website, the download didn’t work for a while on Windows 64-bit. It’s probably fine now, but Gpg4win might be less of a headache for you.

I’ll be providing instructions for Mac in the subsequent sections. The overall steps should be the same, but if you’re using Windows, consult the Gpg4win documentation on how to generate the necessary keys. On the plus side, saving your passphrase should be easier on Windows using Gpg4win. On a Mac, the gpg-agent doesn’t automatically integrate with the OSX keychain, so more work is required. Not to worry, though. We’ll cover that too!

Sorry Linux users, but you probably know how to do this stuff already.

Installing on a Mac

$ brew install gnupg gpg-agent pinentry-mac

Generate a GPG key

$ gpg --gen-key

This asks a series of questions. It’s okay to use the defaults, with no expiration. When asked for an email, it is important to use the same email as the one on your GitHub profile–which should also be the same as the one used for Git. If these three emails do not match, verification will fail.

The last question asks for a passphrase. A passphrase is a sequence of words, not necessarily a sentence. For instance, “bunny butt triple canopy” or “Trump Hair Wild Raccoon Inebriated” are perfectly fine. Unfortunately, you can’t use those now, but you get the idea. It should be something you can remember–at least until you’ve got it saved in your keychain.

Add the public key to your git config

$ gpg --list-keys
/Users/home/.gnupg/pubring.gpg
-------------------------------
pub 1537P/[PUBKEY] 2016-06-30
uid Your Name <youremail@example.com>
sub 1537P/[SUBKEY] 2016-06-29

Note that “PUBKEY” is the key you want to copy to clipboard (the right side of the slash).

$ git config --global user.signingkey <PUBKEY>

Add the key to GitHub

Print your key in ASCII armor format…

$ gpg --armor --export <PUBKEY>

Then copy your GPG key, beginning with — — -BEGIN PGP PUBLIC KEY BLOCK — — — and ending with — — -END PGP PUBLIC KEY BLOCK — — -.

GitHub’s instructions on where to paste this are plain enough…

Sign your commits

You can sign individual commits or set a global git option to sign all commits. To sign an individual commit, add the -S option…

$ git commit -S -m "Signed commit"

Or, to sign all commits, set the option in your global git config…

$ git config --global commit.gpgsign true

After pushing to GitHub, you should see a nice “Verified” badge on the commit…

Saving your passphrase

If you don’t want to have to enter your passphrase every time you sign a commit, there are a few steps to get that working. Otherwise, you can stop here.

There are three config files to edit: “~/.gnupg/gpg.conf”, “~/.gnupg/gpg-agent.conf”, and your “~/.bash_profile” or “~/.zshrc”–depending on whether you use bash or zsh.

gpg.conf

# Uncomment within config (or add this line)
# This tells gpg to use the gpg-agent
use-agent

# Silences the "you need a passphrase" message
# This is a potential source of issues, but I haven't noticed any
batch

gpg-agent.conf

# Enables GPG to find gpg-agent
use-standard-socket

# Connects gpg-agent to the OSX keychain via the brew-installed
# pinentry program from GPGtools. This is the OSX 'magic sauce',
# allowing the gpg key's passphrase to be stored in the login
# keychain, enabling automatic key signing.
pinentry-program /usr/local/bin/pinentry-mac

.bash_profile or .zshrc

# In order for gpg to find gpg-agent, gpg-agent must be running,
# and there must be an env variable pointing GPG to the gpg-agent socket.
# This little script, which must be sourced
# in your shell's init script (ie, .bash_profile, .zshrc, whatever),
# will either start gpg-agent or set up the
# GPG_AGENT_INFO variable if it's already running.

# Add the following
if [ -f ~/.gnupg/.gpg-agent-info ] && [ -n "$(pgrep gpg-agent)" ]; then
source ~/.gnupg/.gpg-agent-info
export GPG_AGENT_INFO
else
eval $(gpg-agent --daemon --write-env-file ~/.gnupg/.gpg-agent-info)
fi

This information has been relayed from a helpful gist by Brian Hatfield.

Open a new shell and check that GPG_AGENT_INFO is set…

$ echo $GPG_AGENT_INFO
> /Users/home/.gnupg/agent-location

The first time you sign a commit, the pinentry program will ask for permission. It shouldn’t ask again after that.

Edit: Saving your passphrase on Windows

I went through this flow myself on Windows and found that gpg4win was not quite enough to save the passphrase so I didn’t have to enter it on every commit. Fortunately, there’s a simple fix. Assuming you are using Git for Windows (formerly called Git Bash), you can add the following to your .bash_profile.bashrc, or .profile:

env=~/.ssh/agent.env

agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; }

agent_start () {
(umask 077; ssh-agent >| "$env")
. "$env" >| /dev/null ; }

agent_load_env

# agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2= agent not running
agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?)

if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then
agent_start
ssh-add
elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then
ssh-add
fi

unset env

That will auto-launch ssh-agent on Windows, effectively saving the passphrase. See GitHub Help for more info.

And that’s it! Your setup is complete. Please feel free to ask questions in the comments, and let me know how I might improve this guide for future readers.

Reference Material