Signing Git Commits

Signing your commits on git with PGP.

Signing your git commits gives everyone the added trust that the work you commit, is truly by you and not a malicious actor pretending to be you or that it hasn’t been modified in transport. For a look at how serious the implications are read the Spoof a commit on Github article and try spoof a git commit yourself!

Setting up signing your commits into git isn’t all that difficult either. This guide takes you through the process and has some bonus tips down below!

Install the toolchain - GPG

First we need to setup the GnuPG Toolkit on your machine.

sudo apt-get install gnupg2
Most Linux installations ship with gpupg tools, gnupg2 is better. It's also best to set it as the app to use:
git config --global gpg.program gpg2

brew install gpg

choco install gnupg

Generate or Import a Key

Once the toolkit is installed, we can opt to generate and create a new GPG Key, or import one - especially if you’re on Keybase.

Generate a new key

Once installed, we need to generate an initial GPG Key.

$ gpg --full-generate-key
gpg (GnuPG) 2.2.19; 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.

gpg: directory '/home/thushan/.gnupg' created
gpg: keybox '/home/thushan/.gnupg/pubring.kbx' created
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)
  (14) Existing key from card

Make sure you select option (1) RSA & RSA (default) and opt for a 4096 bit key.

Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
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) 6m

After which you’ll be asked to select a timeframe, we either rotate keys every 6 months or every 1 year. Then enter your identifiable information and ensure the email address you enter matches that of your Github account.

NOTE: If you get an error about the gpg agent not running like below:

gpg: can't connect to the agent: IPC connect call failed
gpg: agent_genkey failed: No agent running
Key generation failed: No agent running

You can run the agent with:

$ gpg-agent --daemon

And try again.

Once generation has completed, you’ll find your newly minted key in the .gnupg folder in your home directory.

You can list the key with these two commands:

$ gpg --list-secret-keys
sec   rsa4096 2020-06-02 [SC] [expires: 2020-11-29]
uid           [ultimate] Thushan Fernando (Blog example update #22) <>
ssb   rsa4096 2020-06-02 [E] [expires: 2020-11-29]

$ gpg --list-secret-keys --keyid-format LONG
sec   rsa4096/E27FF31D0EB10C13 2020-06-02 [SC] [expires: 2020-11-29]
uid                 [ultimate] Thushan Fernando (Blog example update #22) <>
ssb   rsa4096/76A398C8592EE9B8 2020-06-02 [E] [expires: 2020-11-29]

The latter command gives you the [KEYID] too - which in this example is E27FF31D0EB10C13.

Import GPG Key from Keybase

Importing an existing GPG Key from your Keybase is easy as pie! Make sure Keybase is properly installed on your system (you need the CLI tools which are installed with the keybase App) and follow along:

Export the public key from Keybase and import it to your local GPG:

$ keybase pgp export | gpg --import

The output from the command will give you the key you’re importing (the KEYID in the preceding examples).

If you have multiple keys (check with keybase pgp list), you can export individual keys with:

$ keybase pgp export -q [KEYID]

Subsequently, export the secret key from Keybase and import it into GPG - and you’ll be prompted to enter your passphrase.

$ keybase pgp export --secret | gpg --import --allow-secret-key-import

For multiple keys, you’ll have to use the -q [KEYID] switch as earlier.

$ keybase pgp export -q [KEYID] --secret | gpg --import --allow-secret-key-import

Get your public key block

Once your key has been created or imported, we need to pull out the public key with the GPG [KEYID], save it to a file so you can copy it easily.

gpg --armor --export [KEYID] > gpg-key.txt

This public key you can socialise with the Github Account. Unfortunately it’s not available in BitBucket yet, but see if they eventually will with Feature Request BCLOUD-3166.

Now you can use your key within git by associating it with the user.signingkey setting and tell it to auto sign your future commits too:

$ git config --global user.signingkey [KEYID]
$ git config --global commit.gpgsign true

Now each time you create a commit, you’ll be prompted to enter your passphrase. If you selected not to run the last command and autosign each commit, you can manually sign your commits by appending the -S switch in your commit.

$ git commit -s -m "This is the only thing I commit to."

And if you want to sign all your previous commits now that your keys are setup, see Signing Previous Git Commits guidance.

Get gpg to save your passphrase

You can use the gpg-agent remember your passphrase to make it easy whilst you’re at work.

Modify the ~/.gnupg/gpg-agent.conf by adding the following two lines which asks the agent to save the passphrase for a timeframe. The *-ttl is in seconds, so for the example below, it’s 9hrs (60 * 60 * 9 = 32400).

default-cache-ttl 32400
max-cache-ttl 32400

If you’re using Powershell, here’s a handy script that will create the config file (as it won’t exist in Windows) and set the cache automatically for you.

Set-Content -Path ~\.gnupg\gpg-agent.conf -Value "default-cache-ttl 32400$([System.Environment]::NewLine)max-cache-ttl 32400"

Once configured, you can restart the daemon and verify the values.

$ gpgconf --reload gpg-agent

And verify the settings are enabled:

gpgconf.exe --list-options gpg-agent

It should emit something along the lines of..

default-cache-ttl:24:0:expire cached PINs after N seconds:3:3:N:600::32400
max-cache-ttl:24:2:set maximum PIN cache lifetime to N seconds:3:3:N:7200::32400

You’re set to go sign commits!

Related Articles