Offline GPG Keys

A few days ago my first YubiKey arrived in the mail, so I was excited to copy over my GPG keys. Upon realizing that my old keys were far from perfect and have been created without much care, I decided to create a completely new key pair.

There are lots of tutorials out there on how to create GPG-Keys, but since none could answer all of my questions, I decided to write this post, linkinf to everython that I found helpful and summarizing all of the decisions I had to make.

GPG has a lot of options for you to coose from, so please make sure to understand what you do, before you publish your key. (They cannot be removed from keyservers, once added.)

An article covering keys, of course there needs to be a picture of one

An article covering keys, of course there needs to be a picture of one Photo by Shane Avery on Unsplash

What you might need GPG keys for

The classic use-case is the encryption and signing of emails but you can also use your keys to:

  • allow others to encrypt (instant-)messages only you can read
  • sign your messages, documents, software, git commits, …
  • login to your SSH server
  • build a web of trust
  • let tools encrypt credentials for you
  • share a password-manager with multiple people (e.g. gopass)
  • a lot more, just google for it

Terminology: GPG, GnuPG, PGP, OpenPGP

Because of their simmilar names, I will confuse GPG and PGP in the course of this article, please be forgiving that one.

  • OpenPGP is a standardized data format
  • PGP (Pretty Good Privacy) is a (now) proprietary program. The OpenPGP standard was developed to be independent of the corporate-owned PGP.
  • GnuPG is a free program suite, implementing the OpenPGP standard, providing the command-line tool gpg.

Decisions to be made

Unlike most tutorials on the web, that directly go straight into typing things into the command line, I want to lay out the decisions that I faced when creating the keys. That way, you can decide in what parts you want to divert from the usual procedures.

Usage of Subkeys

GnuPG allows you to create a master key and several sub-keys for it. These sub-keys are used for your day-to-day needs like decrypting your email and signing commits. Since it is rarely used, it is possible to store the master key completely offline, preventing it from being compormized if your laptop is stolen. Since the prospect of a completely air-gapped master-key is tempting, I chose to go for it. However, there are several cases where you need to use your master-key, have a look at the debian wiki for a short list. Be aware, that you need your master key for signing other peoples keys, this means you either need to take it with you to cryptoparties or note down the to-be-signed keys for you to sign them offline.

What kind of key

GnuPG prompts you to select what kind of key you want to create, if it is invoked with gpg --full-generate-key. Since RFC6637 also standardizes Elliptic Curve Cryptography (ECC) in OpenPGP, we can chose from ECC options as well with gpg --expert --full-generate-key.

So should you go for RSA or ECC ? There is no clear answer but a few things to consider:

  • RFC6637 is an extension of RFC4880, not requiring implementors of RFC4880 compliant software to also support RFC6637. This means you might encounter software that will work with RSA but not with ECC.
  • RSA is pretty easy to understand, ECC is way harder to grasp, see RFC6090, Chapter 6 of Koblitz or this primer.
  • ECC has a pretty interesing story: some of its curves are being suspected to contain NSA backdoors, ther might be patent issues and ECC is supposed to be more secure against post-quantum attacks but I have no clue about that topic.
  • ECC is widely used
  • The releases of GnuPG are signed-off by RSA keys, so if you suspect anyone having a sufficiently powerful Quantum-Machine, your RSA keys might not be the problem, since your software creating them might already be compormized. (By the way, did you verify the package before installing it?)
  • ECC keys are way shorter than (secure) RSA keys. This makes them easier to store on really-low-bandwidth storage mediums like paper.

For me, I chose to go with ECC, I mean fancy new tech is cool, wcgw?

So after selecting ECC in gpg, you are prompted to select a curve, amongst (currently 9 ) options. Make sure your used software and hardware does support the selected curve. Since firmware version 5.2.3, the Yubikey 5 supports Elliptic Curves in OpenPGP mode. The Yubikey however does not support all curves supported by GnuPG, so inform yourself before creating keys.

Key expiration

First off, let me clear out a common miunderstanding: an expiring key does not prevent an attacker, that stole the key, from using it. The reason for this is simple: If you have access to the private key, you can extend its expiry date. So, if an attacker stole the key, they can do the same.

Should you use an expiry date on your keys? And if so, on the subkeys and/or the master key? Of course, there is again no clear answer, so let me present you some arguments you can use when deciding for yourself.

Citing the gnupg docs:

For most users a key that does not expire is adequate. The expiration time should be chosen with care, however, since although it is possible to change the expiration date after the key is created, it may be difficult to communicate a change to users who have your public key.

They make a good point here: If you change the expiry date, people communicating with you need to be able to receive that update. This communication problem can be used on both sides of the argument: People that do not update their keys and have an expired key cached, will be prevented (though they can still ignore the expiry date) to send you messages to expired keys. So if you assume they would miss an update of your key expiry, you must also assume that they would miss a revocation of the key. Therefore, people not updating their keys should not be a valid argument.

So an expiring key should be used if you think you might loose your key and your revocation certificate. A more detailed explanation can be found here.

In my opinion, it makes sense to rotate the encryption sub-key, so that people are forced to use different keys over time, so that a compromized key (that you will revoke anyway), can’t be used to decrypt messages you received long ago (since they were sent encrypted with a different key).

Further, I do not see a reason for a signing key to expire: if I sign a document now, it is valid in the context i signed it for. If things change in a few months, then I rather publish a revocation for the signature than wait for some keys to expire. The semantics here are not really clear for me.

I decided to go for the following approach:

  • The master key will not expire
  • For the subkeys:
    • The encryption key will expire
    • The authentication key will expire
    • The signing key will not expire

Key backup

Here we have a trade-off: the prospect of you, loosing access to your key vs. someone else gaining access to the key.

The more copies of your key exist, the more possible places an attacker can try to attack.

So the most secure (but unrealistic) version would be you remembering the whole key, then its copy cannot be stolen (since it does not exist). However, due to the unrealistic nature of this approach, we need to lower the security here to achievable level. In the following, you find a few ideas.

  • Make backups, if you loose your private key, especially the master key, you have a serious problem.
  • Have a revocation certificate ready for the above case.
  • Make encrypted backups of your unprotected key. That way, you can have multiple backups of the key, each one encrypted with a different passphrase (one of which you might remember or hide away (see later)). Use something like AES with a strong enough key to encrypt it. Regarding ‘strong enough’: To cite Bruce Schneier on bute-forcing keys::

    And they [the rules of thermodynamics] strongly imply that brute-force attacks against 256-bit keys will be infeasible until computers are built from something other than matter and occupy something other than space.

  • Always keep the relevant xkcd in mind.
  • Be prepared that you might forget the passphrase for your encrypted backup. Have a way to recover your key even if you forget all of your passphrases (the key-passphrase and the backup-passphrase). One way to do this would be to create several keyshares for a threshold crytosystem with a tool like vault (might be a bit overkill but works nice) and bury the keyshares in the local forest, keep one in your basement (where noone finds anything anyways), give them to some of your friends or store it in the safe in your bank. That way, an attacker needs to know the location of n out of m key shares and physically go there (keep the linked xkcd in mind).
  • Your storage medium will decay, think about printing out the backup. For a if-all-else-fails solution, this is an option. An ECC key is not that long, that it would eb impossible to type it in correctly.

A good start might be this one:

➜  gpg -a --export 0xid >
➜  gpg -a --export-secret-keys 0xid > 0xid.pri.asc
➜  gpg -a --export-secret-subkeys 0xid > 0xid.sub.asc
➜  gpg -a --gen-revoke 0xid > 0xid.rev.asc

You can just zip everything up and restore your keys from this zip.

Please, try restoring your keys from the backup you just made, before you publish the key or use it for anything meaningful.

Your public key (as the name hints), can be accessible publicly, see the next section for some deatils.

Another pitfall: Don’t store your key-passphrase in your password-manager, protected by the key itself.

Key distribution

Your key contains your email address. This means, if someone gets to access your public key, they can see your email. This calls for spam. On the other hand, signing your email in your key is the whole point of using your GPG keys for email-encryption.

So, is it a good idea to publish your key on a keyserver? Decide this for yourself, someone else might upload your public key anyways.

You can also add your public key to github or gitlab to display your signed commits as verified.

Some people add their public key (not only the fingerprint) to each and every email. Please do not do this, don’t be the guy whose key is cluttering every mailing-list thread with 100 copies of the same key.

It makes more sense to publish your key-fingerprint in email-signatures, your website, github-bio etc and either publish the key on a keyserver or make the file available for download

Key creation

Make sure your key is not compromized before you even use it the first time: It might not be the best idea to generate your new keys on a virus-infested windows machine on which you have installed all those torrented ‘Linux-ISOs’.

In my case I chose to generate my keys on an old laptop, that had unfortunately lost most of its utility (the screen died and some keys broke). After surgically removing all wireless connectivity (screw out the broadcom screw-on-board) and installing a fresh linux distro on it, I felt confident that this is a secure-enough air-gapped system.

To check if everything works as expected, you can build a playground as described in the next section.

Keep in mind, that an untested backup is no backup, so go ahead and wipe your keys after creation and backup, just to re-import them from the backup ( and note down the procedure) to be sure that you can always go back to a everything-is-fine state.

Building a playground

We will build a small playground, so you can go through all of the steps to check if anything unexpected happens.

First, ensure you have a recent backup of your existing gpg home and have backed up your keys, so that if something goes wrong you are on the safe side.

Create a new directory and set the premissions, such that only you can access it.

➜  mkdir gpghome
➜  chmod 700 gpghome
➜  ll
drwx------ 2 marco marco 4,0K  7. Apr 14:07 gpghome

Then let gpg use the new directory as its home and go there:

➜  export GNUPGHOME=[yourpath]/gpghome

You can now use a gpg config file. Either write yourself one from scratch or use something like this one as a template.

You can check if this worked by asking gpg for its known keys (should be empty, since we have a new gpg home): gpg -k.

If you create a second playground, you can try out a few things like deleting keys, signing and verifying things, test backup strategies and revoking (sub) keys.

If you are interested in the details of the keys, have a look at this tutorial

Here a few things to ‘train’ with, before creating your real key:

  • Create two key-pairs with subkeys in two separated gpg environments
  • Create a backup, delete an environment and restore it from the backup by importing
  • Check what you can do with your master-key not being imported
  • Import the public key of the other environment (gpg --import ...)
  • Encrypt, sign, verify and decrypt a test file
  • Create another encryption-subkey and revoke the old one (Attention, calling --gen-revoke on a subkey will generate a revocation certificate for your master key)
  • Let the other environment know about the revocation
  • Sign one key with the other one (mini crypto party)
  • Completely revoke one key

One thing to note when testing: Don’t upload your test-keys to a keyserver: For one, they unneccesarily clutter their infrastructure. Even worse, you cannot delete keys from a keyserver. The only thing to do is to publish a revocation certificate but this won’t delete your key.

Generate your key

Just follow this tutorial and adopt the options to your liking.

As always, the ArchWiki is a great resource for reference.

A quick-checklist:

  • Have your key backed up
  • Ensure your revocation certificate is stored in a safe place (accessible even if you forget your key passphrase or the storage-medium is fried)
  • Make sure your public key is available for publication and you do not mess up naming the public and private keys.
  • If you want to keep your master key offline, only transfer the sub-keys to the work-machine.

My new keys

So after all, here is the fingerprint of my new favourite key:

11A0 9CF9 CD8F D395 FA00  40EB C343 79F1 3B20 7067

Going completely overboard - Vault

What to do if you really forget your passphrase(s)? You would need a backup of your key that can be retrieved without remembering a single passphrase. This does not exclude your backup from being encrypted. Here’s how:

  • Your unprotected private gpg key is exported to a file
  • That file is then encrypted with a symmetric cipher with a sufficiently strong password (e.g. WRNd1djkJ2a6bcwMEjeyZIpp4U+/mNvkugHN2yzYrG/m3Wba3T5yGDo8mQ01LYzB.
  • That passphrase is then split and stroed in such a way, that to recover it, m out of n splits need to be known.
  • The splits (aka key shares) are stored in several locations or at different persons. So you (or an attacker) has to retrieve m shares to recover the password for the symmetric encryption.

Since I could not find a program that just does exactly this, I chose to built this with Vault.

Vault is way too powerful for this use case but it works.

Upon initalization of a new ‘vault’, a strong key is created (but not persistently stored) that acts as a master key, encrypting all contained secrets. This master key is split in a configurable amount of shares, of which a configurable amount are required to recover the master key again. I distributed these shares at several places to ‘unseal’ the vault in case of an emergency. Along with a key-share, I stored a copy of the vault-database and an access-token (vault is running in a client-server mode, so you need to authenticate against the server from the CLI (did I mention vault is overkill?)).

The steps to do the above steps and store a secret are as follows:

# run the server
./vault server -config vault_config.hcl

# make life easier
export VAULT_ADDR=""

# init the vault, this shows you the unseal-keys (only once!)
./vault operator init -recovery-shares=5 -recovery-threshold=3

# unseal the vault (requires the key shares)
./vault operator unseal

# login as root with the generated root token
./vault login s.NiArKfIEYWygkHaEbO9U8r1W

# create a root token to store with each unseal key
./vault token create -policy=root

# enable the kv backend
./vault secrets enable -path=secret/ kv-v2

# add our superduper secret
./vault kv put secret/gpg_key password=foobar

# seal the vault
./vault operator seal

If you now stop the server and zip the data directory and store the unseal keys in different places, you have a recovery method that allows you to forget all passwords and still be able to retrieve your key.

Cover image by Shane Avery on Unsplash