sungo.io


GPG: Strong Keys and Rotation (reissue)

Note: This is an edit of a blog post I published back in 2016. The original post interlinked GPG rotation and Keybase but I wanted to provide a version without Keybase

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.

Goals

  • New key with strong modern settings
  • Distinct subkeys for signing, encryption, and authentication

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.

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. 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.