GNU Privacy Guard (GPG)

GNU Privacy Guard (GPG) is a program most notably used for asymmetric cryptography: encryption and signing of information (and the reverse operations: decryption and signature verification). It is often used to:

  • Encrypt and sign the e-mails you send; decrypt the e-mails you receive and verify their signatures.
  • Sign software releases archives, so that your package manager can verify they come from a trusted source (your distro’s maintainers).
  • Sign Git commits to prove that you wrote the code and that your commit was not modified by an adversary.
  • Encrypt (and possibly sign) files before they are transferred over insecure media (flash drives, Slack, etc.) or encrypt sensitive files stored on your machine (such as passwords).
  • Encrypt your backups using your own public key, so that only you can decrypt them. You can also sign them, so that they cannot be silently modified.

Why should you learn this?

Even if you had no use for GPG at all, you would still benefit from learning to use it:

  • You learn to be constructively paranoid. You will likely gain interest in privacy and security in general, which is a good thing.
  • You learn to keep secrets (your secret keys) and how to deal with security incidents (losing your secret keys).
  • Once you set GPG up and become comfortable using it, you will actually enjoy using it. Your privacy bar will raise, and you will encrypt even trivial exchanges—that is a good thing, too.
  • You reserve the right to use end-to-end encryption, and you will defend yourself when people try to strip you of that right. Ever heard of Chat Control?
  • By making your public key available for others to use, you allow them the choice to communicate securely with you, should they feel the need. The choice should be theirs just as it is yours. Related: Google Has Most of My Email Because It Has All of Yours.
  • These skills are useful to have for work, too. In your future job, you will likely be required to sign your Git commits for auditing purposes.

Oh and it is so much fun to bug the NSA!

History: PGP, OpenPGP and GPG

It is useful to understand the difference between PGP (the original program), OpenPGP (the open standard modeled after the original program) and GPG.

In 1991, Pretty Good Privacy (PGP) was created by Phil Zimmerman. Fun fact: in the early 1990’s, encryption technology using keys longer than 40 bits was classified as munitions in the US, and so Zimmerman was investigated for 3 years for “munitions export without a license”:

Zimmermann challenged these regulations in an imaginative way. He published the entire source code of PGP in a hardback book, via MIT Press, which was distributed and sold widely. Anybody wishing to build their own copy of PGP could cut off the covers, separate the pages, and scan them using an OCR program (or conceivably enter it as a type-in program if OCR software was not available), creating a set of source code text files. One could then build the application using the freely available GNU Compiler Collection. PGP would thus be available anywhere in the world. The claimed principle was simple: export of munitions—guns, bombs, planes, and software—was (and remains) restricted; but the export of books is protected by the First Amendment.

(Pretty Good Privacy#Criminal Investigation)

In 1997, OpenPGP was released as a standard to the IETF. Currently, the core of OpenPGP is RFC 4880. The standard defines interoperability for various PGP-inspired programs (including software derived from the original PGP source, which is currently owned by Symantec after a series of acquisitions).

GNU Privacy Guard (GPG), developed by Werner Koch, is a free software implementation of the OpenPGP standard, including some features from the current work-in-progress draft RFC 4880bis-01 by Koch. It is the de facto Linux standard for private communication and digital signatures built on asymmetric cryptography.

The terms PGP, OpenPGP and GPG are used somewhat interchangeably. Don’t be confused by that.

GPG keys

Keys play a central role in GPG. And yet it’s not immediately clear what “a key” is. The term is overloaded in both the documentation and the man pages. A GPG key is much more than just the parameters of an (RSA) key. Let’s tear my public key apart to see what’s inside!

You can download my public key here. It should look something like this:

-----BEGIN PGP PUBLIC KEY BLOCK-----

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
CXAFHIoq4A==
=KyQB
-----END PGP PUBLIC KEY BLOCK-----

What you see is a so-called ASCII-armored (or just armored) version of the key. The actual key is binary data, and therefore not suitable for direct display. The armored version is equivalent, but contains the BEGIN/END markers and the key between them is Base-64 encoded. (In fact, it is Radix-64 encoded: Base-64 encoded with CRC24 at the end (=KyQB).) This makes the key printable and easy to convey in text-oriented media such as the web or the body of an e-mail.

Right now, GPG has no knowledge of the key, but we can still ask it to print some information about the key when we point it to the key file:

~% gpg --show-key dcepelik.gpg
pub   rsa4096 2021-12-18 [C] [expires: 2025-01-21]
      06E1DB4177E5F4437C290557FE352EAA51D04A5A
uid                      David Čepelík <d@dcepelik.cz>
sub   rsa4096 2021-12-18 [E] [expires: 2025-01-21]
sub   ed25519 2023-08-23 [S] [expires: 2025-01-21]

This tells you that:

  • There’s a primary public key in the file (pub)
    • It’s the public part of a 4096-bit RSA key pair (rsa4096)
    • It was generated on December 18, 2021 (2021-12-18)
    • This key may be used for certification only ([C])
    • The key is set to expire on January 21, 2025 (2025-01-21)
    • The fingerprint of the key is 06E1DB4177E5F4437C290557FE352EAA51D04A5A
  • There’s identification of the user who claims the key (user ID, UID; uid) with my name and e-mail address
  • There are two public subkeys (sub):
    • One is used exclusively for encryption ([E], more on this later) and it’s the same type as the primary public key (rsa4096)
    • One is used exclusively for signing ([S], more on this later) and it’s a different type of key (ed25519)

As you can see, “a key” is really several separate objects: the primary public key, the user ID tying the primary public key to my name and e-mail address, and the public subkeys. OpenPGP refers to this format as a Transferable Public Key. It is also called a public (key) certificate.

Let’s state it right now at the beginning: there’s nothing about the key you should trust by default. You shouldn’t trust that it’s mine and you shouldn’t trust the dates. Anybody can generate a key with these exact fields.

Dissecting an OpenPGP public key certificate

In reality, the public certificate is a sequence of binary OpenPGP packets. It is useful to understand the exact structure of the file, as that reveals many important underlying concepts. We can --list-packets to see what is going on:

~% gpg --list-packets --verbose < dcepelik.gpg

The first packet is a public key packet (version 4). Besides the actual RSA public key payload (pkey[0] and pkey[1]), it contains some additional metadata, such as type of key (algo 1 being RSA) and creation time as a UNIX timestamp (created 1639853772). The v4 packet contains no expiration field, it is found in the self-signature (later in the listing).

# off=0 ctb=99 tag=6 hlen=3 plen=525
:public key packet:
  version 4, algo 1, created 1639853772, expires 0
  pkey[0]: FB7C0F5782A5571661A089FD1BDDE687[...]C5F9
  pkey[1]: 010001
  keyid: FE352EAA51D04A5A

The public key packet is followed by a user ID packet, which is just UTF-8 text. Usually it’s an RFC 2822-formatted name-address as is the case here, but that’s not required.

# off=528 ctb=b4 tag=13 hlen=2 plen=31
:user ID packet: "David \xc4\x8cepel\xc3\xadk <d@dcepelik.cz>"

Things get interesting with the signature packet! A signature is a signed hash of some data. In this case, the hash is calculated from the following input:

  • the preceding public key packet,
  • the preceding user ID packet,
  • prefix of the signature packet (from version up to the last hashed subpkt inclusive),
  • (some constants and other fluff).

This hash is signed with the secret key (not shown) which belongs to the public key above, and appended to the signature packet as data. Thus the signature packet is:

# off=561 ctb=89 tag=2 hlen=3 plen=596
:signature packet: algo 1, keyid FE352EAA51D04A5A
  version 4, created 1674415883, md5len 0, sigclass 0x13
  digest algo 8, begin of digest 34 b2
  hashed subpkt 27 len 1 (key flags: 03)
  hashed subpkt 11 len 4 (pref-sym-algos: 9 8 7 2)
  hashed subpkt 21 len 5 (pref-hash-algos: 10 9 8 11 2)
  hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
  hashed subpkt 30 len 1 (features: 01)
  hashed subpkt 23 len 1 (keyserver preferences: 80)
  hashed subpkt 33 len 21 (issuer fpr v4 06E1DB4177E5F4437C290557FE352EAA51D04A5A)
  hashed subpkt 2 len 4 (sig created 2023-01-22)
  hashed subpkt 9 len 4 (key expires after 3y35d0h35m)
  subpkt 16 len 8 (issuer key ID FE352EAA51D04A5A)
  data: 0B3FFDD257DAFC63E831B82E8FF4D8C9[...]D2D

This ties together my RSA key-pair with my name and e-mail address, and also attaches some additional metadata to the key, such as key expiration (hashed subpkt 9).

Because the key-pair is used to sign itself (the secret part is used to sign the public part of the same key-pair), this is a so-called self-signature. A self-signature does not prove that the key belongs to me, but prevents anybody from changing the UID, expiration or the other parameters.

The next packet is a public subkey packet. The structure is the same as for the public key packet above.

# off=1160 ctb=b9 tag=14 hlen=3 plen=525
:public sub key packet:
  version 4, algo 1, created 1639853772, expires 0
  pkey[0]: E6E2287F63A19F8F09E69F91EFFE1AFB[...]CF1
  pkey[1]: 010001
  keyid: 73856FD311842B2F

The last packet is a signature packet again, but a special kind. Whereas the signature above was sigclass 0x13 (“Positive certification of a User ID and Public-Key packet”) where the key was certifying itself, this one is sigclass 0x18 (“Subkey Binding Signature”). The purpose of this signature is to tie this public subkey to the primary public key above, and to control the expiration of the subkey independently of the expiration of the primary key.

# off=1688 ctb=89 tag=2 hlen=3 plen=572
:signature packet: algo 1, keyid FE352EAA51D04A5A
  version 4, created 1674415900, md5len 0, sigclass 0x18
  digest algo 8, begin of digest b9 a2
  hashed subpkt 27 len 1 (key flags: 0C)
  hashed subpkt 33 len 21 (issuer fpr v4 06E1DB4177E5F4437C290557FE352EAA51D04A5A)
  hashed subpkt 2 len 4 (sig created 2023-01-22)
  hashed subpkt 9 len 4 (key expires after 3y35d0h35m)
  subpkt 16 len 8 (issuer key ID FE352EAA51D04A5A)
  data: D844FBB5FE575DB42D0DFA1F303232DE[...]AE0

As you can see, even though I refer to the file as to my “public key”, it is in fact a primary public key with my identity attached to it, another public subkey (tied to the primary key) and a bunch of metadata. Order of the packets matters, for example a signature packet always follows a UID packet, and it always ties together the public key and the most recent preceding UID.

Importing keys

Now that we understand what the key is, we can import the key:

~% gpg --import dcepelik.gpg
gpg: key FE352EAA51D04A5A: public key "David Čepelík <d@dcepelik.cz>" imported
gpg: Total number processed: 1
gpg:               imported: 1

When you import my key, it will become part of your keyring (key collection, in analogy to a physical keyring). To list all public keys in your keyring, you can use gpg -k:

~% gpg -k
[...]
pub   rsa4096 2021-12-18 [SC] [expires: 2025-01-21]
      06E1DB4177E5F4437C290557FE352EAA51D04A5A
uid           [ unknown] David Čepelík <d@dcepelik.cz>
sub   rsa4096 2021-12-18 [E] [expires: 2025-01-21]
[...]

(If you never used GPG before, you will likely only have my key in the keyring.)

You can also list a particular key by providing an additional argument to -k. The search is somewhat “fuzzy”: you can identify the key with a part of the user ID, a short key ID (or “0xshort”), a long key (or “0xlong”) or a fingerprint. The following are all equivalent:

~% gpg -k 06E1DB4177E5F4437C290557FE352EAA51D04A5A             # fingerprint
~% gpg -k '06E1 DB41 77E5 F443 7C29  0557 FE35 2EAA 51D0 4A5A' # fingerprint with spaces
~% gpg -k FE352EAA51D04A5A                                     # long key ID
~% gpg -k 51D04A5A                                             # (short) key ID
~% gpg -k 0xFE352EAA51D04A5A                                   # "0xlong"
~% gpg -k 0x51D04A5A                                           # "0xshort"
~% gpg -k "David Čepelík <d@dcepelik.cz>"                      # UID
~% gpg -k dcepelik                                             # UID substring

All of these will provide the familiar output, but will only list the matching keys:

~% gpg -k 06E1DB4177E5F4437C290557FE352EAA51D04A5A 
pub   rsa4096 2021-12-18 [SC] [expires: 2025-01-21]
      06E1DB4177E5F4437C290557FE352EAA51D04A5A
uid           [ unknown] David Čepelík <d@dcepelik.cz>
sub   rsa4096 2021-12-18 [E] [expires: 2025-01-21]

Do not use the short (32-bit) key IDs to identify keys. For v4 keys which we are using, the key ID is the low 32 bits of the fingerprint. Not only can collisions happen naturally, it is also very easy to generate a key with a given short key ID on purpose. It is then easy to accidentally use the wrong key (either by mistake or because an attacker set you up). Don’t use long keys either. Just use the entire fingerprint—often, you copy/paste it anyway. Refer to evil32.com for details.

Validating GPG keys

Making sure that the key belongs to the real person you wish to communicate with is the most difficult part. You cannot be certain the above key is actually mine! GPG tries to protect you when you try to use a potentially invalid key—for example, when you try to encrypt a message using the just imported public key:

~% gpg -ea -r d@dcepelik.cz
gpg: 73856FD311842B2F: There is no assurance this key belongs to the named user

sub  rsa4096/73856FD311842B2F 2021-12-18 David Čepelík <d@dcepelik.cz>
 Primary key fingerprint: 06E1 DB41 77E5 F443 7C29  0557 FE35 2EAA 51D0 4A5A
      Subkey fingerprint: 40FA CFBE 5D55 F75B 58FF  B942 7385 6FD3 1184 2B2F

It is NOT certain that the key belongs to the person named
in the user ID.  If you *really* know what you are doing,
you may answer the next question with yes.

Use this key anyway? (y/N)

Never assume blindly that a key is valid, that would fundamentally undermine all security. In other words, unless we have met in person and exchanged keys, there is no way for us to communicate securely: I do not know your public key, and you do not know mine. Please note that there’s nothing technically preventing you from encrypting a message using my (possibly phony) public key. It would work just fine. But it would also be really bad to share something sensitive this way.

The first lecture is a good opportunity to validate our keys. To validate keys, we need to:

  • Prove our identity to one another. This can range anywhere from using a reliable witness (“sure, this is Pepa, known him since KG”) to a thorough check of several government-issued identification documents and a DNA swab.
  • Exchange key fingerprints. The keys can be obtained through an insecure channel (you can download my key from my website), providing their fingerprints are verified. Since the fingerprint is the output of a cryptographically-secure hash function, it is extremely unlikely anybody would be able to generate a fake GPG key with a colliding fingerprint.

Once you have checked my identity and made sure that the fingerprint of the public key you imported is exactly the one I gave you, you can tell GPG that the key is mine, squelching the warning we saw earlier. An endorsing signature is used to capture that one user (you) trusts that a certain key (my primary public key) belongs to a certain person (me). To produce such a signature, you can --lsign my key:

~% gpg --lsign dcepelik

pub  rsa4096/FE352EAA51D04A5A
     created: 2021-12-18  expires: 2025-01-21  usage: SC
     trust: unknown       validity: unknown
sub  rsa4096/73856FD311842B2F
     created: 2021-12-18  expires: 2025-01-21  usage: E
[ unknown] (1). David Čepelík <d@dcepelik.cz>

gpg: no default secret key: No secret key

Key not changed so no update needed.

To sign anything, you need your secret key, and here GPG tells you that you do not have any. (The above command should work fine if you already use GPG and have your key pair ready.) That is no surprise if you do not have a GPG key at all. Let’s fix that!

Generating a testing GPG key

A basic GPG key can be created with --gen-key. GPG will only ask you for your name and e-mail address and defaults will be used for everything else. This key won’t be ideal, but it is good enough for testing. We will delete it later:

~% gpg --gen-key
gpg (GnuPG) 2.2.35; Copyright (C) 2022 g10 Code GmbH
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: David Čepelík (testing only)
Email address: d+testing@dcepelik.cz
You are using the 'utf-8' character set.
You selected this USER-ID:
    "David Čepelík (testing only) <d+testing@dcepelik.cz>"

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.
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: /home/d/.gnupg/trustdb.gpg: trustdb created
gpg: directory '/home/d/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/d/.gnupg/openpgp-revocs.d/E5A133DB072600E762CA2A470901029864907CAC.rev'
public and secret key created and signed.

pub   rsa3072 2023-04-03 [SC] [expires: 2025-04-02]
      E5A133DB072600E762CA2A470901029864907CAC
uid                      David Čepelík (testing only) <d+testing@dcepelik.cz>
sub   rsa3072 2023-04-03 [E] [expires: 2025-04-02]

Side note: As you can see, I made sure that the UID contains the string testing. Not only will this make it easier to delete the correct key when the time comes: if I forget to delete the key, nobody will use it for anything serious by accident. This is a good habit to acquire that goes beyond GPG keys—anything not meant for production (real) usage should be marked as such.

Locally signing keys

As this is just a testing key, feel free to use it to sign my key without validating it first:

/ # gpg --lsign 06E1DB4177E5F4437C290557FE352EAA51D04A5A

pub  rsa4096/FE352EAA51D04A5A
     created: 2021-12-18  expires: 2025-01-21  usage: SC
     trust: unknown       validity: unknown
sub  rsa4096/73856FD311842B2F
     created: 2021-12-18  expires: 2025-01-21  usage: E
[ unknown] (1). David Čepelík <d@dcepelik.cz>


pub  rsa4096/FE352EAA51D04A5A
     created: 2021-12-18  expires: 2025-01-21  usage: SC
     trust: unknown       validity: unknown
 Primary key fingerprint: 06E1 DB41 77E5 F443 7C29  0557 FE35 2EAA 51D0 4A5A

     David Čepelík <d@dcepelik.cz>

This key is due to expire on 2025-01-21.
Are you sure that you want to sign this key with your
key "David Čepelík (testing) <d+testing@dcepelik.cz>" (3DA646B7CA6ADF5E)

The signature will be marked as non-exportable.

Really sign? (y/N) y

GPG now considers my key valid and it can be used without warnings:

/ # gpg -ae -r d@dcepelik.cz
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   1  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
gpg: next trustdb check due at 2025-01-21
Hello World!
-----BEGIN PGP MESSAGE-----

hQIMA3OFb9MRhCsvARAAmBwO0ffELg9ClFk7JJSUwBs3eEc7KvSSg/H9qjnxoLOI
fTwprrDphut1q9yfSHhIon+9qpAXuX/fKJ1k3Vgd74SXuLAzHHaj3Rnjz0PqUKJt
xqRGnN+aUsJ2b24TF5vZ+wOXg5qc4LSZnNC2Vf1LMfneoTNDRpPm4g/48KK47PNk
q5BebGC2+Ht4Q34It00fShShDVXH291gD26OrPycg7DyaIWdpwsldsSuFbxy37MV
WP3fQEvUJT/hnpVot1hlleHmDpz7KU8Gq5cVM0CUyzLeUnpIoIJApyA+l4V93+OW
DSaNyI1m7rIC70bLdoKnCp7q/EDku6sncjuaIDQZdV+aYi1OOi6kGxlEwBnfXFJ0
CGTXd3c/PGqf0O1q0k9oodrnqyqwVPr9uAkC5y3nllNxmbupAnO/YcxU4Jd0niYY
r9VlvH3UzZKuq6lqlLRIsorWKyHVX10hUFwYJsBIGKKf843Li/psWhFxQ2diaWBO
YhbvrEDVvG9SUcBgJ081JePjKLf1dp/EZny+1bJjN6WYuzUfmoQ6vTC4rWiQ6w9V
FZ7exCywyd5R/fmXU6B11RDaH4Po4XkrStz+rbFF9ynmWCCBUp1dCnEUmltUXxoO
sB40rFphsCMvmfWP47IsOus9RYBmFRIW8U0TIzkEJZXC3c9QXjRQtEqswGnPN1XS
SAEfuNSamG1r6paqhruH4THug0vOeOkn1IFeB4hCA9ZyCNBn0dCyZX23tnEvFU4l
Z7oFksZViN4j+Cgh8V5MAsQKiuOkxek16g==
=XzY/
-----END PGP MESSAGE-----

(Hint: you need to hit ^D after Hello World! to close gpg’s stdin.)

If you wondered what the l in --lsign stands for, it is “locally”. This is also why PGP told you “The signature will be marked as non-exportable.” A local signature is only kept on your machine, while an exportable signature can be exported and shared with others to let others know that you trust the key.

Revoking keys

There are many reasons why a GPG key may fall out of favor:

  • You make a stupid mistake and you upload the secret key, rather than the public key, to your website. Even if it has only been there for a minute, you can never know whether somebody downloaded the key, and you must consider it compromised (even if the key is encrypted).
  • You lose the physical security token (smart card, YubiKey, etc.) holding the secret key. Even if the device is designed to never reveal the key, you can never be sure, and you must consider the key compromised.
  • The key pair is no longer secure. This happens all the time: as computational power grows, older keys become too weak. The key needs to be superseded with a stronger key and the old key must be considered no longer secure.
  • You delete the secret part of the key by accident, or you forget the passphrase, making the key pair useless.

In each of these cases, you need to let others know that you no longer use the key (because it was compromised or replaced) and that it should not be used in further communication (to encrypt messages sent to you, nor to verify signatures from you). This is called key revocation.

To revoke a key, you need to prove the possession of the secret part of the key. Once you lose the secret key, you can no longer do that. It is therefore very important to generate a revocation certificate right away when you generate the key pair. For example, you could:

~% gpg --gen-revoke 7D3F87540F4FE88218FAE794852037B7B1300295 > revoke.gpg
Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
(Probably you want to select 1 here)
Your decision? 0
Enter an optional description; end it with an empty line:
> This key was never really used, it is just a test.
>
Reason for revocation: Key is no longer used
This testing key was never really used, it is just a test.
Is this okay? (y/N) y
ASCII armored output forced.
Revocation certificate created.

Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable.  But have some caution:  The print system of
your machine might store the data and make it available to others!

This produces a revocation certificate. You should print the certificate and store it somewhere safe. Do not store it with the backup of your secret key, since you do not want to lose both at the same time. You can store the revocation certificate with somebody you trust. (You can also keep it in a safety deposit box at your local bank where you keep all your encrypted backups.) It may be a good idea to print a QR code version of the certificate instead, since QR codes are much more reliably decoded than general OCR:

qrencode -o revoke.png < revoke.gpg

A dummy revocation certificate encoded as a QR code.

If you ever lose your key, take a photo of the QR code, decode it and import the revocation certificate into your keyring:

~% zbarimg -q --raw revoke-test.png > revoke-decoded.gpg
~% gpg --import revoke-decoded.gpg
gpg: key 852037B7B1300295: "Test <test@example.com>" revocation certificate imported
gpg: Total number processed: 1
gpg:    new key revocations: 1
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid: 280  signed:   2  trust: 0-, 0q, 0n, 0m, 0f, 280u
gpg: depth: 1  valid:   2  signed:   0  trust: 2-, 0q, 0n, 0m, 0f, 0u
gpg: next trustdb check due at 2023-04-22
~% gpg -k 7D3F87540F4FE88218FAE794852037B7B1300295
pub   ed25519 2023-03-29 [SC] [revoked: 2023-03-29]
      7D3F87540F4FE88218FAE794852037B7B1300295
uid           [ revoked] Test <test@example.com>

The revocation certificate is in fact a “revocation self-signature” and it attaches to your key. You can then publish the updated key. The signature proves that the legitimate owner of the public key asked for the key to be no longer used.

Backing up the secret keys

Being able to revoke a lost key is essential, but it is better not to lose it in the first place. It is therefore recommended that you back up your secret key:

~% gpg --export-secret-key --armor 7D3F87540F4FE88218FAE794852037B7B1300295
-----BEGIN PGP PRIVATE KEY BLOCK-----

lIYEZCPsFRYJKwYBBAHaRw8BAQdAY/ONv9yVzeLkaPrB9zQ/RHjwJhd+T0camHTg
80rw5Xv+BwMC0YslwyGi/tHzYiNbzySzBTSjyVP6omnxOz+lCJvw7fn9e+3AlSLD
9TmFGRfyjw6JLJPegIU2MEjr0/LYM3RAZsQuI6k5EeHyrpU7lUABLIibBCAWCgBD
FiEEfT+HVA9P6IIY+ueUhSA3t7EwApUFAmQj7JklHQNUaGlzIHRlc3Rpbmcga2V5
IGlzIG5vIGxvbmdlciB1c2VkLgAKCRCFIDe3sTAClaDvAQDJ0i0bSTvHgDMXTkBb
gYaob8jICkcySH8keBRgD5P2iwD/a3Lhry0gIfsL2JvUEdgLLoAP385tahbn775z
q7UdzQW0F1Rlc3QgPHRlc3RAZXhhbXBsZS5jb20+iJkEExYKAEEWIQR9P4dUD0/o
ghj655SFIDe3sTAClQUCZCPsFQIbAwUJA8JnAAULCQgHAgIiAgYVCgkICwIEFgID
AQIeBwIXgAAKCRCFIDe3sTACleNQAP4jsQiqPWv9RKLn6FyddRlRfB+2iyhWyvLf
WucS6ApBTQD8DqjyxeCkVYEC/Pbgu212+TbvSw8lKZ4tHcO9vrzVfAKciwRkI+wV
EgorBgEEAZdVAQUBAQdA+1tUHlaIkx9yONczkJKa3/oZIZXNNPexcD4mz/UrkTED
AQgH/gcDArLqez0ZXoEv83HctD0i3vJMFAy7XMPi3hLT56DbGF3+ZCZ0TLtmeX+k
Z3zGfXDU+2X/UdIYbWGpYzBSKMu7aww2w0J4tzkR8fRzEx/MA2SIfgQYFgoAJhYh
BH0/h1QPT+iCGPrnlIUgN7exMAKVBQJkI+wVAhsMBQkDwmcAAAoJEIUgN7exMAKV
m8sA/1HaTuSeWTczXaqSZVOwetPc3MdAxRzeXZ6kYtws2QRrAQC4PrvJsSfKVxSN
Yx1C833WRph+2meuaF9CxT+Z3V/vDQ==
=aiuS
-----END PGP PRIVATE KEY BLOCK-----

The above command exports your primary secret key. If the key is passphrase-protected (it should be!), the exported version will be as well.

You can then once again qrencode this and print it out. Be wary of the printer and if you ever take a picture of the key, use a device you trust (scanner, camera). Does your smartphone backup all your photos into the cloud? Do you want your secret key to be stored there? The safety of the backup is the upper bound on the safety of your key.

Alternatively, you can use a LUKS-encrypted flash drive, no printing involved. Obviously, you shouldn’t use that drive for anything else.

Deleting keys

An interesting thing about GPG is that it considers public and secret keys distinct objects. It knows, obviously, that a certain public key belongs to a certain secret key. But many commands act on the parts individually. For example, -k would only list the public keys:

~% gpg -k
/home/d/.gnupg/pubring.kbx
------------------------
pub   rsa4096 2021-12-18 [SC] [expires: 2025-01-21]
      06E1DB4177E5F4437C290557FE352EAA51D04A5A
uid           [  full  ] David Čepelík <d@dcepelik.cz>
sub   rsa4096 2021-12-18 [E] [expires: 2025-01-21]

pub   rsa3072 2023-04-05 [SC] [expires: 2025-04-04]
      1EF42C617262F3A4F526136E3DA646B7CA6ADF5E
uid           [ultimate] David Čepelík (testing) <d+testing@dcepelik.cz>
sub   rsa3072 2023-04-05 [E] [expires: 2025-04-04]

While -K only lists the secret keys:

~% gpg -K
/home/d/.gnupg/pubring.kbx
------------------------
sec   rsa3072 2023-04-05 [SC] [expires: 2025-04-04]
      1EF42C617262F3A4F526136E3DA646B7CA6ADF5E
uid           [ultimate] David Čepelík (testing) <d+testing@dcepelik.cz>
ssb   rsa3072 2023-04-05 [E] [expires: 2025-04-04]

In the listings above, one of the keys is the public key we imported earlier and the other is our testing key. A secret key (sec) and secret subkey (ssb) is only present for the testing key. You can tell this by the fingerprints.

Let’s delete the testing key since we no longer need it. You need to delete the secret key first with --delete-secret-key.

Warning: if you used GPG in the past, your keyring may contain your secret key. Please make sure you are actually deleting the testing key.

~% gpg --delete-secret-key 1EF42C617262F3A4F526136E3DA646B7CA6ADF5E
gpg (GnuPG) 2.2.35; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


sec  rsa3072/3DA646B7CA6ADF5E 2023-04-05 David Čepelík (testing) <d+testing@dcepelik.cz>

Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y

GPG prompts multiple times before it smashes your secret keys. Once the secret key is gone, you can delete the public key with --delete-key as well:

~% gpg --delete-key 1EF42C617262F3A4F526136E3DA646B7CA6ADF5E
gpg (GnuPG) 2.2.35; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


pub  rsa3072/3DA646B7CA6ADF5E 2023-04-05 David Čepelík (testing) <d+testing@dcepelik.cz>

Delete this key from the keyring? (y/N) y

Because the testing key was used to locally sign my public key, GPG no longer considers my public key valid. If you try to encrypt something using my public key, you will see the now-familiar warning again.

(Note: also there’s --delete-secret-and-public-keys.)

Primary key and subkeys: C+S+E+A

Roughly, GPG can perform three operations with keys: use them for encryption (E), signing (S) and certification (signing of other keys, C). Technically, it is perfectly possible to have a single RSA key pair and use it for everything (C+S+E+A). In practice, you will usually have a separate encryption subkey (as GPG creates one by default) and ideally, you will also have a separate signing subkey.

First and foremost, a subkey is a key like any other. Though it may differ from the primary key in important ways (for example, the primary key may be 4096-bit RSA, while the subkey is ED25519), there is no inherent difference between them. So why does GPG treat the keys differently? It all boils down to security and convenience.

When you have signed my public key above, you have signed my primary public key, but you have used the public encryption subkey—perhaps without realizing it—to encrypt a message. Because your GPG considers my primary public key valid, and because the encryption subkey is signed by my primary secret key (recall the Subkey Binding Signature from the packet listing above), GPG knows the subkey is valid, too.

Using subkeys makes key management much simpler: you can add and revoke subkeys as you see fit (or as circumstances require) and it does not affect your primary key in any way. All endorsing signatures remain valid, and people still recognize your primary key and any (new) subkeys as valid. With subkeys, you can:

  • use a different key type for each use-case,
  • only revoke the encryption/signing subkey without affecting other keys,
  • only use the primary key for certification (C) and administrative tasks, allowing you to keep the primary private key offline.

Especially the last point—keeping your primary private key offline—is of great importance. As Debian Wiki puts it:

You should keep your private primary key very, very safe. However, keeping all your keys extremely safe is inconvenient: every time you need to sign a new package upload, you need to copy the packages onto suitable portable media, go into your sub-basement, prove to the armed guards that you’re you by using several methods of biometric and other identification, go through a deadly maze, feed the guard dogs the right kind of meat, and then finally open the safe, get out the signing laptop, and sign the packages. Then do the reverse to get back up to your Internet connection for uploading the packages.

Subkeys make this easier: you already have an automatically created encryption subkey and you create another subkey for signing, and you keep those on your main computer. […] Everyone else will use [the subkeys] instead of the primary keys for encrypting messages or verifying your message signatures. Likewise, you will use the subkeys for decrypting and signing messages.

Having a separate key for signing and another for encryption means that the responsibilities of your primary key pair are significantly reduced. You will normally only need the private primary key when you:

  • sign other keys (C) or revoke prior signatures,
  • add or revoke a subkey,
  • add a new UID to your key or
  • change the expiration date of the primary key or any of the subkeys.

There’s another very good reason why you would want to use separate subkeys for distinct purposes, and that has to do with the underlying cryptography. Using the same key for encryption and signing opens up an entire class of attacks, which in theory could work as follows. I can ask you to sign something with your S+E key. If I choose the signed clear-text cleverly enough, I might be able to use your signature to decrypt something that was encrypted with your public key. This attack isn’t possible at all when your S and E keys are distinct mathematical objects.

Generating a proper GPG key

A “better” GPG key can be generated with gpg --full-gen-key --expert. This allows you to create a certify-only primary key and add the encryption and signing subkeys later with --edit-key. You can also use --quick-gen-key and --quick-add-key to create the primary key and add the subkeys, respectively. Unlike --full-gen-key, this is a non-interactive process.

Here are some tips to generate a well-rounded GPG key for everyday use:

  • For the primary key, use rsa4096. At least in theory it provides the highest security level of all algorithms currently available in GPG (discussion).
  • Create an encryption subkey. For maximum security, use rsa4096. cv25519 (curve 25519) is comparable to rsa3072. Its main advantage is a very short public key, though for encryption this should not be a priority.
  • Create a signing subkey. ed25519 (twisted Edwards curve 25519) will provide fast signing and signature verification, short signatures (64 bytes compressed) and short public key (32 bytes compressed).

To keep your key safe:

  • Protect your secret keys with a strong password or a passphrase. This means that secret keys are stored in their symmetrically encrypted form. If you lose the encrypted version, the password will act as the last line of defense: to use your key, an attacker first needs to crack the password (if at all possible), giving you time to generate a new key, revoke the old one and notify others.
  • Backup your primary secret key.
  • Generate a revocation certificate.
  • Keep your secret key offline, as this adds a lot of security. You can export the secret key with --export-secret-key and store it on a USB flash drive. (The drive itself should be encrypted.) You can then delete the secret key from your machine with --delete-secret-key.
  • Do not keep the secret key and the revocation certificate at the same place to avoid losing both at the same time.

Once you are happy with your setup, upload your key to the keyservers.

Using keyservers

Keyservers make key exchange easy: you upload your public GPG key(s) to a keyserver and others can fetch it. GPG comes with built-in keyserver support:

~% gpg --recv-key 06E1DB4177E5F4437C290557FE352EAA51D04A5A
gpg: key FE352EAA51D04A5A: public key "David Čepelík <d@dcepelik.cz>" imported
gpg: Total number processed: 1
gpg:               imported: 1

Please note that (most) keyservers do not perform any validation of UIDs, they are just exchange points. Anybody can send any number of keys with arbitrary name and e-mail address (such as yours). When receiving keys, make sure that the fingerprint is valid; otherwise you may end up fetching an attacker’s key instead. Once you know the fingerprint of a valid key, it’s best to fetch the key by its fingerprint (as above).

You can also --search the key by UID:

~% gpg --search d@dcepelik.cz
gpg: data source: https://162.213.33.8:443
(1)     David Čepelík <d@dcepelik.cz>
          4096 bit RSA key FE352EAA51D04A5A, created: 2021-12-18
(2)     David Čepelík <d@dcepelik.cz>
          2048 bit RSA key AB9E4B176B3D739D, created: 2018-09-08
Keys 1-2 of 2 for "d@dcepelik.cz".  Enter number(s), N)ext, or Q)uit >

Again: it’s absolutely essential that you check the fingerprint of the key afterwards.

As of April 2023, GPG uses hkps://keyserver.ubuntu.com as the default keyserver (Ubuntu keyserver accessed using HTTP Keyserver Protocol over TLS). This can be overridden for a single query with the --keyserver option:

~% gpg --keyserver hkps://keys.openpgp.org --recv-key C466A56CADA981F4297D20C31F3D0761D9B65F0B
gpg: key 1F3D0761D9B65F0B: public key "Martin Mares <mj@ucw.cz>" imported
gpg: Total number processed: 1
gpg:               imported: 1

You can also set your preferred keyserver in ~/.gnupg/gpg.conf. Consider using hkps://keys.openpgp.org, as it is the most privacy-friendly server:

~% cat ~/.gnupg/gpg.conf
keyserver hkps://keys.openpgp.org

Many keyservers do not support any form of record deletion: whatever you send to the keyserver will stay there forever (details). It is possible to update certain parts of your key (for example, self-signatures) and send new GPG packets (new subkeys, revocation self-signatures, etc.).

Think twice before sending your keys. Make sure you have a revocation certificate in case you needed to burn the key. Then send your key with --send-key:

~% ~% gpg --send-key 06E1DB4177E5F4437C290557FE352EAA51D04A5A
gpg: sending key FE352EAA51D04A5A to hkps://keys.openpgp.org

Many users upload their key to a keyserver and also publish a copy on their website (example).

Keeping your public keyring up to date

Run --recv-keys periodically (don’t abuse the keyserver too much):

~% gpg --recv-keys

Please note that you potentially give away sensitive data, as the keyserver can correlate the update traffic with you. In effect, they know the fingerprints of all keys in your keyring. This may or may not be a problem.

If you’re paranoid, you can fetch the keys one by one, spread out in time, each over a different Tor circuit.

Third-party signatures and the Web of Trust

You can sign a key in your keyring with --sign-key. This will create an exportable signature. An exportable signature will become part of the public certificate bundle created with --export, and can be published with the key. Over time, a key may collect several endorsing signatures from others keys, which are then published with the key and get imported into other users' keyrings.

Suppose that Alice and Eve never met. But Alice knows Bob, and Bob knows Eve. When Alice fetches Eve’s key from the keyserver, she finds out that Bob has signed Eve’s key. If Alice trusts Bob’s judgment in signing Eve’s key, she may consider Eve’s key valid.

On a global scale, these trust relationships form a graph called the Web of Trust, where the vertices are the keys (representing the people) and the oriented edges are the signatures (representing trust in the key’s validity). As the graph grows denser, due to the small world phenomenon, a rather small number of people (~6) is needed on average to connect any two people on the planet. A little trust goes a long way.

GPG has a local setting called trust for each key in your keyring. It controls how much you trust the key’s owner when it comes to signing other keys. There are four levels of trust: unknown (default), none, marginal and full (refer to the GPG handbook, chapter 3 for explanation of the levels). GPG by default considers valid any key which earned one full trust signature or three marginal trust signatures (subject to some additional conditions; the constants can be tweaked). For example, I trust Tomáš Volf fully when it comes to signing other keys ([ full ]):

~% gpg -k wolf@wolfsden.cz
pub   rsa4096 2021-04-17 [SC]
      92C05C30F5E5A772B04D459150248D1A100E6AE6
uid           [  full  ] Gray Wolf <wolf@wolfsden.cz>
uid           [  full  ] Tomas Volf <volf.tomas@wolfsden.cz>
sub   rsa4096 2021-04-17 [S] [expires: 2025-02-19]
sub   rsa4096 2021-04-17 [E] [expires: 2025-02-19]

As a result, any signature from Tomáš is as good as my own. You can use the trust command in the --edit-key menu to change a key’s trust level.

Given time, the Web of Trust would grow so large and dense that any two people on the planet would be able to communicate securely even without prior key exchange. That was the idea, anyway.

2019: Death of the Web of Trust and the SKS keyserver pool

When PGP originated in the 1990s, the Internet was still in its early days, but government spying was already in full swing. What if the NSA forced a keyserver to replace a key with another one?

Instead of a single keyserver, a decentralized network of independently operated keyservers was built, which were kept synchronized using the Synchronizing Key Server (SKS) protocol. The network was tiered, with many servers operated by volunteers. Collectively, these servers were known as the SKS keyserver pool and available at sks-keyservers.net.

The government still could force any single keyserver operator to delete or alter records, but the error would be immediately detected and not propagated further into the pool. Deletion of records was unsupported as a consequence of this design choice. Anybody could list their key, or attach their endorsing signatures to other keys, to contribute to the Web of Trust.

Twenty-some years later, the Internet is a very different place:

  • Keyservers can trivially be attacked by uploading lots of bogus keys.
  • Keys can be attacked by having lots of bogus signatures attached to them.
  • Once uploaded, keys and signatures cannot be deleted (by design).
  • (Also, names and e-mail addresses are classified as personally identifiable information (PII) by GDPR, and users are entitled to have their data deleted.)

In 2019 the magnitude of the problem became apparent when someone spammed the keys of two prominent OpenPGP community members with hundreds of thousands of bogus signatures. Anybody who fetched those keys from the keyservers saw their GPG installation grind to a complete halt. The full postmortem written by one of the victims of the signature spamming attack is worth reading.

To mitigate this attack vector, GPG since version 2.2.17 defaults to not importing any 3rd-party signatures (--keyserver-options self-sigs-only) when fetching data from the keyservers. From gpg(1):

self-sigs-only: Accept only self-signatures while importing a key. All other key signatures are skipped at an early import stage. This option can be used with keyserver-options to mitigate attempts to flood a key with bogus signatures from a keyserver. The drawback is that all other valid key signatures, as required by the Web of Trust are also not imported. […]

This deals a significant blow to the Web of Trust, which relies on the keyservers to distribute 3rd-party endorsing signatures. And even before this came to be, the Web of Trust suffered from other issues. As of this writing, it is unclear how 3rd-party endorsing signatures should be handled, or what should replace the Web of Trust. There is however a draft for Abuse-Resistant OpenPGP Keystores by D. K. Gillmor.

Also as a consequence of the attack and regulatory pressure, the SKS keyserver pool fell apart. The community provisioned a new keyserver at keys.openpgp.org which does not synchronize, requires a consent to publish the PII (e-mail opt-in), allows record deletion (after e-mail confirmation) and does not store any non-essential OpenPGP packets, including 3rd-party signatures. You might as well make it your default keyserver and publish your key there—it’s privacy friendly and reliable.

Using gpg-agent

Thanks to gpg-agent (man, cf. ssh-agent), you do not have to type the passphrase (“unlock the key”) each time you need to access the encrypted secret (private) part of the key. Once you unlock the key, gpg-agent will hold it decrypted in memory for you (for some configured time). When you issue a gpg command which needs the secret part of the key, gpg-agent will provide it to the command.

Missing bits

Some bits are still missing and will be added in future revisions of this document. Let us know if you want to contribute any of these:

  • Authentication subkeys
  • Imperfect (and often good enough) alternatives to the WoT: TOFU (with corroboration via orthogonal channels; WKD), CAs, Keybase; relying on the OS keyring
  • Key signing party outline (not too relevant anymore, but still useful to know)
  • (*) How (RSA) keys are generated
  • Encrypting your mail
  • Signing Git commits
  • (*) Using smart cards (e.g. YubiKey)
  • (*) Post-quantum security (is not)

Acknowledgments

  • Ondra Hrubý did a proofread of an early version of this text
  • Tomáš Volf provided extensive feedback and several corrections
  • Jirka Kalvoda and Dominik Stejskal provided several corrections

Thanks!