Skip to content

Instantly share code, notes, and snippets.

@phortuin
Last active March 25, 2024 17:09
Show Gist options
  • Save phortuin/cf24b1cca3258720c71ad42977e1ba57 to your computer and use it in GitHub Desktop.
Save phortuin/cf24b1cca3258720c71ad42977e1ba57 to your computer and use it in GitHub Desktop.
Set up a GPG key for signing Git commits on MacOS (M1)

Based on this blogpost.

To sign Git commits, you need a gpg key. GPG stands for GNU Privacy Guard and is the de facto implementation of the OpenPGP message format. PGP stands for ‘Pretty Good Privacy’ and is a standard to sign and encrypt messages.

Setting up

Install with Homebrew:

$ brew install gpg

Create config files for gpg and the gpg-agent. The agent will make sure you don’t have to type in your GPG passphrase for every commit.

$ mkdir ~/.gnupg
$ touch ~/.gnupg/gpg.conf ~/.gnupg/gpg-agent.conf

Open the gpg.conf file and add:

use-agent

In gpg-agent.conf, add the following lines to make sure your credentials are ‘kept alive’ (source):

default-cache-ttl 34560000
max-cache-ttl 34560000

Optionally, you can install a GUI for entering your passphrase. You don’t need to, but the default is a CLI program and might not provide a nice user experience. With pinentry-mac you can choose to save your passphrase in your MacOS keychain. That’s up to your personal preference.

$ brew install pinentry-mac

If you installed pinentry-mac, make sure to configure the agent. Open the gpg-agent.conf file and add this line:

pinentry-program /opt/homebrew/bin/pinentry-mac

Note: if you’re on Intel, /opt/homebrew should be /usr/local.

Add the following lines to ~/.zshrc (the GPG_TTY environment variable is a requirement for GPG; the second line launches the gpg-agent when you open a new shell):

export GPG_TTY=$(tty)
gpgconf --launch gpg-agent

To effectuate the changes to .zshrc, type:

$ source ~/.zshrc

Create GPG keypair

Now that your environment is properly set up, we need to generate a public/private GPG keypair.

$ gpg --full-gen-key

A wizard is printed to your terminal. You should configure as follows:

  • Kind of key: 4 (RSA, sign only)
  • Keysize: 4096
  • Expiration: 2y (your key will expire after 2 years; you should set a reminder somewhere)
  • Real name: <your github username>
  • Email address: <your email address>

Note: I heartily recommend setting your email address to your 'noreply' GitHub address: username@users.noreply.github.com. You can find your email address on the GitHub Email settings page. Note that if you created a GitHub account after July 2017, your address will also have an ID prefixed to your username; read more here.

The final step in setting up the GPG keypair is typing a passphrase. Make sure it is strong and you have it safely stored in your password vault (I recommend Bitwarden). Whoever has your passphrase can sign your commits and there is no way to prove it wasn’t you.

After creating the keypair, output similar to the following is printed to your terminal:

pub   rsa4096 2021-11-12 [SC] [expires: 2023-11-12]
      AAABBBCCCDDDEEEFFF1112223334445556667778
uid                      username <username@users.noreply.github.com>

The string of characters is your key ID. To confirm you can sign messages with your newly created key, enter in your terminal:

$ echo 'it works' | gpg --clearsign

A message similar to this should appear:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

it works
-----BEGIN PGP SIGNATURE-----
<many characters>
-----END PGP SIGNATURE-----

Adding to Git

We need to add your key to your git config, and to GitHub. First, you need to find the key ID. The (short) ID uses the last 8 characters of the key that was printed to the terminal before. You can retrieve it:

$ gpg --list-secret-keys --keyid-format SHORT

Outputs:

/Users/username/.gnupg/pubring.kbx
----------------------------------
sec   rsa4096/56667778 2021-11-12 [SC] [expires: 2023-11-12]
      AAABBBCCCDDDEEEFFF1112223334445556667778
uid         [ultimate] username <username@users.noreply.github.com>

The 56667778 bit after rsa4096/ is your short key ID. We need it to configure Git to sign commits and tags. Replace the user.signingkey value below with your own key ID:

$ git config --global user.signingkey 56667778
$ git config --global commit.gpgSign true
$ git config --global tag.gpgSign true

Git needs to know your email, and it needs to be the same as the one for your GPG key. This email address needs to be verified on GitHub as well. If you use your ‘private’ GitHub email, that’s already the case.

$ git config --global user.email username@users.noreply.github.com

Finally, you need to add your public GPG key to GitHub. Again, make sure to replace the ID with your own ID:

$ gpg --armor --export 56667778

Outputs:

-----BEGIN PGP PUBLIC KEY BLOCK-----

<many characters>
-----END PGP PUBLIC KEY BLOCK-----

You need to copy the whole block and add it to GitHub. If you’re not sure what to copy, use this command:

$ gpg --armor --export 56667778 | pbcopy

The | pbcopy part will pipe the output of the first part directly to your copy-paste memory.

Go to the GitHub SSH and GPG keys section, click [New GPG key] and paste into the box. Click [Add GPG key], and you’re done!

After getting this done, and after having made your first signed commit, you can see the ‘Verified’ badge on GitHub for that commit (see an example here). Your GPG key ID will be shown when the badge is clicked.

Visual Studio Code

If you use Visual Studio Code, you can turn on signing by changing a setting.

Open VSCode, go to Preferences > Settings, and search for git.enableCommitSigning. Turn this setting on, and you’re good to go.

Troubleshooting

1. Kill the agent

If for some reason you can’t sign, simply kill the agent. It will restart when needed:

$ gpgconf --kill gpg-agent

2. Older/remote shells

On older MacOS versions or certain (remote) shells, you might encounter the error inappropriate ioctl for device. (This error might also turn up if you haven’t configured the GPG_TTY environment variable correctly, see above for instructions.) More context here. You can fix this by using the so called ‘loopback’ option to enter your passphrase directly on the CLI.

Edit gpg.conf and add:

pinentry-mode loopback

Edit gpg-agent.conf and add:

allow-loopback-pinentry

Now, when the agent wants your passphrase it will simply render a basic password input on the CLI:

$ echo 'it works' | gpg --clearsign
Enter passphrase:

3. Using SourceTree

If you use SourceTree, you should point it to the right binary. A solution is posted on Stack Overflow, make sure to also follow this comment.

4. X.509 Certificate

If the commit still can’t be signed, it could be that you use an X.509 certificate to sign your commits (this might be the case in a corporate environment, for example). Tell Git about it:

$ git config --global gpg.x509.program gpg

If that doesn’t cut it, install smimesign, an S/MIME signing utility for use with Git with Homebrew:

$ brew install smimesign

Then, configure git to use an X.509 certificate and smimesign as the gpg program.

$ git config --global gpg.x509.program smimesign
$ git config --global gpg.format x509

H/t to @benhickson and this excellent GitLab guide.

5. I don’t know what .zshrc is or how to edit it

The .zshrc file is a configuration file for your zsh shell (rc stands for runcom) that might not exist yet on your system. You can easily create it like this:

$ cd ~
$ touch .zshrc

These kinds of 'dot files' are usually not edited with standard MacOS apps like TextEdit. You can use vi or nano from the command line instead. Find some useful instructions here.

H/t to @intricateavocado

@safalnpane
Copy link

Great tut! Thanks!

@jfarleyx
Copy link

Very helpful. Thank you.

@psi59
Copy link

psi59 commented May 26, 2022

Thank you!

@julien-michaud
Copy link

thx !

@lab34
Copy link

lab34 commented Jun 11, 2022

Super top !

@benhickson
Copy link

benhickson commented Jun 15, 2022

Did not work for me. Received:

error: gpg failed to sign the data
fatal: failed to write commit object

Edit: fixed it by running this after all the other instructions:

explicitly tell Git to use gpg

git config --global gpg.x509.program gpg

@nick-oxborough
Copy link

great instructions. thanks!

@jaimedlopes
Copy link

Really helpful! Thank you!

@brian-mukuka-bptn
Copy link

Did not work for me. Received:

error: gpg failed to sign the data
fatal: failed to write commit object

Edit: fixed it by running this after all the other instructions:

explicitly tell Git to use gpg

git config --global gpg.x509.program gpg

I am getting this error on m1 . Any help ?

@wblondel
Copy link

wblondel commented Sep 3, 2022

Thank you very much for this very well-written guide!

@chris-kobrzak
Copy link

I've been following the official GitHub guide and had no luck with setting this thing up on my Mac M1. I bumped into your Gist, followed your much more detailed instructions for the command line setup and voila - everything is working!

Thank you so much, Anne!

@907Resident
Copy link

Thanks so much. Followed the steps and now I am all set up!

@thelazyprog
Copy link

It is very helpful to have such a great Gist, since I am lazy. Thanks a lot!

@amelieykw
Copy link

Thank you very much! This is perfect!

@phortuin
Copy link
Author

phortuin commented Nov 9, 2022

[snip]
I am getting this error on m1 . Any help ?

@brian-mukuka-bptn I updated the Gist with some further instructions and a link to a GitLab guide. Do those help? Let me know your findings. Thanks

@phortuin
Copy link
Author

phortuin commented Nov 9, 2022

Did not work for me. Received:

error: gpg failed to sign the data
fatal: failed to write commit object

Edit: fixed it by running this after all the other instructions:

explicitly tell Git to use gpg

git config --global gpg.x509.program gpg

Thanks @benhickson, much appreciated. I added your findings to the troubleshooting part. Can you confirm that you are using an X.509 certificate on your machine? I can't reproduce this error so I just inferred the solution from other tutorials and a bit of research.

@juanky201271
Copy link

Thanks a lot. These directions are amazing...

@uncenter
Copy link

uncenter commented Dec 9, 2022

I'm a little confused on what to do from here:
Screenshot 2022-12-09 at 2 16 41 PM
I did the pinentry-mac, and what does this part want me to do?

@uncenter
Copy link

uncenter commented Dec 9, 2022

I'm a little confused on what to do from here: Screenshot 2022-12-09 at 2 16 41 PM I did the pinentry-mac, and what does this part want me to do?

I figured it out. For anyone with the same issue, this answer from here solved it. I have provided it right here (edited) as well for convenience's sake.

The ~/.zshrc doesn’t exist by default in macOS so you need to create it. The ~/ translates to your home directory and the .zshrc is the ZSH configuration file itself.

So open up a Terminal window and create that file like this; I am using nano as a text editor but feel free to use whatever text editor you feel comfortable with:

nano ~/.zshrc

And then add the lines provided from above
export GPG_TTY=$(tty)
gpgconf --launch gpg-agent

Now to save the file in nano, hit ctrl+X. When it prompts:

Save modified buffer (ANSWERING "No" WILL DESTROY CHANGES) ?

Type “Y” and then you will get a new prompt which looks something like this; note the path /Users/giacomo/ will match your local user’s path:

File Name to Write: /Users/giacomo/.zshrc

Now hit return and the file will be saved and you will now be back to the command line prompt in the Terminal. If you now exit from the Terminal and then open a new window, the ~/.zshrc settings should now be loaded in.

@phortuin
Copy link
Author

@intricateavocado Good to know you figured it out. I wrongly assumed anyone interested in git commit signing to have some experience with the zsh shell and creating a .zshrc is pretty much on top of the list of todo's when working with the shell. I will add a little explainer in the instructions above. Thanks!

@uncenter
Copy link

@phortuin Yes! I don't have much experience with zsh shell but your guide was hugely helpful. Thanks for putting this together!

@nawarian
Copy link

nawarian commented Jan 3, 2023

Hey bud; this helped me a lot. Thank you!

@stramel
Copy link

stramel commented Jan 17, 2023

Thank you for putting this together! The only part that was missing for me was the unsafe directory permissions for .gnupg. I used this script to fix it. https://gist.github.com/oseme-techguy/bae2e309c084d93b75a9b25f49718f85

@phortuin
Copy link
Author

Hey @stramel, thanks for the heads up. Did you run anything as root (sudo) during the process? That might explain why your .gnupg folder had unsafe permissions. If not, I'll add your comment to the troubleshooting section.

@wilmer-data-days
Copy link

Excellent guide. Thank you @phortuin

@Unfaced85
Copy link

good guide, thanks. just enabled this one in our Github Enterprise Server - worked like a charm

@ziakhalid
Copy link

Good Guide, Thanks :)

@lloydj2ons
Copy link

Thank you so much, after trying to get it work for ages, this sorted it. Totally clear and did not miss out vital steps.

@kiki7766
Copy link

kiki7766 commented Oct 4, 2023

Thank you for this amazing step by step guidance! Worked like a charm on Mac and with some modifications on Windows! 💯

@PiotrMur
Copy link

Hi, @phortuin thank you for preparing such a great manual, it helped me a lot!
One thing yet seems not to work work me. I've been trying to upgrade/update gpg-agent. Used $ gpgconf --kill gpg-agent, brew upgrade gpg-agent, brew upgrade gpgconf and nothing could upgrade the gpg-agent version. Do you happen to encounter such a problem? If so, any ideas how to fix it?

@uncenter
Copy link

For people coming in the future I also recommend you take a look at SSH keys, which I found easier to setup thanks to this guide from GitHub.

@kartikver15gr8
Copy link

Thanks a lot man, I was struggling a lot while setting the GPG keys on my M2 Mac, you've made the whole process very easy, deserve a ⭐

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment