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:

$ gpg --full-generate-key

This will launch the prompt that will ask you all needed parameters. Unlike other commands you may find, this one will allow you to configure an expiration date.

  1.  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?
    
    The default option is good enough
  2.  RSA keys may be between 1024 and 4096 bits long.
     What keysize do you want? (3072)
    
    I see no reason not to set it to 4096 to max out securrrrity 😎
  3.  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
    
    I prefer to make unexpired keys
  4. The last two questions are about your real name and email. Use the same ones you use in git.
    1. 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 backup 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:

      .config/fish/conf.d/gpg.fish
      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!