Signing commit messages with GPG in Linux

Signing git messages with GPG keys is important, otherwise someone may easily spoof your commit messages on (e.g.) Github (^1). There are different ways to sign commit messages such as with GPG, SSH, or S/MIME.

I prefer to keep my SSH-Keys I use for signing into accounts separate from my keys I use to sign commit messages. Therefore, for signing commit messages, I use explicit GPG keys. I have two GPG keys, one for my private mail and one for my work mail. For both, I added the public key parts to Gitlab(s) and Github, so I can commit from both private and work laptops.

Some of these steps are not as obvious, which is why I wrote down the process here.

Prerequises

In Linux or your Windows Subystem for Linux (WSL), install the dependencies:

sudo apt install gnupg2
sudo apt-get install -y pinentry-tty

pinentry-tty is a program that allows for secure entry of PINs or pass phrases. That means it tries to take care that the entered information is not swapped to disk or temporarily stored anywhere.

Create ~/.gnupg/gpg-agent.conf.

mkdir -p ~/.gnupg/
nano ~/.gnupg/gpg-agent.conf

.. and add:

pinentry-program /usr/bin/pinentry-tty

Add the following line to your .bashrc:

export GPG_TTY=$(tty)

e.g.

nano ~/.bashrc
# CTRL+O
source ~/.bashrc

Test:

echo "test" | gpg2 --clearsign

Create GPG key

gpg --full-generate-key
  • Key type:
    • either RSA and RSA, an asymmetric key pair for encryption and decryption
    • or a more modern and lean Elliptic Curve key
  • Bit length: 4096 for the highest amount of entropy.
  • Expiration date: 1y. Entirely up to you and your use case, you only want to prove your identity for Git, not guard state-sponsored secrets.

Export the public part:

gpg --armor --export mail@yourmail.com > /tmp/gpgkey.pub
# better use the exact id:
gpg --armor \
    --export OU34752JOWOI278732JDJJ732407z9874328 > /tmp/gpgkey.pub

To backup your private key:

gpg -o /tmp/private.gpg \
    --export-options backup \
    --export-secret-keys OU34752JOWOI278732JDJJ732407z9874328

To later restore your private key from backup (^2):

gpg --import-options restore --import private.gpg
gpg --edit-key mail@yourmail.com
> trust
> 5
> Y
> quit

Test

Enable signing of commit messages globally for git:

git config --global gpg.program gpg2
git config --global commit.gpgsign true
git config --global alias.logs "log --show-signature"
git config --global user.signingkey OU34752JOWOI278732JDJJ732407z9874328

Test:

cd /tmp/
mkdir test && cd test
echo "Some content" >> example.txt
git init
git add example.txt
git commit -m "test"
git logs

Cleanup:

cd ..
rm -rf test

Add to Gitlab

gpg --armor --export OU34752JOWOI278732JDJJ732407z9874328

Go to your Gitlab https://gitlab.yourgitlab.com/-/profile/gpg_keys and add the public key part of your GPG key.

Extending GPG keys

Check expiration:

gpg -K

> /home/user/.gnupg/pubring.kbx
> -----------------------------
> sec   rsa4096 2022-01-13 [SC] [expires: 2023-01-13]
>       ...
> uid           [ultimate] Your Name <mail@yourmail.com>
> ssb   rsa4096 2022-01-13 [E] [expires: 2023-01-13]

Run this command to list the private GPG key you just created, replacing with the email address for your key:

gpg --list-secret-keys \
    --keyid-format LONG mail@yourmail.com

> sec   rsa4096/IJAO23IDSALK324DASD 2022-01-13 [SC] [expires: 2023-01-13]
>       OU34752JOWOI278732JDJJ732407z9874328
> uid                 [ultimate] Your Name <mail@yourmail.com>
> ssb   rsa4096/9LD7324JSAL435 2022-01-13 [E] [expires: 2023-01-13]

First, note that there’s a short form for the ID (IJAO23IDSALK324DASD ) and a long form (OU34752JOWOI278732JDJJ732407z9874328). You can use both forms to select keys.

There may be multiple subkeys available:

  • E means the key is used for used for encryption
  • SC means the key is used for Signing/Pubkey
gpg --edit-key mail@yourmail.com
gpg --edit-key IJAO23IDSALK324DASD

Now you can set the expiration for the selected key:

gpg> expire
(follow prompts)
gpg> save

You have to make a decision about extending validity of vs. replacing the subkey(s). Replacing them gives you limited forward security (limited to rather large time frames). If that is important to you then you should have (separate) subkeys for both encryption and signing (the default is one for encryption only).

Private keys never expire. Only public keys do. Otherwise, the world would never notice the expiration as (hopefully) the world never sees the private keys.

Also, to expire (all) subkeys:

  • instead of 1, select key 1
  • * will be shown in front of “ssb”
key 1
...
save
>sec  rsa4096/IJAO23IDSALK324DASD
>     created: 2022-01-13  expires: 2024-01-03  usage: SC
>     trust: ultimate      validity: ultimate
>ssb* rsa4096/9LD7324JSAL435
>     created: 2022-01-13  expires: 2023-01-13  usage: E

Update git, gitlab:

git config --global user.signingkey IJAO23IDSALK324DASD

Copy new public key, and update in gitlab:

gpg --armor --export IJAO23IDSALK324DASD

Increase password timeout

The password timeout was too short for me. I want to unlock my GPG Key once per day, meaning I would need to increase the timeout to 8 hours.

nano ~/.gnupg/gpg-agent.conf

Add:

# increase gpg timout for password to 8 hours
default-cache-ttl 28800
max-cache-ttl 28800

Restart:

gpgconf --kill gpg-agent
gpg-agent --daemon

Public key exchange

In order to receive keys from other people, keyservers can be used.

For example, in case your public key was published on keys.openpgp.org, someone else may receive it to their local store by:

gpg --keyserver keys.openpgp.org --recv-keys IJAO23IDSALK324DASD