GPG: Strong Keys, Rotation, and Keybase

2016-11-23 14:09 EST

I have sort of a love/hate relationship with strong crypto. I love it and hate that my sphere doesn't use it much. That lack of use sometimes leads me to complacency and my GPG key is no exception. I've used the same key since 2008 with no rotation. It's a 1024 DSA primary key with a single RSA subkey. The key's bounced around from machine to machine and who knows if it stayed secure the whole time. Also, DSA has fallen out of favor as has keylengths that short. Time to generate a new key.

Generating a new key is as straightforward as 'gpg --gen-key'. I've got a few additional goals, though. I also use keybase.io which adds some complications since it's badly documented and uses gpg signatures for its magic.

Goals

  • New key with strong modern settings
  • Distinct subkeys for signing, encryption, and authentication
  • A sane keybase.io transition

Why distinct subkeys?

Primer

First, it's important to remember that a GPG key isn't a single entity. It's more like a keyring. A subkey has its own identity (fingerprint) and function but it is associated with (and signed by) the primary key. A GPG key can have infinite subkeys, all tied together by the primary key.

A primary key can have four functions:

  • Certify - The ability to claim or trust other keys. This is how subkeys are tied together

  • Signing - Indicate that your key either created or approves a specific piece of content, like an email

  • Encryption - Transforming content into a secure form that can only decrypted by another specific key

  • Authentication - Allows the key to be used in place of a password in some cases. For instance, a GPG key can be used in place of an SSH key if the right magic is applied. (At some point, I'll write about this because it's hella difficult to get going on gpg versions below 2.1 (aka pretty much every OS except for FreeBSD.)

My 2008 key allowed the primary key to execute all four functions. That's a 'minimum viable' setup and is the easiest for first-timers. Unfortunately, this allows an attacker to gain full control of your GPG key from any machine that contains your keyring. In the setup I'm about to describe, the biggest risk is that an attacker could impersonate you.

Because Reasons

I decided to separate these functions into distinct subkeys. The primary key only has the ability to certify while subkeys contain the ability to sign, encrypt, and authenticate. This enables a few shiny tricks.

First, you can create what are sometimes called "laptop keys". Laptop keys contain only the subkeys for signing, encryption, and authentication. They cannot modify the main keyring in any way. This allows a secondary computer to perform the usual day-to-day actions without exposing the primary key. Laptop keys also can't modify their own expiration date. If a laptop is lost, the subkeys can be revoked or expired, telling the world not to trust or use those keys in the future.

Second, you can rotate subkeys as often as you want without needing to generate a whole new keyring. This is particularly handy for my use case at the moment where I'd prefer to avoid this whole-key rotation dance for as long as possible.

Third, you can move the primary key (the one with Certify ability) onto a USB stick or Yubico key and hide it away in a safe, keeping it hidden from an online attack.

Key Generation

Rather than copy and paste half of someone else's blog post, I recommend following the instructions in Mike English's article "Generating More Secure GPG Keys: A Step-by-Step Guide". Specifically, use the sections entitled "Generating The Primary Key" and "Adding Subkeys".

Since we are obsoleting an existing key, I strongly recommend adding a comment to the new primary key to provide a textual link. For instance, the comment on my new primary key is "(Obsoletes C134314B)". We'll deal with GPG trust in a moment but a comment allows a human to see the link between keys and know what's going on.

Web Of Trust

Since we've generated an entirely new key, we need to form a trust link between the old and new keys by cross-signing. (I'm assuming you are working on a host that has the secret parts of both your old and new keys.)

First, we sign the new key with the old key:

gpg --default-key ${old_key} --sign-key ${new_key}

Then we sign the old key with the new key:

gpg --default-key ${new_key} --sign-key ${old_key}

Uploading

To upload our keys, we need a public key server. I use pgp.mit.edu. Most of the key servers synchronize so this is largely a matter of preference.

Upload the keys like so:

gpg --keyserver hkp://pgp.mit.edu --send-keys $oldkeyid $newkeyid

Expiring and/or Revoking The Old Key

We need to tell the world to not use the old key anymore. There are two ways to do this.

Expiration is sort of the soft way. It tells the world not to use the key anymore but that you still control the secret parts. Expiration can be changed at any time with the secret parts so you could theoretically bring the old key back to life later.

The hard line is revocation. This tells the world to not trust the key and to scream bloody murder if they see content encrypted or signed with this key. Most clients throw up huge warnings or simply refuse to acknowledge the existence of revoked keys.

The "right" way is to revoke the old key.

First, you need to generate the revocation certificate:

gpg -a --gen-revoke $oldkey > $oldkey.supersede

sec  1024D/C134314B 2008-10-01 Matt Cashner (sungo) <sungo@sungo.us>

Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
(Probably you want to select 1 here)
Your decision? 2
Enter an optional description; end it with an empty line:
> Superseded by 0xAC70D9A5
>
Reason for revocation: Key is superseded
Superseded by 0xAC70D9A5
Is this okay? (y/N) y

Notice how I added a description that points to the new key id.

(You should also generate a revocation key using the reason "Key has been compromised". Store that in a safe offline location for use later if you ever lose control of the secret parts of your key.)

Now, import the revocation certificate and send it to the public key server:

gpg --import $oldkey.supersede
gpg --send-keys $oldkey

Laptop Keys

Once everything's squared away, we can create our "laptop keys". The laptop keyring contains the three subkeys we created via Mike's instructions but not the secret parts of the primary key. As mentioned previously, this allows the laptop to sign, encrypt, and authenticate but does not allow the laptop to certify or modify the basic properties of the keys.

First, we export the public side of the GPG keyring. Then we export the subkeys. Since the subkeys have their own identity, they have their own secrets.

gpg -a --export $mykeyid > ${mykeyid}.pub
gpg -a --export-secret-subkeys $mykeyid > ${mykeyid}_subkeys.gpg

On the laptop, first we import the secret side of the subkeys. Then we import the public key to fill in all the details.

gpg --import ${mykeyid}_subkeys.gpg
gpg --import ${mykeyid}.pub

Make sure to transport the 'mykeyid_subkeys.gpg' file securely since those subkeys can encrypt and decrypt content.

Keybase

Keybase.io aims to make life with GPG easier. It does a really good job but it encourages what I consider bad behavior in key management. By default, it creates and stores a private key on their server. We can never truly verify the condition of their server so a Keybase-hosted private key should always be considered untrustworthy.

I keep my key locally and use the keybase CLI client. This leads us to some undocumented waters as the Keybase folks are focused on the usability of the web site. (I'm assuming from here on out that you've got the keybase CLI client installed and configured.)

If you already have a GPG key attached to your Keybase profile, it's possible to add additional keys. You can import your new key like so:

keybase pgp select --multi $newkeyid

If you go to your Keybase profile on the web, you can see two GPG keys listed.

Since we've revoked the old key, we need to remove it from our Keybase profile. On the website, hit "edit" next to the signature for the old key and select "Revoke this public key". A popup will appear, detailing the CLI commands to run to drop the old key. 'keybase pgp select' uses GPG key IDs but 'keybase pgp drop' uses Keybase internal IDs. The popup will tell you which ID to use.

keybase pgp drop "$keybase_id"

Adding and revoking keys in this fashion does not store your private GPG key on Keybase's servers. The inner workings of Keybase are complicated at best, involving GPG signatures, Merkle roots, and the bitcoin blockchain. The important fact here is that this process tells Keybase, and its users, that this GPG key is yours and can be used to sign and encrypt. A Keybase user can track/follow you and retrieve your GPG public key using the 'keybase pgp pull $userid' command.

Keybase added a UI that shows a bit about how everything ties together. Looking at my profile, it's possible to see how the signatures and devices all tie together via a directed graph or the signature chain.

Conclusion

Whew. That's a lot of words for a handful of CLI commands. I'm hoping, though, that I conveyed the "why" of my approach without getting too much into the theory. If this is still confusing as hell, hit the "Contact Info" link in the header and let me know how I can improve it. There's so much about GPG on the web and so little of it makes sense to people who don't care about the math or details of public key cryptography.

Credit Where Credit Is Due

I used a series of articles by Mike English to get through this process mostly unscathed.