Using OpenPGP keys with ssh

ssh, the command line client for Secure Shell, uses public key cryptography as one method of authentication. For that, an ssh key pair has to be generated and the public part of that pair has to be installed on the server as an authorized key. ssh can also use OpenPGP keys for this purpose.

On the server side, nothing changes; the public part of the key has to be configured as an authorized key.

On the client side:

  • ssh uses an ssh-agent to manage its secrets.
  • There can be more than one active ssh-agent. ssh can be told to use a specific agent through the SSH_AUTH_SOCK environment variable.
  • An agent which can use OpenPGP keys is gpg-agent. gpg-agent has to be configured to:
    • start the ssh-agent subsystem
    • use a specific set of OpenPGP keys in the ssh-agent context
  • gpg-agent needs a gnupg-compatible backend to access the actual keys. chameleon provides this integration for sq's keystore.

Preparation

Prepare the key

To be used for ssh authentication, a key must have a subkey marked for authentication. Use sq inspect to identify the subkey:

$ sq inspect --cert 8140AA1A97177805FD263466718AC099BAFDC830
OpenPGP Certificate.

      Fingerprint: 8140AA1A97177805FD263466718AC099BAFDC830
  Public-key algo: EdDSA
  Public-key size: 256 bits
    Creation time: 2025-03-19 13:29:31 UTC
  Expiration time: 2028-03-19 06:55:52 UTC (creation time + 2years 11months 30days 9h 16m 45s)
        Key flags: certification

           Subkey: EA495531497101582BFCB0F947E2D1A287003BD9
  Public-key algo: EdDSA
  Public-key size: 256 bits
    Creation time: 2025-03-19 13:29:31 UTC
  Expiration time: 2028-03-19 06:55:52 UTC (creation time + 2years 11months 30days 9h 16m 45s)
        Key flags: signing

           Subkey: A07809FFE01CD7091B9CB6D40B3E57A87C4AFF46
  Public-key algo: EdDSA
  Public-key size: 256 bits
    Creation time: 2025-03-19 13:29:31 UTC
  Expiration time: 2028-03-19 06:55:52 UTC (creation time + 2years 11months 30days 9h 16m 45s)
        Key flags: authentication

           Subkey: DEE6688DA0336013F5E96062812861395DA7428C
  Public-key algo: ECDH
  Public-key size: 256 bits
    Creation time: 2025-03-19 13:29:31 UTC
  Expiration time: 2028-03-19 06:55:52 UTC (creation time + 2years 11months 30days 9h 16m 45s)
        Key flags: transport encryption, data-at-rest encryption

           UserID: <alice@example.com>
   Certifications: 2, use --certifications to list

In this example the second subkey listed (Subkey: A07809FFE01CD7091B9CB6D40B3E57A87C4AFF46) has a key flag authentication – that is the right subkey to use.

There is no need for a key to have an authenticating subkey as OpenPGP doesn't use these types of keys, but if your key is missing such a subkey, you can add one with:

$ sq key subkey add --cert $FINGERPRINT --can-authenticate

where $FINGERPRINT is the fingerprint of the primary key you want to add the subkey to. Likewise, you can revoke old authenticating subkeys. You can even have several such subkeys in your key - see chapter Adding subkeys.

Prepare the gpg-agent

Ensure that gpg-agent activates its ssh-agent subsystem. The configuration gpg-agent.conf can be found in ~/.gnupg and has to contain the line:

enable-ssh-support

When started with this option, gpg-agent listens on a UNIX socket for commands related to its ssh-agent functionality. The socket can be found using:

$ gpgconf -L

The output should contain the line:

agent-ssh-socket:/home/user/.gnupg/S.gpg-agent.ssh

(your output might look a little different – look for agent-ssh-socket).

From the example above, /home/user/.gnupg/S.gpg-agent.ssh is the important bit: this is the value that SSH_AUTH_SOCK needs to have in order to use gpg-agent as an ssh-agent.

Register the key

gpg-agent needs to know which keys to use when authenticating an ssh session. For that purpose, add the keygrip of that key to ~/.gnupg/sshcontrol on a line of its own.

To get the keygrip, use chameleon – this example assumes that you have installed the chameleon as gpg-sq.

$ gpg-sq -k --with-keygrip --with-subkey-fingerprints 8140AA1A97177805FD263466718AC099BAFDC830
pub   ed25519 2025-03-19 [C] [expires: 2028-03-19]
      8140AA1A97177805FD263466718AC099BAFDC830
      Keygrip = C7EC53D3635F42437C3C73DE6B34F4577F3FF346
uid           [  full  ] <alice@example.com>
sub   ed25519 2025-03-19 [S] [expires: 2028-03-19]
      EA495531497101582BFCB0F947E2D1A287003BD9
      Keygrip = 55044CA1ED6232F784CFFF13589801F7027B8175
sub   ed25519 2025-03-19 [A] [expires: 2028-03-19]
      A07809FFE01CD7091B9CB6D40B3E57A87C4AFF46
      Keygrip = FE39C76C0F879E840C1EE9E8E655694C73516437
sub   cv25519 2025-03-19 [E] [expires: 2028-03-19]
      DEE6688DA0336013F5E96062812861395DA7428C
      Keygrip = 27244A6CC8B77ABE34BD54557241744157DC30C9

Again, the second subkey listed is the authentication key, identified by the [A] flag and the matching fingerprint. Add the keygrip FE39C76C0F879E840C1EE9E8E655694C73516437 to ~/.gnupg/sshcontrol to use it.

Alternatively you can add keys to the ssh-agent subsystem with:

$ gpg-connect-agent 'keyattr FE39C76C0F879E840C1EE9E8E655694C73516437 Use-for-ssh: true' /bye

and remove them with:

$ gpg-connect-agent 'keyattr FE39C76C0F879E840C1EE9E8E655694C73516437 Use-for-ssh: false' /bye

Use gpg-sq

The gpg-agent uses a binary named gpg to access stored keys. As the gpg implementation provided by g10code doesn't know about Sequoia PGP's keystore, that binary has to be exchanged for chameleon, which does. There are many ways to achieve this. One way is to create a symlink and add the directory where the symlink was created in as the first component in your PATH environment variable:

$ mkdir $HOME/bin
$ ln -s /usr/bin/gpg-sq $HOME/bin/gpg

Adjust the paths accordingly.

Modify $PATH (for instance in your .bashrc):

export PATH="$HOME/bin:$PATH"

In a new shell (or after source .bashrc), which gpg should point to $HOME/bin/gpg; check it with:

$ gpg --version
gpg (GnuPG-compatible Sequoia Chameleon) 2.2.40
Sequoia gpg Chameleon 0.13.1
sequoia-openpgp 2.0.0
Copyright (C) 2024 Sequoia PGP
...

Usage

First check whether ssh can see the OpenPGP key:

$ SSH_AUTH_SOCK=/home/user/.gnupg/S.gpg-agent.ssh ssh-add -L
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBvWSboin0MoLpdliKGTDEMx9zMplSbjfacaWxDeqqKq (none)

This gives you the public part of the ssh key pair, which has to be deposited as authenticated key in ~/.ssh/authenticated_keys on the remote machine.

If you do not see your key in this list, chances are that gpg-agent is still using g10code's gpg.

Use

$ SSH_AUTH_SOCK=/home/user/.gnupg/S.gpg-agent.ssh ssh user@remote-host.tld

to connect to the remote server with your OpenPGP key. For convenience, you can set and export SSH_AUTH_SOCK in your .bashrc (or equivalent) so that you don't need to include it on the command line every time.