Blog

Using GnuPG for SSH authentication

“Using GnuPG for SSH authentication” may refer to two distinct things:

In this note, I will explain how to do both.

2015/01/10 update: This page concerns GnuPG 2.0.x (“stable”). A new note is available with updated instructions for GnuPG 2.1 (“modern”).

Using GnuPG Agent as a SSH agent

You need to make sure that gpg-agent(1) is started with support for SSH, that it is started only once, and that any program needing the agent knows how to contact it.

To do all that, I inserted the following code in my ~/.xprofile script, which is run at the beginning of any X session:

if test -f $XDG_RUNTIME_DIR/gpg-agent-info && kill -0 $(head -n 1 $XDG_RUNTIME_DIR/gpg-agent-info | cut -d: -f2) 2>/dev/null ; then
    eval $(< $XDG_RUNTIME_DIR/gpg-agent-info)
else
    eval $(gpg-agent --daemon --enable-ssh-support --write-env-file $XDG_RUNTIME_DIR/gpg-agent-info)
fi
export GPG_AGENT_INFO
export SSH_AUTH_SOCK

The script first checks for a gpg-agent-info file revealing the presence of a running gpg-agent. If that file exists and refers to an existing process, its contents are evaluated to define the GPG_AGENT_INFO and SSH_AUTH_SOCK variables; otherwise, a new instance of gpg-agent(1) is started, and the output of the starting command is also evaluated. In both cases, the newly defined variables are finally exported into the environment so that client programs will be aware of the running agent.

Of course, if you used ssh-agent(1) agent before, you must also make sure it is not started anymore, since its role will now be fullfiled by gpg-agent(1).

With the GPG agent running, you can start using it with your existing SSH keys, exactly like you would use ssh-agent. For example, to load your default ~/.ssh/id_rsa key into the agent, just run as usual:

$ ssh-add

Using an OpenPGP key as a SSH key

Create an authentication subkey

Let’s assume you already have an OpenPGP key such as the following:

$ gpg2 --expert --edit-key 996BF6BB
Secret key is available.

pub  4096R/996BF6BB  created: 2014-04-15  expires: 2017-04-14  usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/8F603B09  created: 2014-04-15  expires: 2015-04-15  usage: E
sub  2048R/B0A4401F  created: 2014-04-15  expires: 2015-04-15  usage: S
[ultimate] (1). Bob <bob@example.org>

This is a 4096-bit master key with a 2048-bit subkey for encryption and a 2048-bit subkey for signing. We will add a new 2048-bit subkey for authentication purposes:

gpg> addkey
Key is protected.

You need a passphrase to unlock the secret key for
user: "Bob <bob@example.org>"
4096-bit RSA key, ID 996BF6BB, created 2014-04-15

Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
Your selection?

Select (8) RSA (set your own capabilities):

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection?

Select successively (S), (E), and (A) to remove the signing and encryption capabilities and enable the authentication capability.

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection?

Once the only allowed action is Authenticate, select (Q) Finished to exit this sub-menu. The remaining of the procedure is like any other key generation:

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 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) 1y
Key expires at Wed 15 Apr 2015 02:02:56 PM CEST
Is this correct? (y/N) y
Really create? (y/N) y

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

pub  4096R/996BF6BB  created: 2014-04-15  expires: 2017-04-14  usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/8F603B09  created: 2014-04-15  expires: 2015-04-15  usage: E
sub  2048R/B0A4401F  created: 2014-04-15  expires: 2015-04-15  usage: S
sub  2048R/0A072B72  created: 2014-04-15  expires: 2015-04-15  usage: A
[ultimate] (1). Bob <bob@example.org>

gpg> save

Load the authentication private subkey into the agent

If you plan to use an OpenPGP smartcard, you can skip this section and read the next one. Loading the authentication subkey into the GnuPG agent is done automatically when the subkey is in such a smartcard.

Install the monkeysphere package, part of the Monkeysphere project.1 This package provides the openpgp2ssh(1) tool, which will allow to convert an OpenPGP private key block into a format suitable for use with SSH.

Export the authentication private subkey and pass it through that tool:

$ gpg2 --export-secret-subkeys \
  --export-options export-reset-subkey-passwd 0A072B72! | \
  openpgp2ssh 0A072B72 > gpg-auth-keyfile

The exclamation mark after the key ID is to convince GnuPG to export only the specified subkey—otherwise, it will export all subkeys associated with the same master key.

Restrict the permissions on the exported key file, as ssh-add(1) will refuse to load a private key contained in a world-readable file, and import the key into the agent:

$ chmod 0600 gpg-auth-keyfile
$ ssh-add gpg-auth-key-file

Verify that the key is indeed loaded:

$ ssh-add -l
2048 1c:6a:86:3e:c4:f7:7d:3d:8f:21:e0:c3:fd:89:22:99 gpg-auth-keyfile (RSA)

The gpg-auth-keyfile is no longer needed and may be deleted.2 Contrary to ssh-agent, gpg-agent will remember the loaded keys between sessions, so you will not have to load your key again, even after restarting your computer.3

Finally, extract the public key from the agent in a form suitable for inclusion into a ~/.ssh/authorized_keys file:

$ ssh-add -L
ssh-rsa AAAAB3NzaC1yc2EAA[…truncated output…]RnxTx4Mh gpg-auth-keyfile

This last step may also be done using the (unfortunately undocumented) gpgkey2ssh tool provided by GnuPG2. It takes a single parameter which is the ID of a key in your private keyring:

$ gpgkey2ssh 0A072B72
ssh-rsa AAAAB3NzaC1yc2EAA[…truncated output…]RnxTx4Mh COMMENT

Authenticate with an OpenPGP smartcard

If you have an OpenPGP smartcard, you can transfer your private authentication subkey to it.

$ gpg2 --edit-key 996BF6BB
Secret key is available.

pub  4096R/996BF6BB  created: 2014-04-15  expires: 2017-04-14  usage: SC
                     trust: ultimate      validity: ultimate
sub  2048R/8F603B09  created: 2014-04-15  expires: 2015-04-15  usage: E
sub  2048R/B0A4401F  created: 2014-04-15  expires: 2015-04-15  usage: S
sub  2048R/0A072B72  created: 2014-04-15  expires: 2015-04-15  usage: A
[ultimate] (1). Bob <bob@example.org>

Switch to the list of private (sub)keys:

gpg> toggle

sec  4096R/996BF6BB  created: 2014-04-15  expires: 2017-04-14
ssb  2048R/8F603B09  created: 2014-04-15  expires: never
ssb  2048R/B0A4401F  created: 2014-04-15  expires: never
ssb  2048R/0A072B72  created: 2014-04-15  expires: never
(1)  Bob <bob@example.org>

Select the authentication subkey…

gpg> key 3

sec  4096R/996BF6BB  created: 2014-04-15  expires: 2017-04-14
ssb  2048R/8F603B09  created: 2014-04-15  expires: never
ssb  2048R/B0A4401F  created: 2014-04-15  expires: never
ssb* 2048R/0A072B72  created: 2014-04-15  expires: never
(1)  Bob <bob@example.org>

and move it to the smartcard:

gpg> keytocard
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]

Please select where to store the key:
  (1) Signature key
  (3) Authentication key
Your selection? 3

You need a passphrase to unlock the secret key for
user: "Bob <bob@example.org>"
2048-bit RSA key, ID 8F603B09, created 2014-04-15

Unlock your secret key and enter your smartcard Admin PIN when requested.

sec  4096R/996BF6BB  created: 2014-04-15  expires: 2017-04-14
ssb  2048R/8F603B09  created: 2014-04-15  expires: never
ssb  2048R/B0A4401F  created: 2014-04-15  expires: never
ssb* 2048R/0A072B72  created: 2014-04-15  expires: never
                              card no: 0001 00000229
(1)  Bob <bob@example.org>

gpg> save

That’s it. Now the authentication subkey will automatically be loaded into the GnuPG Agent the first time the card is read (you can verify that by running ssh-add -l), and when attempting to connect to a SSH server, you will be prompted for your card PIN.

  1. You may need to also install the Crypt::OpenSSL::RSA and Crypt::OpenSSL::Bignum Perl modules, if they are not automatically pulled down by your package manager.
  2. Actually, that file must be deleted, as it contains your private authentication subkey in an unprotected form!
  3. The loaded keys are stored in ~/.gnupg/private-keys-v1.d.