kaashif's blog

Programming, software freedom and Unix


Backing up PGP private keys

There are hundreds of blog posts about backing up your PGP keys around on the internet. Most of them just say something like: put a passphrase on it, keep it on a USB stick, a CD, a floppy disk, or something like that. These are all very useful ways to back important stuff up - in fact, I just restored a backup of my GPG keys from a CD after deleting them by accident. These mediums are, however, not going to survive for many decades like paper can. And if you store to a writeable medium like a rewritable CD, DVD, USB stick or floppy, there is still the danger of you trying to restore then accidentally deleting your backup. Or, more likely, you write over it without realising many months later that you wrote a Linux distro or movie to that DVD that had your only PGP key backup.

There are dozens of blog posts floating around about paperkey, which is a program intended to extract the important bits of a PGP key, output this binary data in a pleasing human-readable format (basically a hex dump), then allow you to print it out and have a reasonably short thing you could recover your private PGP key from. The reason paperkey has a use at all is that without it, the whole “private” key (which includes a copy of the public key) would be ludicrously long and impossible to type into a computer correctly. By this, I mean it will be about 3000 bytes long. This is short for a computer, but very long for a human.

There is still the question of how to recover this paper key. The difficulty of this will be heavily dependent on how exactly you choose to encode your private key before printing it out. Here are a few methods of encoding binary data as something easily retrievable from a printout.

Getting started

First, I’ll generate a dummy key so I can walk you through the steps of backing everything up.

$ gpg --gen-key
gpg (GnuPG) 2.1.18; Copyright (C) 2017 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Note: Use "gpg --full-generate-key" for a full featured key generation dialog.

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

Real name: Joe Bloggs
Email address: joe@bloggs.com
You selected this USER-ID:
    "Joe Bloggs <joe@bloggs.com>"

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

...unimportant output...

pub   rsa2048 2017-03-11 [SC] [expires: 2019-03-11]
      B39C0AB5ED8C797D25A559B7DDEC6B207C796781
      B39C0AB5ED8C797D25A559B7DDEC6B207C796781
uid                      Joe Bloggs <joe@bloggs.com>
sub   rsa2048 2017-03-11 [E] [expires: 2019-03-11]

There are several choices at this stage. You can either just use GPG’s ASCII output and mangle it some way yourself, or you can use paperkey.

Using GPG’s built in output

Simply run:

$ gpg -a --export-secret-key "Joe Bloggs"
-----BEGIN PGP PRIVATE KEY BLOCK-----

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
=bte1
-----END PGP PRIVATE KEY BLOCK-----

You could print that out and try to type it in to recover. That would be impossible. You could also print it in a nice font and try to use OCR to read it. That might work, but if the OCR software makes even a tiny error, you’d be incredibly hard-pressed to find it. I do not recommend doing this.

You could pipe this into xxd, which would turn it into a hex dump. OCR software may have an easier time with only letters and numbers, but I still don’t recommend it.

If you do go for this method and try to restore from backup, here’s how you do that (after getting your paper key into text file form by either typing or OCRing):

$ gpg --import my-key.txt

Cutting down the size using paperkey

First, we install paperkey. You could go over the sources yourself to verify it’s not malware then compile it yourself, but I have a nominal level of trust in the Debian package maintainers. Thus this is fine for me:

$ sudo apt install paperkey

Next, we export our secret key, pipe it through paperkey to make sure we only end up with the secret bits, then pipe that into a file. There are a couple of different ways to do this.

Using paperkey’s built-in base16 output

This is perhaps the easiest way to proceed. Run the following:

$ gpg --export-secret-key "Joe Bloggs" | paperkey
# Secret portions of key B39C0AB5ED8C797D25A559B7DDEC6B207C796781
# Base16 data extracted Sat Mar 11 00:27:46 2017
# Created with paperkey 1.3 by David Shaw
#
# File format:
# a) 1 octet:  Version of the paperkey format (currently 0).
# b) 1 octet:  OpenPGP key or subkey version (currently 4)
# c) n octets: Key fingerprint (20 octets for a version 4 key or subkey)
# d) 2 octets: 16-bit big endian length of the following secret data
# e) n octets: Secret data: a partial OpenPGP secret key or subkey packet as
#              specified in RFC 4880, starting with the string-to-key usage
#              octet and continuing until the end of the packet.
# Repeat fields b through e as needed to cover all subkeys.
# 
# To recover a secret key without using the paperkey program, use the
# key fingerprint to match an existing public key packet with the
# corresponding secret data from the paper key.  Next, append this secret
# data to the public key packet.  Finally, switch the public key packet tag
# from 6 to 5 (14 to 7 for subkeys).  This will recreate the original secret
# key or secret subkey packet.  Repeat as needed for all public key or subkey
# packets in the public key.  All other packets (user IDs, signatures, etc.)
# may simply be copied from the public key.
#
# Each base16 line ends with a CRC-24 of that line.
# The entire block of data ends with a CRC-24 of the entire block of data.

  1: 00 04 B3 9C 0A B5 ED 8C 79 7D 25 A5 59 B7 DD EC 6B 20 7C 79 67 81 F905AA
  2: 02 8B 00 07 FB 06 1E D2 34 D4 83 5E 32 78 AC 2D 4D 59 BA CD B5 41 84698B
  3: A2 AE 3C 4F CA 56 85 A1 21 78 9C 73 DE 27 C5 68 52 69 DD 8F 0B 1D 5CBE5F

... lots more output ...

This is a pretty good printout to have: it has instructions on how to recover, it has CRCs for every line and for the whole thing, everything’s in nicely formatted base16. There is a problem: it can’t be easily read by a machine.

So you could OCR it and feed it into paperkey again, but this is error-prone. There are CRCs, so you can easily check if each line is correct and guess the correct line (since the errors are not random, OCR will mistake similar-looking letters for one another).

Here is how you would do that (again, after converting your paper backup to a text file):

$ paperkey --pubring pubring.gpg --secrets printout.txt --output secretkey.gpg

Where the pubring.gpg is the file located in ~/.gnupg after you’ve imported your public key. You can then import secretkey.gpg as usual.

Converting to a machine-readable paper format

You might want to convert to a QR code, barcode or some other machine-readable image format. For this section, I’ll assume you have your secrets in a secrets.bin file, made as follows:

$ gpg --export-secret-key "Joe Bloggs" | paperkey --output-type raw --output secrets.bin

There are a few ways to convert this to a machine-readable image: let’s start off with the most well-known, QR codes.

Encoding as a QR code with qrencode

Install qrencode:

$ sudo apt install qrencode

Now you can encode your secrets.bin as follows:

$ qrencode --8bit --level=M -o key.png < secrets.bin

It looks like this:

Note: the reason I used the error correction level of M instead of the highest, H, is that the resulting image would be obnoxiously large and hard to print out (except on a poster). Printing a large image on a small sheet of paper would only defeat the whole purpose of error correction: you would be introducing more errors (squashed image) in order to reduce errors.

If you find that your key’s image is way too big, turn the error correction level to L (this is actually the default).

This is great, but if your key is huge (maybe your chosen key format is just big), you might notice that the largest type of QR code, a version 40 177x177 one, can only store 2953 bytes. This is a serious problem since if your key is too big, you’re simply SOL - there is no workaround. We’ll have to find a different format.

If this problem doesn’t affect you, then I highly recommend this method: it’s really easy to restore a QR code backup with any QR code reader.

Encoding as a data matrix with dmtxwrite

Install the program:

$ sudo apt install dmtx-utils

Now, encode your secrets:

$ dmtxwrite -e 8 -f PNG -o key2.png < secrets.bin

You’ll end up with this:

This has fewer problems than QR codes, in my opinion, since there is no limit to the size of the data you’re encoding. It is just as easy to restore the data, though, so really this is an almost purely aesthetic choice if either will work for you. If your key is huge, use this. If your key is not huge, it doesn’t matter which you pick.

Encoding with a linear barcode

No, this is stupid. Do you know how long that barcode would have to be? And half of it would need to be error correction stuff. And folding the paper in half would probably triple the number of errors you get.

Conclusion

I would personally slap my private key into a paper data matrix and CD-R and call it a day. I keep a folded up copy of my private key in my wallet, just in case. In case of what, I have no idea.

Just don’t put your private key online (even if it has a passphrase), that’s just idiocy. Even if you’re storing it with someone you trust, this doesn’t mean the network between you and them is also trustworthy. The only type of offsite backup of this sort of sensitive data you should be keeping is one that you physically transport offsite yourself.