Using your YubiKey as an OpenPGP Smart Card

Last Updated: 2018-09-05

Key Storage is a Problem

Let's suppose you're a massive cryptography fanboy like me, and you have, for some baffling reason, decided to start using PGP to protect your emails. You went ahead and made yourself the beefiest key practical and you're confident that the best attacks currently possible against RSA would make breaking your key prohibitively expensive - except for Rubber Hose Cryptnalysis, anyway.

The problem is, PGP key sitting on some host machine some place, connected to the internet and subject to all the risks and vulnerabilities thus implied, isn't exactly safe. Sure, a Private Key at rest has some security elements to it - you did set a passphrase, right? - but a key in use doesn't, and we've already seen that computers aren't as secure as we think they are.

One solution to the overall problem of security in key materials was the development of the Smart Card - a securized processing solution on a convenient plastic card that could be used with a compatible reader to perform key operations on behalf of some host machine. You've probably seen these before at hospitals and other security-focused organizations - a slot in a terminal or laptop not unlike the card reader on a chip-and-pin enabled merchant pinpad. With the processor on the card doing the actual cryptographic math, the secure component (the private key) never needs to even be seen by the host machine at all. At present, it's the most secure way to handle keys that I'm aware of.

Of course, unless you have an enterprise-grade machine, that's going to be one more dongle to carry around - the reader for the card itself. I'm not exactly opposed, but most people like minimizing their kit, and if we can obviate the need for the reader, so much the better.

Fortunately, there's a solution, and one that I personally feel you should already have close to hand - your hardware authentication token. I'm a big advocate for these, and as far as I'm concerned, some of the best on the market right now are manufactured by Yubikey. A yubikey and an OpenPGP smart card have a lot in common - they're both discreet, secure cryptographic processors on a small, convenient form factor, which perform some form of cryptographic operation away from the host machine in a secure, reproducible way. Of course, generating an authentication OTP and performing a key signing are somewhat different. Maybe we're out of luck.

Since the YubiKey NEO, Yubikeys have been capable of acting as either a PIV smart card generally, or an OpenPGP Smart Card specifically. As it happens, the NEO was also the first token to add NFC support (making it useful in mobile applications) and the last model to be Open Source designed (and therefore to have independantly-verifiable security). I highly recommend this model.

What you'll need:

The simple version is a Yubikey Neo or newer, and a copy of either OpenPGP or a compatible implementation of that standard, such as the GNU Privacy Guard, which is what I use. GPG is available on all platforms and pretty much any computer with a modern operating system will have access to the right dependencies for use. As it happens I've set up my token to work on Ubuntu, OS X, and Windows machines all with a minimum of fuss.

Lastly, you will need a small thumb drive, SD card, or other easily-secured bit of media for the offline keystore, and a secure place to store that device.

Step 0: Device Prep and Dependency Installation

Whenever we are dealing with software, and especially with security software, there are some special considerations to be made. We can't just go and install whatever we want. We're going to open ourselves up to all sorts of nonsense. Fortunately, this is an old issue where we have a good solution - the Hash. It's very important that you check any packages you install for the programs we're going to discuss if we're going to install them manually. If you are especially paranoid you could do this all in a VM working from a fresh install of an OS which you know to be clean, or by using a reasonably secure live boot. I find TAILS has some applications in this regard due to its features that prevent you from writing directly to disk, but this also complicates the critical step of key backup.

Yubikey 4s and NEOs shipped after November 2015 already have all modes enabled. If you have an older NEO, see this guide.

If you are on a linux device, you can fetch the files more elegantly using debian's Advanced Package Tool (APT) or your other package manager of choice. It's up to you to whether to trust your package manager to do its own integrity checking or not. Using apt, the command is:

apt-get update && apt-get upgrade && apt-get install -y gnupg2 gnupg-agent pinentry-curses scdaemon pcscd yubikey-personalization libusb-1.0-0-dev

As a note, in my experience pinentry-curses, though recommended, is not really necessary. The rest of those programs are required, however.

If you're using windows, you only need GPG4Win and, if using for SSH, PuTTY or another compatible terminal program.

On a mac, you had ought to install homebrew, and use it to install, per: brew install gnupg yubikey-personalization hopenpgp-tools ykman pinentry-mac

From here, you have completed the portion of the process where it is desirable to have an internet connection - many of the paranoid would now recommend disconnecting.

Step 1: Hardening GPG for this Purpose

If you are on Windows, skip this step.

First, you will want to create a dir in /tmp which will not survive a system reboot, and assign this to the GPG home directory. This can be done from the CLI with export GNUPGHOME=$(mktemp -d) ; echo $GNUPGHOME.

Second, while the settings of GPG are reasonably secure by default, they could be better. You can achieve this result by catting in a whole bunch of text at once:

cat << EOF > $GNUPGHOME/gpg.conf
use-agent
personal-cipher-preferences AES256 AES192 AES CAST5
personal-digest-preferences SHA512 SHA384 SHA256 SHA224
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
cert-digest-algo SHA512
s2k-digest-algo SHA512
s2k-cipher-algo AES256
charset utf-8
fixed-list-mode
no-comments
no-emit-version
keyid-format 0xlong
list-options show-uid-validity
verify-options show-uid-validity
with-fingerprint
EOF

While a complete discussion of cryptographic hash security and cipher strengths, suffice it to say that these settings are suitable both in terms of "fitness for purpose" and general compatibility.

Step 2: Creating the Master Key

Regardless of whether you are using the Yubikey Neo or a Yubikey 4, you are going to need to create a master key with for-purpose subkeys. This is both an asset in a raw security model way and a necessary consequence of the design and behaviour of the device itself. Fortunately, since the Master Key never needs to touch the YubiKey directly, and indeed will rarely be used, it can be of any arbitrarily large size. For practical reasons I personally use a key no larger than 4096 bits, and I certainly wouldn't recommend anything smaller. There is still some debate in security circles whether or not the impact on performance of keys at 4096 is merited - RSA themselves suggest that a 2048-bit key will be sufficient for security up until around 2030, while the 4096 requires exponentially more compute time to crack in the same way and would likely be secure beyond your lifespan, barring advancements in computing or medicine as yet unseen.

First, invoke gpg2 --full-generate-key to begin key generation. This is an interactive process, so it's easy to walk you through. First, we need to select our key type - choose option 4, since all the master key is going to exist to do is issue revocation certificates and update the expiry information of keys. You'll be prompted to enter your keysize here, and then an expiry date.

While key expiry is a good way to manage some security risks, it really just adds an extra complication to the Master Key. Since the Master Key can update its own expiry date anyway, it's a pretty pointless complication to add. I suggest skipping it. Later, we're going to generate a revocation certificate that will allow you to invalidate the master key (and its subkeys) on demand anyway. (As of GPG 2.1, revocation certificate generation is automatic).

Next, enter a good, strong passphrase when prompted. Then, the system will prompt you for some identifying information. Depending on your use case it may, or may not, be desirable to fill out this information accurately. I personally have, but if you're using GPG to protect yourself in situations where anonymity is useful, not only is GPG not really your friend, but you definitely shouldn't put your actual details in here.

At the end of the generation process you'll receive a blob of information. Toward the bottom, on a line that begins with "pub", there will be a hex string. This is your keyID. Save this for the time being as an environment variable so that you can continue to use it throughout this process. Do this with export KEYID= and your key ID.

Step 3: Adding the Subkeys

You can now create additional keys, called subkeys, which exist as single-purpose extensions of the master key. A note on size: in the case of the Yubikey NEO, you will be limited to a size of 2048 Bits. If you have a Yubikey 4, you can use 4096 Bits. You still want to use the largest master key practical, as ideally, you will keep the master key securely for the rest of your life and never have to replace it.

Begin editing the key with gpg2 --edit-key $KEYID. What follows is an interactive process. You will need to go through this process three times - once each for an Encryption, Signing, and Authentication subkey. In each instance, use the command addkey to begin. While the Encryption and Signing keys are easy enough to generate, there is a trick to creating an RSA authentication-only key. Select option 8 and use the in-terminal instructions to toggle the capabilities until only authentication is enabled.

Step 4: Key Backup

Prepare your offline storage media by formatting it with LUKS encryption, or another encryption option available to you if your OS does not support LUKS - Windows users, have a look at VeraCrypt. Once that's done, mount the device, and make a note of the mountpoint.

To save time and simplify the process (so as not to have to muck about with secure deletes) we are going to write the key directly to the backup media with the following commands (Windows users must use "\" in paths!):

$ gpg --armor --export-secret-keys $KEYID -o /mount/path/to/dir/mastersub.gpg
$ gpg --armor --export-secret-subkeys $KEYID -o /path/to/dir/sub.gpg

You should also locate the revocation certificate and place that on the backup media. It will be at $GNUPGHOME/openpgp-revocs.d/[key fingerprint].rev

Finally, get the public version of the keys and the public-side of the ssh cert with the following two commands. The third command will push the key to the default keyserver (where it will eventually propogate widely).

gpg2 --armour --export-public-key $KEYID -o /mount/path/to/dir/public.gpg
gpg2 --armour --export-ssh-key $KEYID -o /mount/path/to/dir/ssh.key

Step 5: Setting up the "Smart Card"

Plug in your yubikey, and use gpg2 --card-edit to access the card control menu. Our first order of business will be to change the Admin PIN and the User PIN from their defaults of 12345678 and 123456, respectively, using the command passwd. Your new pins can be up to 127 ASCII characters long. Since you should be using a password manager anyway, feel free to make them as long as you feel is practical - as the keys won't be able to be retrieved from the device, the physical security of your yubikey is part of the security model here. Since impersonation would require theft, I don't advocate extremity in PIN selection the same way I do with passphrases.

As a side note, if you read the GPG documentation on OpenPGP smart cards, it mentions that entering the admin PIN more than a few times incorrectly will brick the card. This is true of the classic style PGP cards, but as this card is essentially an emulated card-in-reader solution, yubico has a method, shown here, for resetting the OpenPGP aplet to factory defaults in that event. If things come to that you will have to go through the process of adding your keys to the card again.

When you are done changing pins, set other values as you see fit, and set Signature PIN to "forced", then return to the normal command prompt with quit.

Now it's time to load in the subkeys! Just like we did to generate the subkeys, we need to invoke gpg2 --edit-key $KEYID in order to do this. From there, the command keytocard will let you push each key individually to the yubikey. While doing so, pay special attention to set the roles for the signing and authentication key correctly. Technically, both are signing keys, but as they are different, this is a potential sticking point for later.

Once the keys are on the smart card, run --card-status one more time to make absolutely sure, and then reboot into your native OS.

Step 6: Setting Up a Workstation

Since resetting the machine blew away our temporary GPG session, we'll need a new one. This is true whether we're at the same machine or whether we're setting up a second machine where we might need to use these keys. To begin, repeat the process for piping a secure configuration into the config file (or edit the file with your nano alternative of choice). Next, we need to import the public version of our keys. If the public version of the key is stored on a server, you can recall it that way as well using gpg2 --receive-key {Fingerprint}. Additionally, if there's a need for it, you should import your ssh key into any authorized_keys directories you need access to with it. Depending on your OS you may also need to configure your SSH client slightly differently, but I haven't experienced that issue in a few years, and I'm sure you'd be better served by finding a more up to date guide on it.