Blog Cover

GPG + Git basics: How to generate keys, sign commits, and export keys to another machine

Author profile image
Aitor Alonso

Dec 26, 2022

8 min read

In this article I will treat some interesting topics related to GPG keys and Git. I will explain what are GPG keys and what are they used for, how to generate them, how to use them to sign commits (and also how to add them to GitHub), and how to export them to another machine with ultimate trust.

What are GPG keys and what are they used for?

PGP ("Pretty Good Privacy") is an encryption program that provides cryptographic privacy and authentication for data communication. PGP is used for signing, encrypting, and decrypting texts, e-mails, files, directories, etc. It can be used to sign git commits too, which allows you and anyone else to verify the true authorship of a commit, in a cryptographic safe way.

This is a very useful feature to ensure that the commit is really from the author, and not from a malicious person trying to impersonate the author (which it is easy to achieve with Git, as all you have to do is to change the author name and email in the commit to "impersonate" someone).

If you ever wonder what are the "Verified" green check that appears near some commits in GitHub, those are commits that are signed with a GPG key that belongs to the author of the GitHub account and identifies the email used in the commit. More on that later.

A verified commit

GPG uses asymmetric cryptography to work. This means that there are two keys: a public key and a private key. The public key is used to encrypt data and verify signatures, and the private key is used to decrypt and sign data. The public key can be shared with anyone, but the private key must be kept secret.

Generating GPG keys

To generate a GPG key, you can use the gpg command. You can found it in most Linux distros as a package with the gnugp name, and also in MacOS with brew. Once installed, we can continue with the following:

gpg --full-generate-key

After executing this command, you will come through a guided process where you will be asked to select the type of key you want to generate. I recommend you to choose the default option, which is RSA and RSA, and also to choose a key size of 4096 bits, like so:

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.

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

GnuPG needs to construct a user ID to identify your key.

Real name: John Doe
Email address: test@test.com
Comment:
You selected this USER-ID:
    "John Doe <test@test.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?

Ensure that you use a real email address, as this will be used to identify the key. You can also add a comment if you want. You can add a passphrase to the key if you want, but it is not mandatory. If you add a passphrase, you will have to enter it every time you use the key (similar to passphrase-protected SSH keys), which adds an extra layer of security.

Adding the new generated keys to Git to sign commits

Once you have generate the key, you can add it to Git to start signing commits. First, we need to know the key ID. To do so, we can use the following command:

gpg --list-secret-keys --keyid-format=long
/home/aalonso/.gnupg/pubring.kbx
------------------------------------
sec   rsa4096/85D3AAC2A8C4989E 2022-12-26 [SC]
      72DA3C7163C7D8FE854DC67D85D3AAC2A8C4989E
uid                 [ultimate] John Doe <test@test.com>
ssb   rsa4096/8FD5D3E520A1B19C 2022-12-26 [E]

You can see an ID after sec rsa4096/<THIS_IS_THE_ID>. In this case: 85D3AAC2A8C4989E.

Now open your existing .gitconfig file (or create it if it doesn't exist under $HOME/.gitconfig) and add the following lines:

[user]
  name = John Doe
  email = test@test.com
  signingkey = 85D3AAC2A8C4989E

Now, you can start signing commits. To do so, you can use the -S flag when you commit:

git commit -S -m "This is a signed commit"

If you want to sign all commits by default, you can add the following line to your .gitconfig file under the previous added [user] section:

[commit]
  gpgsign = true

Now, you can make your commits as always and the will be automatically signed.

git commit -m "This commit is signed by default"

Adding the new generate keys to GitHub for the "Verified" green check

First, we need to get the public key, which GitHub used to verify the commit signature. To do so, we can use the following command given the ID we got before:

gpg --armor --export 85D3AAC2A8C4989E
-----BEGIN PGP PUBLIC KEY BLOCK-----
blah blah blah
-----END PGP PUBLIC KEY BLOCK-----

Copy the entire GPG key, beginning with -----BEGIN PGP PUBLIC KEY BLOCK----- and ending with -----END PGP PUBLIC KEY BLOCK-----, and follow the official steps to add it to your GitHub account. Similar step-by-step instructions can be found for other platforms such as GitLab or Bitbucket in their related official docs.

Exporting GPG keys to another machine

If you want to export your GPG keys to another machine, so let's say you have a laptop and a desktop, you can do so by following the next steps.

First, in the machine where you generate the GPG keys, you need to export the secret. To do so, you can use the following command:

gpg --export-secret-key -a test@test.com > test_key.priv

If your private key is protected by a passphrase, it will be asked. Now, copy the exported key to the machine where you want to import it. Be sure to do this via a safe channel, so you don't compromise your private key, specially if it is not protected by a passphrase. You can use scp for that.

scp test_key.priv user@host:/path/to/destination

Once the exported private key file is in the destination machine, you can import it with the following command:

gpg --import test_key.priv

Congrats! You have successfully exported your GPG keys to another machine. However, if you does gpg --list-secret-keys --keyid-format=long, you will see the key is not trusted by default, as it is from an "unknown" source.

/home/aalonso/.gnupg/pubring.kbx
------------------------------------
sec   rsa4096/85D3AAC2A8C4989E 2022-12-26 [SC]
      72DA3C7163C7D8FE854DC67D85D3AAC2A8C4989E
uid                 [unknown] John Doe <test@test.com>
ssb   rsa4096/8FD5D3E520A1B19C 2022-12-26 [E]

This won't affect your ability to use it to sign commits, but it is a good thing to solve this in case you need it for another tasks. To do so, you can use the following command:

gpg --edit-key -a test@test.com

Now, in the gpg prompt, use the trust command to ultimately trust the key, and save the changes with the save command.

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.

Secret key is available.

sec  rsa4096/85D3AAC2A8C4989E
     created: 2022-12-26  expires: never       usage: SC
     trust: undefined     validity: unknown
ssb  rsa4096/8FD5D3E520A1B19C
     created: 2022-12-26  expires: never       usage: E
[ unknown] (1). John Doe <test@test.com>

gpg> trust
sec  rsa4096/85D3AAC2A8C4989E
     created: 2022-12-26  expires: never       usage: SC
     trust: undefined     validity: unknown
ssb  rsa4096/8FD5D3E520A1B19C
     created: 2022-12-26  expires: never       usage: E
[ unknown] (1). John Doe <test@test.com>

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

sec  rsa4096/85D3AAC2A8C4989E
     created: 2022-12-26  expires: never       usage: SC
     trust: ultimate      validity: unknown
ssb  rsa4096/8FD5D3E520A1B19C
     created: 2022-12-26  expires: never       usage: E
[ unknown] (1). John Doe <test@test.com>
Please note that the shown key validity is not necessarily correct
unless you restart the program.

gpg> save

That's it! Now, the key should show ultimate trust as in the original machine where it was generated. Don't forget to update the .gitconfig in the destination machine to use the new imported GPG key to sign commits!