Signing your work on Git
Why Is It Important?
A strict policy of signing all commits could prevent someone committing as you (perhaps with
GIT_COMMITTER_EMAIL) from fully blaming you for a change. You can verify signatures using
$ git log --show-signature commit e80c6f611429db4e437a377d7b1b76c167594dcd gpg: Signature made Sat Apr 9 11:19:58 2016 WEST using RSA key ID 6C1EEE05 gpg: Good signature from "Rui Afonso Pereira <email@example.com>" [ultimate] gpg: aka "Rui Afonso Pereira <firstname.lastname@example.org>" [ultimate] Author: Rui Afonso Pereira <email@example.com> Date: Sat Apr 9 11:19:40 2016 +0100
This article shows how to use public-key cryptography to sign git commits.
This article relies on GNU Privacy Guard (GnuPG), which is a tool for secure communication. It is a complete and free implementation of the OpenPGP standard as defined by RFC4880, also known as PGP — short for Pretty Good Privacy.
We shall start by installing the GnuPG tool. On macOS, you can grab it using Homebrew:
brew install gpg2
Furthermore, this binary can be aliased as
In other *NIX systems, either
gnupg is likely already installed. From this point on, due to the different ways that systems refer to the binary, I’m going to address it as
Make Your Keys
To use the GnuPG system, you’ll need a public key and a private key, also known together as a keypair. A public key is available to many, whereas the private key must be kept secret. You should never share your private key with anyone, under any circumstances.
If you don’t have an existing keypair, let’s proceed by generating one.
$ gpg --gen-key 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?
Let’s start by pressing
Enter and accepting
(1) RSA and RSA (default) as default.
RSA keys may be between 1024 and 8192 bits long. What keysize do you want? (2048)
You should type
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)
I do not want to bother with refreshing my key regularly, so mine never expires.
Then, we can move on to the next step.
GnuPG needs to construct a user ID to identify your key. Real name: Rui Afonso Pereira Email address: firstname.lastname@example.org Comment: You selected this USER-ID: "Rui Afonso Pereira <email@example.com>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?
Now take the time to review the information and press
Finally, GnuPG needs a passphrase to protect the primary and subordinate private keys that you keep in your possession.
You need a Passphrase to protect your private key. Enter passphrase:
A good passphrase is crucial to the secure use of GnuPG so it should be carefully chosen.
We shall consider having multiple email addresses: a work email and a personal email. It would be tedious if we had to go through this whole process for each. You can easily edit your key to add another user ID:
$ gpg --edit-key firstname.lastname@example.org Secret key is available. pub 2048R/6C1EEE05 created: 2016-04-08 expires: never usage: SC trust: ultimate validity: ultimate sub 2048R/8C44BD6A created: 2016-04-08 expires: never usage: E [ultimate] (1) Rui Afonso Pereira <email@example.com> gpg>
In this prompt, you can type
help for more information. To add a new user ID, type
gpg> adduid Real name: Rui Afonso Pereira Email address: firstname.lastname@example.org Comment: You selected this USER-ID: "Rui Afonso Pereira <email@example.com>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o You need a passphrase to unlock the secret key for user: "Rui Afonso Pereira <firstname.lastname@example.org>" 2048-bit RSA key, ID 6C1EEE05, created 2016-04-08 pub 2048R/6C1EEE05 created: 2016-04-08 expires: never usage: SC trust: ultimate validity: ultimate sub 2048R/8C44BD6A created: 2016-04-08 expires: never usage: E [ultimate] (1). Rui Afonso Pereira <email@example.com> [ultimate] (2) Rui Afonso Pereira <firstname.lastname@example.org> gpg> save
The key now has two UIDs attached.
$ gpg --list-keys 6C1EEE05 pub 2048R/6C1EEE05 2016-04-08 uid [ultimate] Rui Afonso Pereira <email@example.com> uid [ultimate] Rui Afonso Pereira <firstname.lastname@example.org> sub 2048R/8C44BD6A 2016-04-08
Exporting a Public Key
To add your public key to your development platform, such as GitHub, you must first export it. To do so, paste the text below, substituting in the GPG key ID you’d like to use. In this example, the GPG key ID is
$ gpg --armor --export 6C1EEE05 # Prints the GPG key, in ASCII armor format
You shall now proceed by adding the GPG key to your GitHub account.
We should now configure git to automatically gpgsign commits. This consists of pointing git to your signing key ID, and then enabling automatic signing of git commits.
$ git config --global user.signingkey <YOUR-SIGNING-KEY-PUB-ID> $ git config --global commit.gpgsign true
Furthermore, since we are using the
gpg2 binary on macOS, we should also tell this to Git:
$ git config --global gpg.program gpg2
Finally, from now on, every commit will be automatically signed. However, it is still required to insert the passphrase every single time.
Automatic Commit Signing on macOS
For security reasons, GnuPG always requires the passphrase every time it needs to sign something. This effectively means that the passphrase is required on every single git commit. If this sounds like a lot of work to you, we can automate the process, in a clear trade-off between security and convenience.
We shall start by installing the following binaries:
$ brew install gpg-agent pinentry-mac
Now navigate to your
GPGHOMEDIR, which is
$HOME/.gnupg by default, and add the following to your
# Uncomment within config (or add this line) use-agent # This silences the "you need a passphrase" message once the passphrase # handling is all set. batch
While inside the same directory, let’s configure our
# Enables GPG to find gpg-agent use-standard-socket # Connects gpg-agent to the macOS keychain via the brew-installed # pinentry program from GPGtools. This is the macOS magic, allowing # the gpg key's passphrase to be stored in the login keychain, # enabling automatic key signing. pinentry-program /usr/local/bin/pinentry-mac
Now, it’s time for the real trick in this whole process. For GPG to find the
gpg-agent, the later must be running, and there must be an environment variable pointing GPG to its socket. The following will either start
gpg-agent or set up the
GPG_AGENT_INFO variable if it’s already running. You should add this script to your
.zprofile so that it starts for every shell.
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
The high level diagram for the automatic signing is thus
git -> gpg -> shell/env variable -> gpg-agent -> pinentry -> keychain.
Automatic commit signing should now be successfully configured.