This page is about setting up and using GPG. It's a bit like an alternate manual. GPG is actually GnuPG, a complete and free implementation of the OpenPGP standard as defined by RFC 4880 (also known as PGP). My problem has always been putting it all together, and to explain how to use it. Hopefully this page will help you do just that.
This page is for people who are able and willing to use the command line. The problem with explaining how to exchange encrypted messages is that it varies from tool to tool. The command line is the same everywhere. It may be hard to use, but the benefit is that all the various steps can be explained easily in text. This is also why so many git problems are answered with command line invocations of git itself. It's the common denominator. Using gpg directly, on the command line, has the same goal: it's the common denominator, and it's easy to explain in text.
Exchanging public keys with partners
Using a Revocation Certificate
We'll generate a new secret key using GPG. A secret key is like your identity. You need to keep it safe. Every secret key comes with a public key. This is what other people will need to send you email. You'll have to get it to them somehow. In return you will get the public keys of your partners. Both secret and public keys are stored in keyrings in your home directory.
Here's what an initial run of gpg looks like:
alex@melanobombus:~$ gpg --list-keys gpg: directory '/home/alex/.gnupg' created gpg: keybox '/home/alex/.gnupg/pubring.kbx' created gpg: /home/alex/.gnupg/trustdb.gpg: trustdb created
The “~/.gnupg” directory is where all the files are kept. The “pubring.kbx” file is where the keys are kept. This is the important one. You should backup this file.
Let's create our secret key using `gpg --generate-key`.
Here's what you want to answer:
1. a name
2. an email address
3. a passphrase
A *passphrase* is like a very long password. Use a good one and don't forget it. All your other passwords will end up being protected by this one passphrase.
Here's what the entire process looks like:
alex@melanobombus ~> gpg --full-generate-key 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? 1 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (3072) Requested keysize is 3072 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) 2y Key expires at Sun 16 Feb 2025 12:02:37 PM CET Is this correct? (y/N) y GnuPG needs to construct a user ID to identify your key. Real name: Alex Schroeder Email address: alex@alexschroeder.ch Comment: You selected this USER-ID: "Alex Schroeder <alex@alexschroeder.ch>" 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. 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: revocation certificate stored as '/home/alex/.gnupg/openpgp-revocs.d/C9F3BC47287D8DDEA5576132069E0DD06A9B3268.rev' public and secret key created and signed. pub rsa3072 2023-02-17 [SC] [expires: 2025-02-16] C9F3BC47287D8DDEA5576132069E0DD06A9B3268 uid Alex Schroeder <alex@alexschroeder.ch> sub rsa3072 2023-02-17 [E] [expires: 2025-02-16]
The output here lists your fingerprint. You can reprint this using the command `gpg --fingerprint` and an email address. Over the years you might create more and more keys like that so the list gets longer and longer. Watch out for the expired keys!
I like to expire my keys. This simplifies things because I don't have to worry about revocation certificates and all that. If I forget about the key, it’ll be unusable after a few years anyway.
Also note that the message told you about a revocation certificate it created. If you keep it somewhere safe, you'll be able to tell other people that your key got compromised and that it shouldn't be used anymore, in a cryptographically secure manner. This assumes that your enemies might be trying to spoof your friends, telling them that you switched keys when in fact you haven't. The revocation certificate is how you tell them you *are* switching keys!
Usually, you don't have to do that. Instead, you'll *extend* your key, moving the expiration day up a year or two. See below for more about that.
Use `gpg --edit-key FINGERPRINT` to edit the new key and add new email addresses.
A new email address is called a user id (uid), so the command to use to edit the key is `adduid`.
alex@melanobombus ~> gpg --edit-key C9F3BC47287D8DDEA5576132069E0DD06A9B3268 Secret key is available. sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1). Alex Schroeder <alex@alexschroeder.ch> gpg> adduid Real name: Alex Schroeder Email address: alex@gnu.org Comment: You selected this USER-ID: "Alex Schroeder <alex@gnu.org>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1) Alex Schroeder <alex@alexschroeder.ch> [ unknown] (2). Alex Schroeder <alex@gnu.org> gpg> save
Use multiple `adduid` commands to add more email addresses. Surprisingly, `save` ends editing, much like a text adventure.
This is also where you set the key to "ultimate trust":
alex@melanobombus ~> gpg --edit-key C9F3BC47287D8DDEA5576132069E0DD06A9B3268 Secret key is available. sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1). Alex Schroeder <kensanata@gmail.com> [ unknown] (2) Alex Schroeder <alex@gnu.org> [ unknown] (3) Alex Schroeder <alex@alexschroeder.ch> gpg> trust sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1). Alex Schroeder <kensanata@gmail.com> [ unknown] (2) Alex Schroeder <alex@gnu.org> [ unknown] (3) Alex Schroeder <alex@alexschroeder.ch> Please decide how far you trust this user to correctly verify other users' keys (by looking at passports, checking fingerprints from different sources, etc.) 1 = I don't know or won't say 2 = I do NOT trust 3 = I trust marginally 4 = I trust fully 5 = I trust ultimately m = back to the main menu Your decision? 5 Do you really want to set this key to ultimate trust? (y/N) y sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1). Alex Schroeder <kensanata@gmail.com> [ unknown] (2) Alex Schroeder <alex@gnu.org> [ unknown] (3) Alex Schroeder <alex@alexschroeder.ch> gpg> save
Pick one of the user ids as your primary:
alex@melanobombus ~> gpg --edit-key C9F3BC47287D8DDEA5576132069E0DD06A9B3268 Secret key is available. sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1). Alex Schroeder <kensanata@gmail.com> [ unknown] (2) Alex Schroeder <alex@gnu.org> [ unknown] (3) Alex Schroeder <alex@alexschroeder.ch> gpg> 3 sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1). Alex Schroeder <kensanata@gmail.com> [ unknown] (2) Alex Schroeder <alex@gnu.org> [ unknown] (3)* Alex Schroeder <alex@alexschroeder.ch> gpg> primary sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1) Alex Schroeder <kensanata@gmail.com> [ unknown] (2) Alex Schroeder <alex@gnu.org> [ unknown] (3)* Alex Schroeder <alex@alexschroeder.ch> gpg> save
What if you get “gpg: agent_genkey failed: Permission denied” as you’re trying to generate the key? The problem might be that you switched user accounts inside your terminal. The problem is that gpg is trying to prevent the old account from snooping. Here’s an example where I switch from the “alex” account to the “guest” account:
alex@melanobombus:~$ sudo su guest guest@melanobombus:~$ gpg --generate-key gpg (GnuPG) 2.2.12; Copyright (C) 2018 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: Agnes Li Email address: agnes@li.name You selected this USER-ID: "Agnes Li <agnes@li.name>" Change (N)ame, (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: agent_genkey failed: Permission denied Key generation failed: Permission denied
Here’s how I can show that indeed the terminal belongs to “alex”:
guest@melanobombus:~$ ls -l $(tty) crw--w---- 1 alex tty 136, 1 Apr 1 22:46 /dev/pts/1
The solution is to use `tmux`:
guest@melanobombus:~$ tmux guest@melanobombus:~$ ls -l $(tty) crw--w---- 1 guest tty 136, 2 Apr 1 23:06 /dev/pts/2
And now `gpg --generate-key` should work as expected.
This is what we would send our partners:
alex@melanobombus:~$ gpg --export --armor alex@gnu.org -----BEGIN PGP PUBLIC KEY BLOCK----- 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 5BklAoTR0s26VD63cQSIKLwt0V+u =hQcc -----END PGP PUBLIC KEY BLOCK-----
Let’s export it to a file:
alex@melanobombus:~$ gpg --export --armor alex@gnu.org > alex.pub
When contacting somebody for the first time, you probably want to attach this file, or just paste it into the email. They would save the attachment, or copy and paste this block into a file called `alex.pub` and import it. Here, I got a public key from a friend and imported it.
alex@melanobombus:~$ gpg --import roland.pub gpg: key A5E1F208237B14BE: public key "Roland Li <roland@li.name>" imported gpg: Total number processed: 1 gpg: imported: 1
If you meet your partners face to face, giving them a copy of your public key is easy. If you never met, it's harder. How do you make sure that criminals didn't interfere? This is called a man-in-the-middle attack. You could make it harder by publishing your fingerprint on various channels. Attackers would have to replace these fingerprints everywhere. It's not perfect, but it's much better than nothing.
alex@melanobombus:~$ gpg --fingerprint alex@gnu.org pub rsa3072 2020-05-07 [SC] [expires: 2022-05-07] ABEF 08C3 4DB4 DB8A 73EB BBB8 AE49 5BC6 3253 DDE8 uid [ultimate] Alex Schroeder <alex@gnu.org> sub rsa3072 2020-05-07 [E] [expires: 2022-05-07]
The fingerprint is right here: `ABEF 08C3 4DB4 DB8A 73EB BBB8 AE49 5BC6 3253 DDE8`. Put it on your web page, in your email signatures, tweet it, and so on.
Since we just got a key, we should do the same, of course:
alex@melanobombus:~$ gpg --fingerprint roland@li.name pub rsa3072 2020-05-07 [SC] [expires: 2022-05-07] B1D4 7AA4 E828 115D FAF1 46AA A5E1 F208 237B 14BE uid [ unknown] Roland Li <roland@li.name> sub rsa3072 2020-05-07 [E] [expires: 2022-05-07]
Perhaps he sent us the fingerprint via Signal, Threema, phone call, website, etc. It’s just good form to verify it using some other channel.
Todo
Let's encrypt a file before we get started. Create a text file using your favourite editor and save it as `message.txt`. Let’s encrypt it:
alex@melanobombus:~$ gpg --encrypt --armor –recipient alex@gnu.org roland@li.name message.txt
Now, you'll have an encrypted file called `message.txt.gpg` next to the unencrypted `message.txt` file and both you and Roland can decrypt it. Note that if you didn't add yourself to the recipients, not even you can read your message!
Don't name your file for the things you're talking about or you'll be giving away important information.
You are trying to encrypt a file and gpg tells you that you have an “unusable key”�� Now what?
The problem is that you have a key in your keyring, but it is unusable for what you’re trying to do, which is encrypting a file. So how do we verify this? Here’s what my keys look like, after a few years:
alex@melanobombus:~$ gpg --list-keys alex@gnu.org … pub dsa1024 2006-06-26 [SC] [revoked: 2006-06-26] 26EC7E44221AE68A2D8DA19BE8F3168C89D0FBCC uid [ revoked] Alex Schroeder <alex@emacswiki.org> uid [ revoked] Alex Schroeder <alex@gnu.org> uid [ revoked] Alex Schroeder <kensanata@gmail.org> uid [ revoked] Alex Schroeder <alex.schroeder@openlaw.ch> pub dsa1024 2001-04-12 [SCA] [expired: 2002-04-12] 32BB88D48E068141AAAFB074447A4B1723AAC850 uid [ expired] Alex Schroeder (Kensanata) <alex@gnu.org>
If you see the expiry date approaching for your own key, you should change its expiry date. See below for more.
If you want to encrypt a file for somebody and all you have is revoked and expired keys, then you have to check whether there are updated keys available from the keyservers. See below for more.
If the intended recipient doesn’t use keyservers, you need to let them know some other way… Tricky!
Send the key to your default keyserver:
alex@melanobombus:~$ gpg --send-key C9F3BC47287D8DDEA5576132069E0DD06A9B3268 …
If you're using `keys.openpgp.org` as your key server, you need to upload the key via their web site when you're uploading it for the first time.
Export the key to a file:
gpg --export alex@gnu.org > ~/.gnupg/me.key
Upload this file and confirm the email addresses you want to make public.
Once you have done that, you can send them the key like you usually would. Or use the keyserver explicitly:
alex@melanobombus:~$ gpg --keyserver hkps://keys.openpgp.org \ --send-key C9F3BC47287D8DDEA5576132069E0DD06A9B3268 …
To link your key with social media, software forges and the like, you can add special notes tools can verify. Keyoxide is one of them.
Here's an exampe using GitHub.
1. sign in
2. create a Gist linking to your Keyoxide profile
3. add a notation to your GPG key linking to the Gist (see below)
4. send your key to the keyserver
This is how you add the key. Note that I select my uid before adding the notation. If I don't do this, the notation is added to all of them.
alex@melanobombus ~/.gnupg> gpg --edit-key C9F3BC47287D8DDEA5576132069E0DD06A9B3268 Secret key is available. sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1). Alex Schroeder <alex@alexschroeder.ch> [ unknown] (2) Alex Schroeder <kensanata@gmail.com> [ unknown] (3) Alex Schroeder <alex@gnu.org> gpg> 1 sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1)* Alex Schroeder <alex@alexschroeder.ch> [ unknown] (2) Alex Schroeder <kensanata@gmail.com> [ unknown] (3) Alex Schroeder <alex@gnu.org> gpg> notation Enter the notation: proof@ariadne.id=https://gist.github.com/kensanata/ffb8ecad8d48c9091ad63fb767534340 No notations on user ID "Alex Schroeder <alex@alexschroeder.ch>" Adding notation: proof@ariadne.id=https://gist.github.com/kensanata/ffb8ecad8d48c9091ad63fb767534340 sec rsa3072/069E0DD06A9B3268 created: 2023-02-17 expires: 2025-02-16 usage: SC ssb rsa3072/0FD708D18B49BF59 created: 2023-02-17 expires: 2025-02-16 usage: E [ unknown] (1) Alex Schroeder <kensanata@gmail.com> [ unknown] (2) Alex Schroeder <alex@gnu.org> [ unknown] (3)* Alex Schroeder <alex@alexschroeder.ch> gpg> save
If you added the notation to the wrong uid, select the uids where you want to delete the notation, use the `notation` command and answer `none`.
Upload to keyservers as before.
Now, when you visit the Keyoxide profile, you'll see a check next to the GitHub account.
Other people can now be sure that the public key they're getting is the one the GitHub user used, even if they can't confirm the user's legal name.
Eventually, you’ll want to change the expiry date of your key. Here’s how to extend it by two years.
alex@melanobombus:~$ gpg --edit-key alex@gnu.org gpg (GnuPG) 2.2.12; Copyright (C) 2018 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. Secret key is available. sec rsa3072/AE495BC63253DDE8 created: 2020-05-07 expires: 2022-05-07 usage: SC ssb rsa3072/8400D003187AD626 created: 2020-05-07 expires: 2022-05-07 usage: E [ unknown] (1). Alex Schroeder <alex@gnu.org> gpg> expire Changing expiration time for the primary key. 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) 2y Key expires at Sat 01 Apr 2023 23:15:40 CEST Is this correct? (y/N) y sec rsa3072/AE495BC63253DDE8 created: 2020-05-07 expires: 2023-04-01 usage: SC ssb rsa3072/8400D003187AD626 created: 2020-05-07 expires: 2023-04-01 usage: E [ unknown] (1). Alex Schroeder <alex@gnu.org> gpg> save
Again, if you’re getting “gpg: agent_genkey failed: Permission denied” then your terminal belongs to a different user (most likely because you switched users using `sudo su`) and if you’re sure this is correct, you can run `tmux` and continue inside the tmux environment to work around that.
What if you don't want to extend the key? Let the old one expire, no harm done.
Create a new key pair using `gpg --generate-key` (or `gpg --full-generate-key`).
Upload to keyservers as before.
Todo
Todo