Skip to content

An OpenPGP authentication subkey for SSH

These steps use gpg and ssh, which exist today. Making the subkey with ritueel, so that an offline master key binds a subkey born on the card, is planned (issue #18). Try everything on a test key first. To learn how the master key and the subkeys fit, read the OpenPGP custody chain.

An OpenPGP key can log you in to SSH servers. The key is a subkey with the authentication flag (A), kept on a card, so it cannot be copied off your computer and each use needs the PIN and, if the card asks, a touch. The master key stays offline.

  • A card with an authentication subkey born on it, bound by your master key. A signing and an encryption subkey may be on the same card.
  • gpg 2.2 or later with scdaemon, and an ssh client.
  • The card’s PIN in your password manager.

This is the usual way. gpg-agent speaks the SSH agent protocol and uses the card through scdaemon.

  1. Turn on SSH support. Add enable-ssh-support to ~/.gnupg/gpg-agent.conf, then restart the agent: gpgconf --kill gpg-agent.
  2. Point SSH at the agent. In your shell profile, set SSH_AUTH_SOCK to the output of gpgconf --list-dirs agent-ssh-socket.
  3. List the key SSH sees. ssh-add -L should print one line for the authentication subkey. If it does not, plug the card in and run gpg --card-status once so gpg learns the key.
  4. Export the public half. gpg --export-ssh-key <fingerprint>! prints the line to put in authorized_keys, or to hand to the CA that signs SSH certificates (SSH). The ! selects that exact subkey.
  5. Log in. ssh host. The agent asks for the PIN, and touches follow your card’s policy.

What gpg-agent offers over SSH (read from the GnuPG manual and source, 2026-11-01): it uses the authentication (A) key; it speaks Ed25519, RSA, ECDSA (P-256, P-384, P-521) and certificates of those types; it does not speak the FIDO sk-* key types. Keys on an active OpenPGP card are used without listing them; the sshcontrol file that lists keygrips by hand is deprecated in favour of a Use-for-ssh attribute on the key.

An SSH client can also load a PKCS#11 module that talks to the card directly, with PKCS11Provider in ~/.ssh/config or ssh -I. Which module you need depends on the card: OpenSC’s module reads OpenPGP cards, and a YubiKey’s PIV applet has its own module. The key then comes from whatever the module exposes, not from gpg.

For a YubiKey’s PIV applet, the usual module is libykcs11: ssh -I libykcs11.so user@host for one login, ssh-add -s libykcs11.so to put the card’s keys in the agent, and ssh-keygen -D libykcs11.so -e to export the public keys. PIV slot 9a asks for the PIN once for a session and slot 9c for every signature.

Choose this when you do not want gpg-agent in the loop. Know the cost: a module and scdaemon both want the card, and a program that holds it keeps the others out. Do not run both at once, and keep the root cards, which are used only at ceremonies, away from your daily card.

OpenSSH can also make a key on a FIDO2 token (ssh-keygen -t ed25519-sk), with options for a resident key and for requiring the PIN. It does not use your OpenPGP key at all, and gpg-agent cannot serve it. Choose it when you want an SSH key that is separate from your OpenPGP identity.

  • ssh -v host says it offers the key by its fingerprint and the server accepts it.
  • Unplug the card and the login fails. That is the point.
  • The agent offers no key. Another program holds the card. Close it, or restart scdaemon.
  • A wrong PIN three times blocks the PIN. Do not retry guesses. The admin PIN unblocks it; see recover if the card is lost instead.
  • The card is lost. The key on it cannot be used by anyone without the PIN. Remove its public key from every authorized_keys and CA list, then revoke the subkey (Revoke a key).

Do all of this on a test key and a throw-away server first. End the drill by removing the key from the server and checking that the login now fails.