Use GPG for SSH keys

  • gpg, ssh
  • 3
  • 1
  • finished
Table of Contents

GPG and gpg-agent are capable to serve GPG authentication subkeys as SSH keys. GPG has infamously unfriendly user interface so here’s hopefully an easy to follow tutorial which explains how to:

  • add new SSH key managed by GPG;
  • change ssh-agent to gpg-agent and add new SSH keys to it;
  • use it in ~/.ssh/config
  • copy it to the server’s authorized_keys with ssh-copy-id

New GPG Key

For tutorial completeness sake, let’s create a new GPG key. You can of course skip this step if you already have a key which you’d like to use.

NOTE: In this article I’m using gpg2 command, but you can change it to ordinary gpg.

To generate a new GPG key, you should run:

$ gpg2 --full-generate-key

and answer some questions which GPG asks you (like your name or e-mail address which should be associated with the new key). You can safely use default answers for most of them. Consider using longer key length than the suggested default though (for example 4096 bytes).

New Subkey

First we need to add a new authentication subkey to the existing GPG key. We do it by entering an interactive GPG command line:

$ gpg --expert --edit-key <key id>

(... output ...)

gpg> addkey
Please select what kind of key you want:
   (...)
   (8) RSA (set your own capabilities)
   (...)

From here we choose a subkey type to which we can set our own capabilities. In this case I chose RSA (set your own capabilities). We should change it to leave only Authenticate capability. New subkeys have Sign and Encrypt capabilities set by default so we have to choose s and e (to disable them) and then a (to enable Authenticate).

We confirm selctions, then choose the subkey length (which is by default the same as length of our primary key), key expiry date (0 by default, meaning no expiry date) and confirm everything with save command.

Let’s confirm that new subkey was added:

$ gpg2 --list-keys --with-keygrip --with-subkey-fingerprint

and note subkey’s keygrip and fingerprint, we’ll need it later.

This way we can add as many authentication keys as we want. Repeat this step if you’d like to use different SSH keys for different services.

Enabling GPG Agent

We can’t use ordinary ssh-agent with keys managed by GPG. We must use gpg-agent with SSH support enabled. To enable it, we add enable-ssh-support line to ~/.gnupg/gpg-agent.conf. While we’re here, it’s also worthy to change the default times for keys caching (i.e. time in which gpg-agent won’t ask as for the password). After all our changes it should look more-less like this:

enable-ssh-support

default-cache-ttl 180800
max-cache-ttl 180800
default-cache-ttl-ssh 180800
max-cache-ttl-ssh 180800

Next, we must actually run gpg-agent:

$ unset SSH_AGENT_PID
$ agent_sock=$(gpgconf --list-dirs agent-socket)
$ export GPG_AGENT_INFO=${agent_sock}:0:1
$ export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
$ gpg-connect-agent updatestartuptty /bye

It’s best to add these lines to your session startup scripts (like ~/.xsessionrc). If you’re using Debian, there’s a good chance that your OS already does this part somewhere in /etc/X11/Xsession.d. If you’re using Wayland, run them before your compositor starts.

Adding Key to GPG Agent

GPG Agent automatically manages keys listed in ~/.gnupg/sshcontrol, whithout need to add them with ssh-add. You need to add the keygrip of your key here (see gpg2 --list-keys from New Subkey step).

If you still want to use ordinary SSH keys along the GPG ones, you should add them once with ssh-add, which should automatically update sshcontrol file with their keygrips.

Next, we should restart gpg-agent to apply the changes, for example by running gpg-connect-agent /bye, or systemctl restart gpg-agent.service gpg-agent.socket, if you happen to manage gpg-agent with systemd.

Exporting Public Keys

Some tools and configuration files, like ~/.ssh/config, require ordinary public keys. It’s possible to get them with gpg2 --export-ssh-key <key id> > ~/.ssh/gpg.pub.

But here’s a catch: --export-ssh-key exports only the last SSH key. To export a specific key, we must pass its fingerprint, followed by an exclamation mark (see gpg2 --list-keys from New Subkey step on how to get it):

$ gpg2 --export-ssh-key <fingerprint1>! > ~/.ssh/key1.pub
$ gpg2 --export-ssh-key <fingerprint2>! > ~/.ssh/key2.pub

You can confirm that correct key was exported by checking its name (at the end): openpgp:0x<fingerprint-end>, where fingerprint-end arejthe last 8 hexadecimal numbers of full subkey’s fingerprint.

Now we can use it in ~/.ssh/config:

Host localhost
    Hostname 127.0.0.1
    IdentityFile ~/.ssh/gpg.pub
    IdentitiesOnly yes

or we can send it to the remote server with ssh-copy-id:

$ ssh-copy-id -f -i ~/.ssh/gpg.pub ssh.example.com

Notice that we must use -f flag, because we don’t have a private key exported. This can result in more than one copy of the key being installed on the remote system (if you run ssh-copy-id more than once), but it shouldn’t be a problem and you can always edit authorized_keys manually.

Good job, we have configured GPG to handle our SSH needs! Now it’s time for the hardest part: replacing all SSH keys for all online services.