pgp

Background

Exactly 7 years ago I wrote this article showing how to use PGP to secure your emails. Today I decide to rewrite it, to make it useful in 2023, and to instruct people who might want to encrypt their communication with me but don't know how.

This is a complete rewrite, expect more than just PGP in emails. I will use my own key as an example, feel free to test what you learned with it.

Install GnuPG

GnuPG is the most commonly used PGP software, you can install it on your system easily:

Linux

Typically gpg is preinstalled since most package managers depend on it, make sure your version is up to date (should be 2.4):

~
❯ gpg --version
gpg (GnuPG) 2.4.0
libgcrypt 1.10.2-unknown
Copyright (C) 2021 Free Software Foundation, Inc.
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /home/jm33/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

If your gpg is not up to date, follow your distro's documentation to upgrade

Windows

Install Gpg4win

MacOS

Mac GPG

How to Use GPG

Generate Your Key

First of all you need your own keypair:

~
❯ gpg --full-generate-key
gpg (GnuPG) 2.4.0; Copyright (C) 2021 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
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection? 9
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection? 1
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) 0
Key does not expire at all
Is this correct? (y/N) y

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

Real name: jm33-test
Email address: jm33.me@gmail.com
Comment: Demo
You selected this USER-ID:
    "jm33-test (Demo) <jm33.me@gmail.com>"

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

pub   ed25519 2023-06-04 [SC]
      00416191C169BCF410712173E9FAF7E43E456E68
uid                      jm33-test (Demo) <jm33.me@gmail.com>
sub   cv25519 2023-06-04 [E]

In this example, I follow all default options, and I suggest you do the same, except for expiration, if you want it to expire after some time to update it regularly, feel free to do so.

Perhaps you have already noticed 00416191C169BCF410712173E9FAF7E43E456E68, this is the fingerprint of your key, you can view it with the following command

❯ gpg --fingerprint 00416191C169BCF410712173E9FAF7E43E456E68
pub   ed25519 2023-06-04 [SC]
      0041 6191 C169 BCF4 1071  2173 E9FA F7E4 3E45 6E68
uid           [ultimate] jm33-test (Demo) <jm33.me@gmail.com>
sub   cv25519 2023-06-04 [E]

This fingerprint is used to identify your unique key, when verifying signed data, look for fingerprints like this and compare it with the one you got in your contacts list or published by its owner (for example my key fingerprint is in my GitHub/Twitter bio)

Publish Your Key

Did I mention it's a keypair?

It means you get two keys, one public and one secret. Like what their names imply, you will be publishing your public key on a keyserver.

In simple words, the public key is used to encrypt data, for the coresponding secret key to decrypt, which is why you need to keep the secret key "secret".

To make it more complex, when you encrypt data with a public key (of the recipient), what happens under the hood is:

  • A random and strong key is generated and used to encrypt the data with a symmetric cipher (usually AES)
  • The AES key is then encrypted with the public key, using the cipher the public key accepts (when the keypair was created, ECC in this example)
  • Now the encrypted AES key is attached to the encrypted data

When decrypting

  • The secret key is used to decrypt the AES key
  • Then AES key is used to decrypt the data

Okay, now you understand how the keypair works, let's upload your key to a keyserver

gpg --keyserver hkps://keyserver.ubuntu.com --send-key 00416191C169BCF410712173E9FAF7E43E456E68

Then you can tell people to download your public key using the following command

gpg --keyserver hkps://keyserver.ubuntu.com --recv-key 00416191C169BCF410712173E9FAF7E43E456E68

Obtain Public Key of Your Recipient

Just like the command above, if the key is available on keyserver and can be trusted, use --recv-key option to download and import the public key of your recipient.

Or request it directly from your recipient, in many cases they also publish it on their own website, like I do

Clear Sign: Sign clear-text messages without encryption

The GPG page on this website shows a PGP signed message, which can be verified if you have my public key.

How is it created?

gpg --clear-sign -u 9CF295D2 cleartext.txt

-u is used to specify which secret key to use for signing the message, it's useful when you have multiple keys in your system. You need to replace 9CF295D2 with your own key fingerprint (it can be full-length or shortened like this one).

How is it verified?

❯ gpg --decrypt signed_msg.txt
You can fetch my latest public key from keyserver, please prefer this key when encrypting emails:

    gpg --keyserver hkps://keyserver.ubuntu.com --recv-key 410C99D49CF295D2

To verify:

    gpg --fingerprint 410C99D49CF295D2

You should see:

    pub   ed25519 2023-05-21 [SC]
          7396 73C5 A6C3 797A 1298  6FFF 410C 99D4 9CF2 95D2
    uid           [  full  ] jm33@jm33.me <jm33@jm33.me>
    sub   cv25519 2023-05-21 [E]


The following keys are also in use:

    FF8EDA690269AD60674F1CF1D73C51683A5DBF07
    A914BB005F8A42F36C2997594876EC6D60BDB1DF

You can fetch them using gpg with the same command above, just replace the key ID

To sign my key:

    gpg --sign-key 410C99D49CF295D2

To upload the signed key to keyserver:

    gpg --keyserver hkps://keyserver.ubuntu.com --send-key 410C99D49CF295D2

Thanks a lot in advance if you decide to sign my key!
gpg: Signature made Wed 31 May 2023 11:43:18 AM CST
gpg:                using EDDSA key 739673C5A6C3797A12986FFF410C99D49CF295D2
gpg: Good signature from "jm33@jm33.me <jm33@jm33.me>" [ultimate]

The last line gpg: Good signature from "jm33@jm33.me <jm33@jm33.me>" [ultimate] proves that it's a good signature, and if you look further, the signature is made by 739673C5A6C3797A12986FFF410C99D49CF295D2, which is my own key, so now you can know for sure that this message is signed by me.

How does this work?

We have a pair of keys, if data is encrypted with one key, then it can be decrypted by the other (key).

This applies to:

  • Encrypted with public key, decrypt with secret key, this is well known.
  • Encrypted with secret key, decrypt with public key, this is called signing and verifying

So, to verify that the data is signed by you, the recipient just need to decrypt it with your public key, if it's a valid signature, it's guaranteed that it will decrypt.

To make it more complex, in real life it doesn't simply encrypt all data using this secret key (it's impractical and low performance), instead, it first calculates the checksum (hash, SHA256 usually) and then encrypt the checksum.

To verify the signed data, the recipient needs to calculate the checksum and compare it with the one provided, if they match, the message is considered authentic.

Encrypt Data with PGP

Symmetric Encryption

This doesn't require any public key, you specify the AES key (kind of, it's the passphrase) to directly encrypt data.

The benefit is that anyone with the passphrase can decrypt

# encrypt, default to <file_name>.gpg
gpg -c data.bin

# decrypt
gpg -d data.bin.gpg >decrypted.bin

Encrypt to Recipient

This is where PGP shines. You specify a recipient, and gpg can encrypt data that only the recipient can decrypt, better still, you can also sign the encrypted data using your own secret key, so the recipient can make sure the encrypted data is sent by you.

# encrypt msg.txt to recipient 00416191C169BCF410712173E9FAF7E43E456E68, and sign it using key 410C99D49CF295D2
/tmp
❯ gpg --encrypt --recipient 00416191C169BCF410712173E9FAF7E43E456E68 -u 410C99D49CF295D2 --sign msg.txt

# decrypt and verify
/tmp
❯ gpg --decrypt msg.txt.gpg
gpg: encrypted with cv25519 key, ID FD2B7239D91B16B3, created 2023-06-04
      "jm33-test (Demo) <jm33.me@gmail.com>"
You can fetch my latest public key from keyserver, please prefer this key when encrypting emails:

    gpg --keyserver hkps://keyserver.ubuntu.com --recv-key 410C99D49CF295D2

To verify:

    gpg --fingerprint 410C99D49CF295D2

You should see:

    pub   ed25519 2023-05-21 [SC]
          7396 73C5 A6C3 797A 1298  6FFF 410C 99D4 9CF2 95D2
    uid           [  full  ] jm33@jm33.me <jm33@jm33.me>
    sub   cv25519 2023-05-21 [E]


The following keys are also in use:

    FF8EDA690269AD60674F1CF1D73C51683A5DBF07
    A914BB005F8A42F36C2997594876EC6D60BDB1DF

You can fetch them using gpg with the same command above, just replace the key ID

To sign my key:

    gpg --sign-key 410C99D49CF295D2

To upload the signed key to keyserver:

    gpg --keyserver hkps://keyserver.ubuntu.com --send-key 410C99D49CF295D2

Thanks a lot in advance if you decide to sign my key!
gpg: Signature made Sun 04 Jun 2023 07:42:23 PM CST
gpg:                using EDDSA key 739673C5A6C3797A12986FFF410C99D49CF295D2
gpg: Good signature from "jm33@jm33.me <jm33@jm33.me>" [ultimate]

Thunderbird and PGP

First you need to export your secret key so you can import it to Thunderbird's key manager (I know, but Thunderbird won't automatically pick up your key from GPG)

gpg --export-secret-keys -a 00416191C169BCF410712173E9FAF7E43E456E68 >keys.asc

Assuming you have already configured your email account in Thunderbird, open account settings.

> Account Settings > select your account > End-to-End Encryption > Add Key

  • If you already have a personal OpenPGP key pair from another software, choose Import an existing PGP key, select key.asc and import.
  • If you don't have a key yet, choose Create a New OpenPGP key.

After importing or creating it, while still in account settings, select the key you want to actively use with your email account.


Comments

comments powered by Disqus