Signed Git commits allows us to be sure that a commit has been made by the user (name and email) that the git log says.

To do so we’ll use a GPG key.

Generate a GPG key

First we need to install GPG:

brew install gnupg

Once it’s installed we’ll create the key:

gpg --full-gen-key

We’ll select RSA and RSA kind of key:

Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection? 1

Then, we’ll chose 4096:

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits

We’ll also chose to not expire:

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)
Key does not expire at all
Is this correct? (y/N) y

After getting a verification step to ensure that everything is ok we’ll be asked for a password that will be requested every time we do a Git commit.

Git setup

We’ll need to get the GPG key ID:

gpg --list-secret-keys --keyid-format LONG <your_email>

The output will be:

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
sec   rsa4096/DDDDAAAA11113333 2021-05-12 [SC]
      1111792333392BE6F4431E32DA917BCEB3666622
uid                 [ultimate] Your Name <your.email@example.com>
ssb   rsa4096/1234BF57VVVV3333 2021-05-12 [E]

We need the sec ID: DDDDAAAA11113333.

With it we can then run this command to get the public key block:

gpg --armor --export DDDDAAAA11113333

Use this public key to configure your GitLab or GitHub account. You’ll find a place to add GPG keys in your user preferences.

Then we also need to setup our computer to be able to use that key:

git config --global user.signingkey DDDDAAAA11113333
git config --global gpg.program gpg

Add the following text to ~/.gnupg/gpg.conf. Create the file if doesn’t exist:

no-tty
use-agent

Export the GPG_TTY environment variable in your .zshrc or .bash_profile file:

export GPG_TTY=$(tty)

Then if we want to just sign the commits sometimes we can just add -S parameter when doing a git commit:

git commit -S -m "Test"

But if we want we can set up Git to do it always:

git config --global commit.gpgsign true

Common Errors

If you get the following error:

gpg: signing failed: Inappropriate ioctl for device
gpg: [stdin]: clear-sign failed: Inappropriate ioctl for device

Check if GPG_TTY is well exported. Maybe you are missing a source ~/.zshrc call.

If you get the following error when doing commits:

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

To troubleshoot try first to execute: echo "test" | gpg --clearsign, you might get something like:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

test
gpg: signing failed: No pinentry
gpg: [stdin]: clear-sign failed: No pinentry

If you get that you either need to install pinentry:

brew install pinentry

or kill gpg agent after upgrading it:

gpgconf --kill gpg-agent

Resources