While there’s plenty of guides that try to give a full rundown of GPG and how it works, this is just a straightforwards breakdown for creating and using a new GPG key using proper modern defaults (the long breakdown may come later).
What GPG is used for in Git is signing commits. No encryption/decryption necessary.
As a result, we’ll be generating:
- A master key with no expiration date, which can be used to certify any new subkeys
- A subkey for signing, with an expiration date of 5 years
- (optionally) An encryption key for future use
tl;dr
1 | export KEYUID='Richard H. Pajerski II (devedge) <richard.pjski2@proton.me>' |
first things first
The system and version I’m running is:
SoC: Apple M3 Pro
gpg (GnuPG) 2.4.7
libgcrypt 1.10.3
GPG was installed using Homebrew (brew install gnupg
)
To avoid incomprehensible errors like this:
1 | error: gpg failed to sign the data: |
you need to add this to ~/.gnupg/gpg.conf
:
1 | use-agent |
and this to ~/.gnupg/gpg-agent.conf
:
1 | allow-loopback-pinentry |
And then run:
1 | echo RELOADAGENT | gpg-connect-agent |
in your terminal to reload the GPG agent. (Thanks to Daniel15’s post)
Why?
From what I can gather, GPG considers passwords typed over stdin on the CLI to be insecure. Rather than utilizing existing tools such as sudo
, they wrote their own, pinentry
. However, instead of making things more secure, this complication and associated incomprehensible error messages drives users to avoid using GPG at all (this is a very common theme with GPG).
You could also install another program, pinentry-mac
, but this will open up an annoying popup window every single time GPG needs your password. The above solution will force GPG to use your terminal, just like every other application. If you would like to install pinentry-mac
however, this blog post is a good starting point.
generating the gpg keys
Many guides still show how to generate RSA keys. However, the modern alternative for a while has been ED25519. To use it, run:
master key
1 | gpg --quick-generate-key '<User ID>' ed25519 cert never |
This generates a Certify-only (cert
) ed25519
key that never
expires.
The <User ID>
can generally be anything. The standard format follows the convention of:
Your Name (comment) <your.email@address.com>
However, keep in mind that for commits to appear as valid on websites such as Github, you need to include an email that matches one of your verified Github emails. The angle brackets surrounding the email (< >
) are required.
Once you hit Enter, it’ll ask you to provide a password. If you followed the configuration steps in the first section, it’ll only ask for it once, so be sure to type/paste it correctly.
GPG will then print the new key:
1 | pub ed25519 2024-12-20 [C] |
The [C]
stands for Certify. Generally, since GPG relies on a ‘web of trust’ where people hold onto your key for a long time, you want your root level key to rarely expire. It will also have no abilities, existing solely to sign subkeys that you actually use for day-to-day work.
signing key
The signing key is a subkey of the master key, and they are both logically considered part of the same GPG key.
1 | gpg --quick-add-key 55BE5089F634003042AE70985E88702C976C97B1 ed25519 sign 5y |
This generates a Sign-only (sign
) ed25519
key that expires in 5 years (5y
). The 55BE5...
string is the master key’s full fingerprint, which was printed above.
To continue signing after this time has passed, you’ll need to generate a new subkey again with the master key. The idea behind this is that if you’ve had your key copied/stolen somehow, it can’t be used forever.
Note on ed25519 encryption
Adding an encryption key involves a very small change: instead of ed25519
, you’ll need to specify cv25519
for the encryption algorithm:
1 | gpg --quick-add-key 55BE5089F634003042AE70985E88702C976C97B1 cv25519 encr 5y |
listing keys
While there’s numerous ways to list your keys, I’ve found that you generally don’t need to worry about most of them. Regardless of how many subkeys your GPG key has, referencing either the master key’s ID or any part of your UID will allow GPG to pick the appropriate subkey. If you have multiple subkeys with the same function, GPG picks the most recently created one.
However, here’s a few I’ve found useful:
The default:
1 | $ gpg --list-keys devedge |
Same as above, but with the master key ID split into 4-character chunks:
1 | $ gpg --fingerprint devedge |
You can also go all-out and list each of the subkey fingerprints:
1 | $ gpg --list-keys --with-subkey-fingerprints --keyid-format=LONG devedge |
Finally, to export your public key in ASCII plaintext to share, run:
1 | $ gpg --armor --export devedge |
Or directly exported to a file:
1 | gpg --armor --export --output devedge.asc devedge |
signing commits with them
A good idea to ensure you never forget to sign commits is to run the command:
1 | git config --local commit.gpgsign true |
in the root directory of your git repository. This will add a configuration entry in .git/config
for that repo, automatically requiring a signature for every commit. This is more convenient than having to manually type the -S
flag to sign every git commit
as a one-off.
To specify your new key for that repository, run one of the following:
1 | # some part of the UID |
You could also (confusingly) specify the key fingerprint or full key ID of either the master key or the signing key:
1 | # master key fingerprint (last 16 characters of full key ID) |
GPG will automatically pick the signing key you created. If you have multiple, it appears to use the most recently created one.
If you want to use this key globally for every repository on your machine, replace the --local
flag with --global
. This will place the configuration options in your home directory instead, inside ~/.gitconfig
.
resources
- Git guide to signing your work:
https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work - Github guide to using your GPG key with Github:
https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key - Fixing GPG “Inappropriate ioctl for device” errors:
https://d.sb/2016/11/gpg-inappropriate-ioctl-for-device-errors - Guide for
pinentry-mac
:
https://velvetcache.org/2023/03/26/a-peek-inside-pinentry/ - Another article covering a slightly different solution to signing commits over ssh:
https://ertt.ca/blog/2022/01-10-git-gpg-ssh/