kaashif's blog

Programming, with some mathematics on the side

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]
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"


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.


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.