This article is also posted on my blog.
The problem
I’m using Git for Windows, and have configured it to sign every single commit and tag using GPG (GnuPG), which uses Pinentry, a program that allows for secure entry of PINs or passphrases.
Every time when I commit my code, a Pinentry window will always popup and ask me for the passphrase to unlock my key. However, the window never gains focus, and I have to move my mouse and click on the input box to start typing, which is quite annoying to me.
A bug report is found on GnuPG’s Phabricator, but seems there’s still no solution or workaround.
Trying to use the “loopback” (command-line) mode of Pinentry
After digging deep into GnuPG’s documentations, I found out that there’s a “loopback” mode of Pinentry, which will redirect the passphrase queries back to the caller (GnuPG), so that the user will be prompted to input the passphrases directly in the command line.
Loopback mode is disabled by default. To enable it, edit the config of GPG agent (~/.gnupg/gpg-agent.conf
) and add the following line.
allow-loopback-pinentry
Tell GPG to reload the config with gpg-connect-agent reloadagent /bye
. After that, you will be able to add--pinentry-mode loopback
to gpg
commands.
Looks great. But…
How to use loopback mode when signing commits with Git?
First I thought there might be some Git config that can be used to add extra arguments to GPG. There’s a gpg.program
config for setting a custom program instead of gpg
used by Git. I tried to set it to gpg --pinentry-mode loopback
, but it won’t work, throwing me errors like “cannot spawn gpg — pinentry-mode loopback: No such file or directory”.
Then I wrote a Bash script to serve as a “proxy” for GPG. The script writes simple, just adding the loopback arg and letting all the other args pass through.
#!/usr/bin/bash
gpg --pinentry-mode loopback $@
Save it somewhere (~/.gpg-pinentry-loopback
for me), grant the execution permission to it with chmod +x ~/.gpg-pinentry-loopback
, and set the full, Windows-style path as the gpg.program
config for Git:
> git config --global gpg.program "C:/Users/beta/.gpg-pinentry-loopback"
Now let’s see if it works.
Wrapping things up
1. Add allow-loopback-pinentry
to ~/.gnupg/gpg-agent.conf
.
2. Reload GPG config.
gpg-connect-agent reloadagent /bye
3. Create a Bash script at ~/.gpg-pinentry-loopback
(or any path you like).
#!/usr/bin/bash
gpg --pinentry-mode loopback $@
4. Grant execution permission to the script. Replace the path with your own.
chmod +x ~/.gpg-pinentry-loopback
5. Set the script as the GPG program for Git. Replace the path with your own (full Windows-style path required).
git config --global gpg.program "C:/Users/beta/.gpg-pinentry-loopback"