Sign commits with GPG

⏳ 6 min read📅 2021-08-21

GPG is a very complex tool. However, to sign git commits you only need a handful scope of it. This post will show you how to set up and manage your GPG keys with ease.

🔗Why?

When you commit something, Git inserts your name and email in the commit message.

Check my other post on how to elaborately setup different accounts for different folders.

In short, these params are taken from a config file or environment variables. This means that anybody can make a commit with your name and email and no one will be able to make a difference between you and the scammer.

This is a problem especially in huge Open Source projects: hackers really want to sneak commits into theirs source tree.

🔗How can GPG help?

GPG allows us to confirm that commits were made by who we think made them. Asymmetric cryptography is awesome! To use it, you will need to generate two keys : public and private. Git will then sign your commits with the private key. The public key needs to be uploaded somewhere:

Anybody will be able to verify that the signature is valid. SCMS can do this automatically, since you’ve uploaded your public key there. That’s why you can see the “Verified” label next to some commits:

Example of verified commit

Example of verified commit

One other feature is that you can enable “All commits must be signed” options in your SCMS, so nobody will be able to mimic you .

🔗How to set it up?

I’m using Fish shell, but it should be easy to port these code snippets to the shell of your choice.

First, we need to help GPG to correctly detect a TTY.

.config/fish/conf.d/gpg.fish
set -x GPG_TTY (tty)

In most cases, this is not needed. However, termux environment on my Android phone required this.

Now we can generate the keys. Unlike other commands you may find, this one will allow you to configure an expiration date:

$ gpg --full-generate-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)
	(14) Existing key from card
Your selection?
> 1 # The default option (1) is good enough

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072)
> 4096 # I see no reason not to set it to 4096 to max out securrrrity :sunglasses:

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
	```
> 0 # I prefer to make unexpired keys

The last two questions are about your real name and email. Use the same ones you use in git.

Now when the generation is complete you can view your keys with this command:

$ gpg --list-secret-keys
sec   rsa4096 2021-05-11 [SC]
      54907B090CE0C406B02CE0F65A031208A03B7BDA
uid           [ultimate] Sergei Gureev <bemyak@pm.me>
ssb   rsa4096 2021-05-11 [E]

The long number here (5490…) is a key identifier. It will be used a lot, so take a note of it.

Okay, now we can get and upload our public key:

gpg --armor --export 5490…

This will print out a huge key. Just copy and paste it to your SCMS.

You can already start using it with git. To create a signed commit:

git commit -S

To sign all commits by default:

git config --global commit.gpgsign true

If your Git user differs from the GPG email, you can tell Git explicitly which key to use:

git config --global user.signingkey 5490…

Cool! Are we done? Not quite!

Always back up your keys!

🔗Backup and restore keys

GPG won’t let you just view the private key. They are stored in a vault (~/.gnupg/pubring.kbx by default). However, backing up the vault is not recommended: it contains not only your keys, but others that your system trusts:

$ strings .gnupg/pubring.kbx | egrep '<.+@.+>'
Spotify Public Repository Signing Key <tux@spotify.com>
Thomas Dickey <dickey@invisible-island.net>
Emanuel Borsboom <emanuel@borsboom.io>
Emanuel Borsboom <manny@fpcomplete.com>
Emanuel Borsboom <emanuel.borsboom@gmail.com>

If you will just replace this file on a new system, a lot of things might break!

The proper way to do this is to export your keys first:

mkdir ~/.gnupg/export
gpg -a --export-secret-key -o ~/.gnupg/export/private-keys.asc

Then you can import them:

gpg --import ~/.gnupg/export/private-keys.asc

🔗Transfer keys to another machine

I use yadm to manage my dotfiles and transfer them between different machines. You can’t just put the backup there, it needs to be encrypted first!

~/.config/yadm/encrypt
.gnupg/export/*

Now you can run

yadm encrypt

It will take all files listed in ~/.config/yadm/encrypt and put them into .local/share/yadm/archive. This archive can then be synced safely.

On the target system you run

yadm decrypt

and enter you passphrase to extract files from the archive back to their directories.

Run

gpg --import ~/.gnupg/export/private-keys.asc

to import it to your keystore.

🔗Helpers

I use these fish functions that help me automate some routine:

set -x GPG_TTY (tty)

function gpg-export
        mkdir ~/.gnupg/export
        gpg -a --export-secret-key -o  ~/.gnupg/export/private-key.asc
end

function gpg-import
        gpg --import ~/.gnupg/export/private-key.asc
end

function gpg-ls
        gpg --list-secret-keys --keyid-format LONG
end

function gpg-get
        gpg --armor --export $argv[1]
end

The function names should be self-explanatory.

🔗Outro

Hope this helped you to secure your online presence :) Stay safe!