Tutorials

OpenPGP Keys on a YubiKey

Don’t let your PGP or SSH keys be copied from your computer. Protect your keys in an OpenPGP Card.

You may want to review the typographical conventions used on this site.

Threat Model

PGP or SSH keys stored in files on your computer are susceptible to theft. Maybe a browser exploit allows an attacker to upload a copy of your private keys to their web server. Maybe you left your terminal unlocked and unattended for a few minutes, and someone copied your private keys to a USB drive or across the network.

Storing your private keys in an OpenPGP Card, instead of in files on your computer, prevents the theft of your private keys without physically stealing your OpenPGP Card.

Buy a YubiKey

Yubico’s YubiKey is one of the best OpenPGP Cards on the market. The YubiKey also provides Smart Card (PIV), Universal 2nd Factor (U2F), FIDO2, and One-Time-Password (OTP) features in the same device. However, this tutorial focuses on the OpenPGP features of the YubiKey.

You can buy a YubiKey 5 for about $45 from the Yubico store or from Amazon.

Install the Required Linux Software

Install the required GnuPG, Smart Card, and YubiKey packages.

apt-get install pcscd opensc
apt-get install pinentry-curses pinentry-gtk2 pinentry-qt
apt-get install gnupg gpg gpg-agent dirmngr scdaemon
apt-get install yubikey-manager

Configure scdaemon

~/.gnupg/scdaemon.conf

# Power down smartcards after the specified number of seconds of inactivity.
# The user will need to enter the PIN again after the next power up.
card-timeout 900  # 15 minutes.

# Disable the integrated support for CCID compliant readers.
# This allows using pcsc (instead of conflicting with it).
disable-ccid

Generate an Offline Key Pair

Before generating a new OpenPGP key pair, you should harden your GnuPG configuration so that the hardened settings are used when generating new keys and subkeys.

Your offline key pair must be on a normally-offline, and preferably encrypted, storage device in order to guard against theft while still allowing you to recover from a lost or stolen YubiKey. A bootable encrypted Linux on USB is a great place to generate and store your offline key pair. If your offline key storage is bootable, install and configure the same software as above.

In the following example session, some of the lengthy or repetitive output has been trimmed in the interest of length and clarity.

Generate a new master key that is only capable of certifying other keys.

gpg --expert --full-gen-key
Please select what kind of key you want:
   (8) RSA (set your own capabilities)
Your selection? 8

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

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

Your selection? S
Your selection? E

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

Your selection? Q

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096

Please specify how long the key should be valid.
Key is valid for? (0) 2y
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: John Doe
Email address: john.doe@example.com
Comment:
You selected this USER-ID:
    "John Doe <john.doe@example.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

Edit your new key and add several single-purpose subkeys.

gpg --expert --edit-key XXXXXXXXXXXXXXXX

Add a signature-only RSA subkey.

gpg> addkey
Please select what kind of key you want:
   (4) RSA (sign only)
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096

Please specify how long the key should be valid.
Key is valid for? (0) 2y
Is this correct? (y/N) y
Really create? (y/N) y

Add an encryption-only RSA subkey.

gpg> addkey
Please select what kind of key you want:
   (6) RSA (encrypt only)
Your selection? 6
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096

Please specify how long the key should be valid.
Key is valid for? (0) 2y
Is this correct? (y/N) y
Really create? (y/N) y

Add an authentication-only RSA subkey.

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

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? S
Your selection? E
Your selection? A

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

Your selection? Q

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096

Please specify how long the key should be valid.
Key is valid for? (0) 2y
Is this correct? (y/N) y
Really create? (y/N) y

Save your changes and exit the key editing session.

gpg> save

Copy Subkeys into the YubiKey

Backup your keys and subkeys before loading the subkeys into the YubiKey. This is required because loading the subkeys into the YubiKey actually removes the subkeys from the keyring files and moves them to the YubiKey. These backup files preserve an offline copy of keys and subkeys.

gpg --armor --output XXXXXXXXXXXXXXXX_public.asc --export XXXXXXXXXXXXXXXX
gpg --armor --output XXXXXXXXXXXXXXXX_secret.asc --export-secret-keys XXXXXXXXXXXXXXXX

Copy the public-key-only XXXXXXXXXXXXXXXX_public.asc to your regular online computer.

Edit your key to move subkeys from your keyring to the YubiKey.

gpg --edit-key XXXXXXXXXXXXXXXX

Transfer your signature-only subkey to the YubiKey’s signature key slot.

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

Transfer your encryption-only subkey to the YubiKey’s encryption key slot.

gpg> key 2
gpg> keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2
gpg> key 2

Transfer your authentication-only subkey to the YubiKey’s authentication key slot.

gpg> key 3
gpg> keytocard
Please select where to store the key:
   (3) Authentication key
Your selection? 3
gpg> key 3

Save your changes and exit the key editing session.

gpg> save

Unplug the YubiKey, delete the card-migrated keys, and re-import your backed up private keys.

gpg --delete-secret-and-public-key XXXXXXXXXXXXXXXX
gpg --import XXXXXXXXXXXXXXXX_secret.asc

At this point you should shutdown, offline, and safely store your offline keys.

Go to your regular online computer and import the public key and subkeys. Then plug in the YubiKey and check the YubiKey’s card status to connect that YubiKey to the matching public key and subkeys.

gpg --import XXXXXXXXXXXXXXXX_public.asc
gpg --card-status

Harden YubiKey OpenPGP Settings

The three-letter parameters are short for (sig)nature, (dec)ryption, and (aut)hentication.

ykman openpgp set-touch sig On
ykman openpgp set-touch enc On
ykman openpgp set-touch aut On

Check the current settings with the following command.

ykman openpgp info

Change the YubiKey PINs

Connect your YubiKey to your computer and edit the OpenPGP card settings.

gpg --card-edit

Whenever you have a gpg/card> prompt, help shows the currently available commands and list shows the current OpenPGP card settings.

Several of the commands used in this session are not available until after the admin command is run. You can run help before and after running the admin command to see the general differences in the available commands.

gpg/card> admin
Admin commands are allowed

The YubiKey’s default user PIN is 123456, the default admin PIN is 12345678, and the default reset PIN is unset. You need to change these PINs.

gpg/card> passwd

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 1

Your selection? 3

Your selection? 4

Your selection? Q

I want every signature generation to require entering my user PIN. The forcesig command toggles the current setting, so verify the setting is correct with list after toggling.

gpg/card> forcesig

gpg/card> list
Signature PIN ....: forced

While you are editing the OpenPGP card, you can also set your name, sex, preferred language, and other settings.

gpg/card> name
Cardholder's surname: Doe
Cardholder's given name: John

gpg/card> sex
Sex ((M)ale, (F)emale or space): M

gpg/card> lang
Language preferences: en

Quit the session once you are done editing the OpenPGP card settings.

gpg/card> quit

Upload Your Public Key to the Keyservers

At this point, you should have an encrypted offline backup of your keys and subkeys, a copy of your private subkeys loaded into your YubiKey, your public key and subkeys on your computer and linked to your YubiKey, and your YubiKey settings personalized and hardened.

You are now ready to upload your public key and subkeys to the keyservers.

gpg --send-keys XXXXXXXXXXXXXXXX

If you are using the keys.openpgp.org keyserver, you need to verify each email address by which your public key should be searchable. Execute the following command, copy the returned URL into your web browser, and follow the instructions at that URL.

gpg --export XXXXXXXXXXXXXXXX | curl -T - https://keys.openpgp.org/

Information about the design and privacy of keys.openpgp.org is available at the following links.

OpenSSH Support

Ensure that your ~/.gnupg/gpg-agent.conf enables ssh-agent support.

# Enable OpenSSH Agent (ssh-agent) protocol support.
enable-ssh-support

Ensure that your ~/.profile tells ssh to use gpg-agent instead of ssh-agent.

if [ -z "$SSH_AUTH_SOCK" ]; then
    export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
fi

Kill any running ssh-agent and gpg-agent processes for your user so that the new gpg-agent settings can take effect.

killall -u ${USER} ssh-agent gpg-agent

Log out and log in to set the session environment variables required for ssh to use gpg-agent instead of ssh-agent. Then check that gpg can still see your YubiKey, which has the side-effect of ensuring that gpg-agent is running.

gpg --card-status

At this point, ssh should be able to use the authentication key slot on the YubiKey as an SSH authentication key.

The following command retrieves the SSH-formatted public key from the YubiKey. Place the retrieved public key into ~/.ssh/authorized_keys on the appropriate systems and register the public key with SSH-based services like GitHub or GitLab.

ssh-add -L

If multiple public keys are listed, the authentication Key on the YubiKey is the one that ends with a card number comment that contains the serial number of that YubiKey.

ssh-rsa ... cardno:XXXXXXXXXXXX

Now that you have a YubiKey that can be used for SSH public key authentication, you should harden your OpenSSH configuration to disable SSH password authentication.

Using the YubiKey on a New Computer

Fetch your public key from the keyservers. Then plug in the YubiKey and check the YubiKey’s card status to connect that YubiKey to the matching public key and subkeys.

gpg --recv-keys XXXXXXXXXXXXXXXX
gpg --card-status

Tags: authentication, encryption, GnuPG, OpenKeychain, OpenPGP, OpenSSH, PGP, security, signature, SSH, YubiKey