Skip to content

Instantly share code, notes, and snippets.

@viktor-evdokimov
Forked from troyfontaine/1-setup.md
Last active July 12, 2022 19:40
Show Gist options
  • Save viktor-evdokimov/fd3b569f9ea93ea77ee7300c34bed897 to your computer and use it in GitHub Desktop.
Save viktor-evdokimov/fd3b569f9ea93ea77ee7300c34bed897 to your computer and use it in GitHub Desktop.
Signing your Git Commits using GPG on MacOS

Methods of Signing with GPG on MacOS

Last updated June 23, 2022

There are now two ways to approach this:

  1. Using gpg and generating keys
  2. Using Kryptonite by krypt.co

This Gist explains how to do this using gpg in a step-by-step fashion. Kryptonite is actually wickedly easy to use-but you will still need to follow the instructions

For using a GUI-based GIT tool such as Tower or Github Desktop, follow the steps here for signing with either GPG or Krypt.co.

There has been a number of comments on this gist regarding some issues around the pinentry-program and M1 Macs. I've finally gotten a chance to try things out on an M1 and I've updated the documentation in 2-using-gpg.md to reflect my findings.

Using GPG

Step 1: Install software

We use the Homebrew package manager for this step.

brew install gpg2 gnupg pinentry-mac       

Step 2: Create the .gnupg Directory

If this directory does not exist, create it. EDIT: June 2022 - Fixes single quotes to allow expansion of the subshell

# Make the directory
mkdir ~/.gnupg

# Tells GPG which pinentry program to use
echo "pinentry-program $(brew --prefix)/bin/pinentry-mac" > ~/.gnupg/gpg-agent.conf

Step 3: Update or Create ~/.gnupg/gpg.conf

If this file does not exist, create it.

# This tells gpg to use the gpg-agent
echo 'use-agent' > ~/.gnupg/gpg.conf

Step 4: Modify your Shell

Append the following to your ~/.bash_profile or ~/.bashrc or ~/.zshrc

...
export GPG_TTY=$(tty)

Step 5: Restart your Terminal or source your ~/.*rc file

# on the built-in bash on macos use
source ~/.bash_profile
# if using bash through homebrew over ssh use
source ~/.bashrc
# and if using zsh
source ~/.zshrc

Step 6: Update the Permissions on your ~/.gnupg Directory

You will need to modify the permissions to 700 to secure this directory.

chmod 700 ~/.gnupg

Step 7: Kill the GPG Agent

To ensure that you don't run into issues, run the below command to ensure a freshly configured gpg-agent is launched.

killall gpg-agent

Step 8: Create your GPG Key

Run the following command to generate your key, note we have to use the --expert flag so as to generate a 4096-bit key.

gpg --full-gen-key

Step 9: Answer the Questions

Once you have entered your options, pinentry will prompt you for a password for the new PGP key. There are a number of arguments on the topic of expiration dates with GPG Keys, for brevity and the sake of keeping this explanation simple we're not using Subkeys in this example and showing a non-expiring example. If you want to follow best practices, you will want to look into generating a Primary key and then Subkeys and the secure handling involved with that.

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)
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 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

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <[email protected]>"

Real name: John Smith
Email address: [email protected]
Comment:
You selected this USER-ID:
    "John Smith <[email protected]>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
You need a Passphrase to protect your secret key.

Step 10: Get your key info for Git, etc.

# List your keys
gpg -k

Step 11: Get your key id

Use the next command to generate a short form of the key fingerprint.

Copy the text after the rsa4096/ and before the date generated and use the copied id in step 13:

gpg -K --keyid-format SHORT
sec rsa4096/######## YYYY-MM-DD [SC] [expires: YYYY-MM-DD]

*You need to copy the output from your terminal similar to the example above where the ######## is following the slash. *

Step 12: Export the fingerprint

In the output from step 10, the line below the row that says 'pub' shows a fingerprint-this is what you use in the placeholder. The output from below is what you copy to Github. Documentation on how to do that is here

# The export command below gives you the key you add to GitHub
gpg --armor --export <your key id>

Step 13: Configure Git to use gpg

git config --global gpg.program $(which gpg)

Step 14: Configure Git to use your signing key

The below command needs the fingerprint from step 10 above:

git config --global user.signingkey 1111111

Step 15: Configure Git to sign all commits (Optional-you can configure this per repository too)

This tells Git to sign all commits using the key you specified in step 13.

git config --global commit.gpgsign true

Step 16: Perform a Commit

git commit -S -s -m "My Signed Commit"

Step 17: Pinentry Prompt

You will now be prompted by Pinentry for the password for your signing key. You can enter it into the Dialog box-with the option of saving the password to the macOS X Keychain.

Step 18: Submit your PGP key to Github to verify your Commits

Login into Github.com and go to your settings, SSH and GPG Keys, and add your GPG key from the page.

Step 19: Submitting Your Key to a Public Keyserver (very optional)

Before you jump on submitting your key to a service such as the MIT PGP Key Server, you should consider the following:

  • You cannot delete your key once submitted
  • Spammers have been known to harvest email addresses from these servers
  • If you're only signing your Git commits to Github this isn't necessary

Troubleshooting

Error No pinentry

This is caused by an incorrectly configured pinentry program. Review Step 2 and complete the second part again.

Error No such file or directory

This is caused by a missing configuration to specify the pinentry program. If you were following an earlier version of this gist that said you did not need to specify a pinentry program, you will need to re-do the second part of Step 2.

Other Errors

If you have any errors when generating a key regarding gpg-agent, try the following command to see what error it generates:

gpg-agent --daemon

Git Signing with a GUI Application (e.g. Git Tower or GitHub Desktop)

Manually Installed GPG

Step 1: Modify ~/.gnupg/gpg-agent.conf

use-standard-socket
# Below option is deprecated
pinentry-program $(brew --prefix)/bin/pinentry-mac
enable-ssh-support

Step 2: Modify ~/.gnupg/gpg.conf

use-agent
no-tty

Step 3: Restart GPG Agent

gpgconf --reload gpg-agent

Step 4: Copy startup-gpg-agent.sh to ~/bin/

Copy the .sh file in this gist to ~/bin/.

Step 5: Copy org.gnupg.gpg-agent.plist file to ~/Library/LaunchAgents/

Copy the the plist file in this Gist to ~/Library/LaunchAgents/.

Using Krypt.co

Step 1: Copy startup-gpg-agent.sh to ~/bin/

Copy the .sh file in this gist to ~/bin/.

Step 2: Copy org.gnupg.gpg-agent.plist file to ~/Library/LaunchAgents/

Copy the the plist file in this Gist to ~/Library/LaunchAgents/.

Automatically Sign Your Commits

To automatically sign all of your commits (which may be overkill), you can simply update your ~/.gitconfig file by running the below command:

git config --global commit.gpgsign true

Otherwise, run the below command per repository by navigating to the directory of the repo:

git config commit.gpgsign true
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!-- This needs to be placed at ~/Library/LaunchAgents/org.gnupg.gpg-agent.plist -->
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.gnupg.gpg-agent</string>
<key>ProgramArguments</key>
<array>
<!-- Be sure to set this path correctly! -->
<string>/Users/yourusername/bin/start-gpg-agent.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
# Ensure that gpg can find the agent when needed
if [ -f ~/.gnupg/.gpg-agent-info ] && [ -n "$(pgrep gpg-agent)" ]; then
source ~/.gnupg/.gpg-agent-info
export GPG_AGENT_INFO
else
eval $(gpg-agent --daemon --write-env-file ~/.gnupg/.gpg-agent-info)
fi
# This line is important for GUI tools to also find it
launchctl setenv GPG_AGENT_INFO $GPG_AGENT_INFO
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment