Writing mostly about computers and math.


A sign outside a key cutting place.

Original image from Jay Gooby on Flickr. Some rights reserved: cc by.

I'll preface this by saying that if you just want encrypted email and don't mind having a new email address then your best bet is to just use something like ProtonMail. It automatically generates and manages keys for you so it can encrypt mail to other people with keys and decrypt encrypted mail sent to you. If you don't want to bother setting it up yourself and you're OK with the limitations of their system then it's probably your best choice. If you'd like to know how to use GPG and how you can use it to encrypt whatever you want then read on.

Public-Key Cryptography

Public-key cryptography was invented in the 1970's - twice as it turns out. Once in secret at GCHQ and again a few years later by some cryptographers in the US. One of the main innovations that gave rise to modern public-key crypto is called RSA after its inventors: Ron Rivest, Adi Shamir, and Leonard Adleman. RSA takes advantage of the difficulty of factorization to provide security. To put it simply, figuring out which two prime numbers were multiplied together to give the key is hard enough that nobody can break RSA in a reasonable amount of time (if the key is big enough that is - a number like 15 is pretty easy to factor into 3 and 5).

These keys are usually used with a system like OpenPGP that provides a selection of encryption algorithms and lets you do key generation, compression, signing, symmetric encryption, and lots of other things. GPG is a free OpenPGP client that implements a lot of different algorithms but the parts we care about involve public and private keys. People smarter than me have written in great detail about how the math works so I won't cover that here since it's not the point of this article.

For the purposes of this article, a key consists of two parts: a public part that you spread around as widely as you can and a private part that you keep to yourself. Something encrypted with one key can only be decrypted by the other. If I want to send a message that only you can read then I use your public key since only your private key can decrypt it.

The opposite is true too; I can encrypt something with my private key and anyone with the public key can decrypt it. It's not secret since anybody can get the public key but it's still useful because the keys can only decrypt things encrypted with their counterpart. This is called signing because it works a lot like a handwritten signature; it's an attestation that the thing that is signed came from the person whose signature is on it, i.e. the person with the private key. Digital signatures are useful in lots of situations but that's also not the point of this article so I'll just leave it at that.

Generating a Key

The first step in getting a key is to install GPG. For most *nix systems the package is just called gpg:

$ apt install gpg

On other operating systems there are other options. On macOS you can do something like the above if you use MacPorts or Homebrew; I think the package is called gnupg but the installed binary will be available as gpg so all the commands in this article should work just fine from your terminal. If you want a GUI and Apple Mail integration then you can use GPG Suite or a similar program.

Windows users will want to use something like Gpg4win. This gives you a GUI key manager (Kleopatra) as well as an Outlook plugin and, most importantly, gpg.exe. The commands here should work fine in the Windows command line except for --full-gen-key; on the version of Gpg4win that I tested you have to use the --gen-key flag instead but it appears to do the same thing.

Once the installation finishes you're ready to generate your first key (remember to use --gen-key with Gpg4win).

$ gpg --full-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.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 

So here GPG is asking us what kind of key to generate. We want to be able to use the key for both encryption and signing so we have to pick one of the first two options. The second option, DSA and ElGamal has slightly worse properties than RSA for what we want to do so we'll pick RSA here.

Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)

Now we have to tell it how big to make the key. The trade-off is basically that bigger keys are more secure but slower. We don't really care about speed here (modern CPUs can do thousands of operations per second even with 4096-bit keys) so we'll use the max of 4096 bits.

Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 

Deciding the key's validity period is up to you and it's possible to change it after the key has been generated if you really want to. It's useful to have the key expire for a lot of reasons: you could lose your password, your private key could be compromised, and so on. One year isn't a bad choice so that's what I'll use here.

Key is valid for? (0) 1y
Key expires at Wed 28 Feb 2018 10:47:38 PM EST
Is this correct? (y/N) y

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

Real name: 

Now that that's out of the way we just need to tell GPG about the identity associated with this key. In general you want to use your real name and email address but use whatever your situation calls for. This information will be used in GPG's web of trust so it's important that people be able recognize the key as yours if you want the key to be trusted (more about that later). Once that's all filled out you'll be prompted for a password for the private key. This is very important so make sure you choose a strong password that you won't forget. If you forget this password there is no way to recover it and you will not be able to decrypt anything encrypted with its corresponding public key.

Real name: Peter Beard
Email address: samplekey@peterbeard.co
Comment: This is a sample key.
You selected this USER-ID:
    "Peter Beard (This is a sample key.) <samplekey@peterbeard.co>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 5DF274C713D0B439 marked as ultimately trusted
gpg: revocation certificate stored as '/home/peter/.gnupg/openpgp-revocs.d/543C1C8FD5B8FAFDCDC714FD5DF274C713D0B439.rev'
public and secret key created and signed.

pub   rsa4096 2017-03-01 [SC] [expires: 2018-03-01]
uid                      Peter Beard (This is a sample key.) <samplekey@peterbeard.co>
sub   rsa4096 2017-03-01 [E] [expires: 2018-03-01]

Entropy collection and prime generation take a while, even on a fast computer, so don't be surprised if it sits for a few minutes without doing anything. When it finishes it'll store the key in its database for you to use. But how to use it?

Using the Key

With an RSA keypair we can basically do three things: encrypt, decrypt, and sign. GPG will operate on files or on STDIN so you can use it to encrypt pretty much whatever you want. For example, if somebody wants to encrypt a short message for me with this key, they can just do this:

$ echo "SECRET MESSAGE" | gpg -r samplekey@peterbeard.co --encrypt --armor


The -r lets you specify whose key to use when encrypting - you can see I just gave it my email address but there are other ways to specify a key that I'll mention in the end of this section. The --armor option basically just takes the binary output from GPG and Base64 encodes it so we can treat it like text. This is almost always what you want.

Now let's say I've got one of these PGP message blocks in a file. I can decrypt it like this since I have the private key that goes with it:

$ gpg --decrypt ./SECRET.asc 
gpg: encrypted with 4096-bit RSA key, ID 3E308101CBDD0638, created 2017-03-01
      "Peter Beard (This is a sample key.) <samplekey@peterbeard.co>"

Pretty neat, right? GPG knows which private key it needs to decrypt it since the public key it used to encrypt is stored in the output. Once you give it the password it spits out our secret message. If you try to decrypt a message you don't have the private key for you'll see this:

$ gpg --decrypt ./government-secrets.asc 
gpg: encrypted with 8192-bit RSA key, ID E05AD409D6DFD684, created 2015-04-10
      "WikiLeaks Editorial Office High Security Communication Key (You can contact WikiLeaks at http://wlchatc3pjwpli5r.onion and https://wikileaks.org/talk) <contact-us-using-our-chat-system@wikileaks.org>"
gpg: decryption failed: No secret key

We can also sign things if we want to prove that they came from us. Let's say we're sending a friend a nice birthday greeting and we want them to know it's really from us. That might work like this:

$ echo "Happy birthday!" | gpg --sign --armor > birthday.sig
$ cat birthday.sig 


If we send that file to the birthday boy or girl then they can read it and check that it's really from us (and not from somebody trying to cover for the fact that we forgot their birthday) like this:

$ gpg --decrypt birthday.sig 
Happy birthday!
gpg: Signature made Wed 01 Mar 2017 01:19:42 AM EST
gpg:                using RSA key 543C1C8FD5B8FAFDCDC714FD5DF274C713D0B439
gpg:                issuer "samplekey@peterbeard.co"
gpg: Good signature from "Peter Beard (This is a sample key.) <samplekey@peterbeard.co>" [ultimate]

Key Management

By default GPG keys are stored in databases called "keyrings." Usually these live in files called ~/.gnupg/*.gpg but you can export subsets of your keys into external keyrings if you want to. We will definitely want to export the public key we just generated so let's look at how to do that.

Exporting Your Public Key

To share your public key with anybody you'll probably want to get it into a format that you can move around on the internet. With gpg we can just use --export to get a key out of a keyring, but without any information it will just dump all of the keys from all of the keyrings which is really not what we want. To pick a specific key, we need some kind of identifier. Fortunately, GPG will give us one.

$ gpg --list-keys
pub   rsa2048 2009-09-04 [SC] [expires: 2020-08-29]
uid           [ unknown] deb.torproject.org archive signing key
sub   rsa2048 2009-09-04 [S] [expires: 2018-08-30]

[ redacted some keys ]

pub   rsa4096 2017-03-01 [SC] [expires: 2018-03-01]
uid           [ultimate] Peter Beard (This is a sample key.) <samplekey@peterbeard.co>
sub   rsa4096 2017-03-01 [E] [expires: 2018-03-01]

[ redacted more keys ]

This will print a list of all the keys GPG has stored on your machine (in my case, all the keys in ~/.gnupg/pubring.gpg). If you installed Gpg4win or another GUI client then you can see this list there too. Anyway, we can see the key we generated in the middle there: rsa4096 2017-03-01 [SC] [expires: 2018-03-01]. Under that we see what's called the key's "fingerprint," basically a hash of the key — in this case 543C1C8FD5B8FAFDCDC714FD5DF274C713D0B439. That's unique so we can use that to tell GPG to export just that key, either with the whole fingerprint or just the last 4 bytes like this:

$ gpg --armor --export 13D0B439

                       and so on and so on

It's also possible to just select the key by email address like we did before but it's important to know how to use fingerprints too in case there's more than one key for an email address or something like that.

Fingerprints are also useful for making sure that you have a genuine key. If somebody emails you their public key they could then call you to verify that you got the correct key by checking the fingerprint you have against the one they have. If they don't match then one of the keys was tampered with.

Exporting Your Private Key

You will also probably want to export your private key so you have a backup. You'll notice that it's not listed when we do --list-keys; that's because we have to --list-secret-keys to see our private keys.

$ gpg --list-secret-keys
[ redacted ]

sec   rsa4096 2017-03-01 [SC] [expires: 2018-03-01]
uid           [ultimate] Peter Beard (This is a sample key.) <samplekey@peterbeard.co>
ssb   rsa4096 2017-03-01 [E] [expires: 2018-03-01]

Fortunately a private key has the same fingerprint as its corresponding public key so we can just use the number we already found to export it. Now, for backing up private keys, there are two options (and you might want to do them both): a digital key or a paper key.

To export a digital copy of your key the process is almost the same as exporting a public key:

$ gpg --armor --export-secret-keys 13D0B439

                many more lines of output here

You can save that to a file and then import it later if we want to use the key on another machine or if the original key is lost. Make sure to keep this file safe since if somebody has it they just have to guess your password instead of crack the actual key itself.

For paper keys, we can use a special tool called, apropriately, "paperkey." It's in the repos for most package managers; on my Debian machine it's just

$ apt install paperkey

to install it.

Once installed, there are a few different ways to use paperkey. The easiest is to just pipe the output from GPG right into paperkey (note the missing --armor):

$ gpg --export-secret-keys 13D0B439 | paperkey --output paperkey.txt

That produces a fairly large text file that contains a printable version of the private key. Re-importing it is not completely trivial so I won't cover it here but if the key is important to you then it's a good idea to print this out and store it somewhere secure just in case.

Importing Keys

If we have one of these files from somebody else then importing it is easy:

$ gpg --import pubkey.gpg
gpg: key 5DF274C713D0B439: "Peter Beard (This is a sample key.) <samplekey@peterbeard.co>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1

That key was already in the database but you get the idea. That's great if we have a file with the key in it, but what happens if all we have is somebody's email address? Can we send them an encrypted message? It turns out that we can. The infrastructure that makes that possible is a bit more complicated to explain and I think this article is long enough. I guess I'll make a series out of this topic and put a link to the next part here when it's written.

So, to recap:

  • $ gpg --full-gen-key to generate a key
  • $ gpg --armor --encrypt -r <email address> to encrypt something
  • $ gpg --armor --sign <something> to sign something
  • $ gpg --decrypt <something> to decrypt something or check a signature