Private Keys

I regularly use gpg and ssh keys during the day, from signing git commits, encrypting sensitive messages to email and authenticating with servers. I regularly use gpg via Enignmail and the Thunderbird GUI and from the command line in zsh. I was having a couple of issues with the default ubuntu 16.04 setup for managing keys.

  1. gnome-keyring-agent was not always handling encrypted private keys correctly resulting in some scripts failing to authenticate with an error message too many authentication attempts even though ssh-add -l showed the correct keys where available.
  2. using ssh, ssh-add from gpg2 from the command line when a password was requested to decrypt a key a GUI dialog would pop up disrupting my focus.

Ideally a terminal based password entry would present itself when working in a terminal and a GUI dialog present itself when working with a desktop app. A solution would also have to fix the too many authentication attempts error.


The gpg-agent is a good candidate, it is a daemon to manage secret keys similar the ssh-agent or the gnome-keyring-daemon. It’s used by gpg2 and can be used as an agent for ssh. gpg-agent has numerous pinentry programs that allow gpg to read passphrases in a secure manner, there are GUI and terminal based programs and the gpg-agent can be configured to use different pinentry programs.

I updated GnuPG to the latest version using

My .zshrc contains the following:

# gpg
export GPG_TTY
# gpg as ssh-agent
if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
  export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"

# let pinentry know which console to display in for ssh-agent
  echo UPDATESTARTUPTTY | gpg-connect-agent > /dev/null

# let gpg-agent know which pinentry program to use
  echo "term" > /tmp/pinentry-app

# add zsh hook to exec above function before every command execution
autoload -U add-zsh-hook
add-zsh-hook preexec zsh-preexec

# similar to `ssh-add -D` but works with gpg-agent
  grep -o ^[A-Z0-9]* ~/.gnupg/sshcontrol | xargs -I% rm ~/.gnupg/private-keys-v1.d/%.key
  echo '' > ~/.gnupg/sshcontrol

Next a simple shim for pinentry is needed, when invoked it selects either a terminal pinentry program or a GUI pinentry program based on contents of /tmp/pinentry-app

# Configuration -- adjust these to your liking

# if /tmp/pinentry-app is "x11" then use gui
if [ $(cat /tmp/pinentry-app) = "x11" ]; then
  exec "$PINENTRY_X11" "$@"
  exec "$PINENTRY_TERMINAL" "$@"

This needs to be linked to pinentry so it is used as our default pinentry program.

ln -s ~/bin/pinentry /usr/local/bin/pinentry -f

The gpg-agent needs ssh enabled and then stopped, it’ll automatically restart when needed.

echo 'enable-ssh-support' >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent

Next the GUI applications using GnuPG need configured, in my case Thunderbird Enigmail. First another simple shim is created ~/bin/gpg2-thunderbird, this time for gpg2, which tells gpg-agent to use the GUI pinentry.


echo "x11" > /tmp/pinentry-app
exec "/usr/local/bin/gpg2" "$@"

Then Thunderbird > Enigmail > Preferences > Basic > Files and Directories > Override with is updated with the location of the gpg2 shim.


I now have my desired private key management setup how I want it. A terminal based pinentry when working in the terminal and a GUI based pinentry when working from a GUI.

GUI pin entry

Terminal pin entry