Julian Simioni

Troubleshooting GPG git commit signing

As part of setting up a new laptop recently, I was setting up git commit signing.

Despite having most of my configs in a git repository or otherwise tracked, I ran into a problem with setting this up.

Here’s the error:

1
2
3
$ git commit
error: gpg failed to sign the data
fatal: failed to write commit object

And the answer (for me):

Make sure the user.signingkey option in your .gitconfig is in the correct format! This is very silly, but there are some easy ways to get it wrong. First, some correct examples.

Standard long key format (recommended)

1
2
3
4
[user]
    name = Julian Simioni
    email = julian@simioni.org
    signingkey = 0x4CEEB1E5A7FD15E1

That’s 0x followed by the last 16 characters of your key id. At least as of gnupg 2.2.0 its the standard output of a command like gpg --list-secret-keys

Long key format without the hexidecimal prefix

1
2
3
[user]
    # ...
    signingkey = 4CEEB1E5A7FD15E1

Still 16 characters, but without the prefix clarifying that the value is written in hexidecimal.

Short key format (works, but not recommended)

1
2
3
[user]
    # ...
    signingkey = A7FD15E1

This is the “short” format, consisting of only 8 characters. It works too, and was much more standard in the past. However its not recommended as its now far too easy to generate keys that have the same final 8 characters.

DOESN’T WORK: other key lengths

1
2
3
[user]
    # ...
    signingkey = CEEB1E5A7FD15E1

You might be assuming, like I did, that GPG and git would be smart enough to allow you to use any suffix of your key, much like git allows you to use any unique prefix of a git commit hash. That is not the case. I suppose it makes sense. Oh well.

Other useful debugging steps

While in my case the issue with my commit signing was simple user error, I did go through quite a few other debugging steps, and they were helpful in figuring out where the error was not! Here they are in case they are useful to me again later (quite likely), or others.

Many of these came from this helpful Stackoverflow thread.

Ensure basic encryption works

A simple way to test gpg and your secret key itself is to issue a command like the following:

1
echo "test" | gpg ---clearsign

This will send a small bit of text (“test”) to gpg, and have it print out the same text, but with a plaintext signature attached. If it works, then you know quite a few things are working: gpg itself, your secret key, whatever method you are using to enter the passkey to your key (if you have one, which you should!), etc.

GPG Agent settings, or lack thereof

GPG internally uses an “agent” program. Basically, whenever you run gpg, it launches a process in the background that will stick around. That process is used to remember your passphrase temporarily, for convenience, and probably other things.

In the past, ensuring the gpg command you run on the command line can communicate with this agent has been challenging. If you’ve ever seen instructions regarding adding various GPG_AGENT_INFO environment variables, its an attempt to properly set up this communication channel.

The good news is that as of GnuPG version 2.1.0, none of this is needed. There is now a “standard” method of connecting to the agent and everything is supposed to just work. Compare the instructions in the documentation for version 2.0 and the latest version to see what I mean.

GPG_TTY environment variable

This variable is important to set up. It will help GPG know which terminal it is running on, so that the prompt to enter your key passphrase is shown in the correct place. Again, from the GnuPG documentation, this will do the trick:

1
2
GPG_TTY=$(tty)
export GPG_TTY

Handling local terminal and SSH connections gracefully

There’s nothing worse than not being able to use gpg because you SSHed into your computer, and when you ran gpg, it popped up a dialog box to enter your passphrase on the computer display itself, rather than in your SSH session.

Fortunately, its easy to tell pinentry, the underlying program responsible for managing passphrase entry, to do the right thing during SSH sessions.

1
2
3
if [[ -n "$SSH_CONNECTION" ]]; then
    export PINENTRY_USER_DATA="USE_CURSES=1"
fi

This comes from the GnuPG Gentoo Wiki article.

Pretty fancy.