Using OpenPGP keys with ssh

ssh, the command line client for Secure Shell, uses public key cryptography as one method of authentication. For that, a ssh key pair has to be generated and the public part of that pair has to be configured at the server side as an authorized key. ssh can also use OpenPGP keys for that purpose. A bird's eye view of the configuration looks like:

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

On the client side:

  • ssh uses an ssh-agent to manage its secrets, here an ssh-agent has to be used which can facilitate OpenPGP keys.
  • There can be more than one active ssh-agent, ssh can be told to use a specific agent by setting the SSH_AUTH_SOCK environment variable accordingly.
  • 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. Using chameleon for that purpose allows to integrate the keystore of sq.

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 subkey to use.

There is no necessity for a key to have an authenticating subkey, OpenPGP functionality doesn't use these type of keys. If your key misses such a subkey, you can add one by:

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

With $FINGERPRINT being 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 to a unix socket for commands related to its ssh-agent functionality. The socket can be found by:

$ gpgconf -L

The output has to 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 SSH_AUTH_SOCK has to have in order to use gpg-agent as ssh-agent.

Register the key

gpg-agent has to know which keys to use when authenticating a 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 the chameleon - this example assumes that you 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 - see flag [A] and the matching fingerprint. Add the keygrip FE39C76C0F879E840C1EE9E8E655694C73516437 to ~/.gnupg/sshcontrol to use it.

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

$ 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 provided by g10code doesn't know about the keystore from Sequoia PGP, that binary has to be exchanged for chameleon. 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 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. You can check by

$ 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 if ssh sees 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 still uses g10codes 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).