GnuPG Guide

Encryption is a process that transforms information into an unreadable format that cannot be decrypted without the appropriate key or password. Alongside encryption, a digital signature is a scheme demonstrating the authenticity of digital information; a valid signature gives the recipient reason to believe, with a very high mathematical probability, that data was created by the known sender, and that it wasn't altered in transit. Various encryption standards, algorithms, and specialized protocols are available for use depending on the need; SSL, SSH, TLS, HTTPS, IPsec, etc.

PGP (Pretty Good Privacy) is an old, mature, thoroughly-tested, and battle-tested generic encryption standard which can be used to encrypt and sign data. There are many implementations for PGP, the most widely-used of which is GnuPG/GPG (GNU Privacy Guard), which this humble guide will go through.

GnuPG encrypts data using asymmetric key pairs, individually generated by GnuPG users; the "public key" and "private key":

Similarly, it can also be used to sign data in order to avoid forgery or tampering:

Installing GnuPG ~

There exist many graphical frontends to GnuPG (even for Android devices, like OpenKeychain), but they don't concern this guide since they're self-explanatory, and their usage differ from frontend to another. What really matters is that GnuPG itself comes with a command-line interface provided in Linux (and BSD) operating systems, which allows us to understand how the entire thing works. Windows has gpg4win too (which also includes the graphical interface Kleopatra).

To install GnuPG, you need to get it from your distribution's package manager. For example, to get it from the Arch/Artix Linux repositories, issue the command: pacman -S gnupg. This will install GnuPG (version "2.0.0" or later), and you can check the version by issuing: gpg --version.

Generating Keys ~

To create a private and public key pair, issue the command: gpg --full-generate-key. This will bring up a selection screen with many options in GnuPG:

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
What keysize do you want? 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
Is this correct? (y/N) y

In this example, we used the sensible defaults that every key pair generation should use:

  1. ECC (sign and encrypt) for the key would enable both encryption and signing.
  2. Curve 25519 for the elliptic curve would use the most secure one against brute-force attacks.
  3. 0 for the expiration date would make it last forever. You can change this according to your needs.

If you want a prime-field cryptography algorithm instead of an elliptic curve, you can choose RSA and RSA (with the 4096 keysize) instead of ECC (with the Curve 25519 curve). Both provide acceptable classical security (~128 to ~140 bits), but RSA-4096 incurs heavy operational overhead for signing and decryption, alongside vastly larger key and signature sizes compared to Curve 25519; hence, outside of legacy system compatibility or specific regulatory compliance, the latter is preferred.

Afterwards, GnuPG would ask for some user data (i.e., a name, an email address, and a comment) which is only used for making encryption and decryption easier, but they can be completely fake if you want to. The comment should remain blank since it just adds to the confusion:

GnuPG needs to construct a user ID to identify your key.
> Real name: Hatsune Miku
> Email address: miku@dayo.jp
> Comment:
You selected this USER-ID:
    "Hatsune Miku <miku@dayo.jp>"
> Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

Next, it'll prompt you for a passphrase to encrypt your private key at rest on your drive; this is merely a protective security layer, so if an attacker somehow exfiltrates your private key (whether through malware, an unencrypted backup, or physical device theft), they will need the passphrase. Make sure to use a good password! You definitely don't want your private key in the wrong hands.

After some waiting, the key pair generation will be a success!

pub   ed25519 2007-08-31 [SC]
      [fingerprint]
uid                  Hatsune Miku <miku@dayo.jp>
sub   cv25519 2007-08-31 [E]

You can afterwards edit $GNUPGHOME/gpg.conf (where the $GNUPGHOME environment variable is usually ~/.gnupg/ by default) and set the new secret key as the default key using the fingerprint of the key pair:

default-key [fingerprint]

The Private Key ~

Listing Private Keys ~

Simply issue: gpg --list-secret-keys. The private key is called a "secret key" in GnuPG merely for historical reasons.

Exporting the Private Key ~

Preventing the need to create a new key pair (in case your hard drive fails or something), you should export your private key and keep it safe somewhere (e.g., storing it on a USB, syncing it across multiple devices, etc.). Check out my Backup Guide!

To export the private key, issue: gpg --armor --export-secret-keys [name/email/id] > private-miku.asc. You can also encrypt the output using a password (it could be the same as your private key's passphrase) for extra security; you can read about doing that below in > Symmetrical Encryption.

Oh, and let's talk about --armor before we continue; by default, keys (as well as any other output from GnuPG such as encrypted data, signatures, etc.) are exported in a binary format. Key pairs in binary format are inconvenient when dealing with key pair exchange, so therefore, GnuPG supports a command-line option called --armor that causes the output to be generated in an ASCII-armored format (.asc) instead of a binary format.

Importing the Private Key

Simply issue: gpg --import private-miku.asc.

The Public Key ~

Listing Public Keys ~

Simply issue: gpg --list-public-keys, or simply: --list-keys. You can distinguish the key's capabilities by the codes in the [] brackets:

Viewing Information of Public Keys ~

If you want to view the full information about your (or any other) public key, just issue: gpg --list-keys --with-fingerprint --keyid-format=[short/long] [name/email/id]. The result would be something like this:

pub   ed25519/[id] 2007-08-31 [SC]
      Key fingerprint = [fingerprint]
uid                  Hatsune Miku <miku@dayo.jp>
sub   cv25519/[subkey] 2007-08-31 [E]

The output's layout might change depending on the version used, but it's always self-explanatory:

Exporting the Public Key ~

For people to encrypt data that only you can decrypt, you must exchange public keys. To send your public key to a correspondent, you must first export it by issuing something like: gpg --armor --export [name/email/id] > public-miku.asc, which outputs the .asc file into the current directory so you can send it to others and have them import it.

Importing Public Keys ~

In order to encrypt data to someone else using their public key, you first need to import it by issuing: gpg --import public-teto.asc.

Signing Public Keys ~

It's very important to validate the person from whom you receive a public key; you can't just download the public key of someone you know and trust it as his public key, since it could be an impersonator. Therefore, after checking the public key's fingerprint and associated information, you can import it (as seen above) and sign it by issuing: gpg --sign-key [name/email/id], and add it to your "ownertrust" database by issuing: gpg --edit-key [name/email/id], typing trust, selecting a trust level (such as 4 for "full" trust), and typing save.

When you sign someone's public key you are effectively saying: "I testify that this key actually belongs to this person." People who trust you and your judgement can verify the signature of that public key. This mechanism is unique to PGP encryption and forms what is known as the "Web of Trust" (WoT).

Phil Zimmermann, the creator of PGP, described the Web of Trust as follows:

As time goes on, you will accumulate keys from other people that you may want to designate as trusted introducers. Everyone else will each choose their own trusted introducers. And everyone will gradually accumulate and distribute with their key a collection of certifying signatures from other people, with the expectation that anyone receiving it will trust at least one or two of the signatures. This will cause the emergence of a decentralized fault-tolerant web of confidence for all public keys.

While not universally utilized by all PGP users, participating in the Web of Trust system is highly recommended; it allows users to be decentralized, peer checked, and disrupts bad-actors.

If you want to verify a key for your own local use without publicly exporting or sharing your signature, you can use --lsign-key instead of --sign-key.

Verifying Public Key Signatures ~

To verify a public key that is signed by someone you know, you need to import the key then verify if someone you know has signed it with: gpg --check-sigs [name/email/id], so you can sign it and trust it afterwards (thus building a "Web of Trust").

Note that sometimes when using --check-sigs, something similar to this may appear:

<n> signatures not checked due to missing keys.

That just means that the key you received has also been signed by other people, but since you don't have their public keys, you can't verify their signatures.

Editing Public Keys ~

Simply issue: gpg --edit-key [name/email/id] then type help for a list of commands. For example; check to check the signatures added to the public key, clean to clean unusable user IDs and signatures, quit to quit, etc.

If you edit your own key you will get so many more options, like changing the password of your private key or even setting a "photo" on your public key! This will obviously make the public key much larger in size, though...

Deleting Public Keys ~

To delete an imported public key for whatever reason (such as if a user generated a new key pair and you need to delete their old public key before importing the new one), you need to issue: gpg --delete-keys [name/email/id].

Encrypting Files ~

To encrypt a file with a user's public key, you need to issue: gpg --encrypt --recipient [name/email/id] file.txt. The command will result in the creation of a file called file.txt.gpg which is in a binary format.

If you would like it to be an ASCII-armored version instead, simply add --armor to the command so it looks like this: gpg --armor --encrypt --recipient [name/email/id] file.txt.

Symmetrical Encryption ~

Symmetric cryptography uses a secret, shared password for encrypting data, rather than a public key to encrypt and a private key to decrypt. GnuPG allows doing that too with the --symmetric option: gpg --symmetric --armor --cipher-algo AES256.

Here, we used --cipher-algo AES256 since AES256 is the most secure algorithm.

Decrypting Files ~

To decrypt a file encrypted with your public key (or with a shared password), you need to issue: gpg --decrypt file.txt.gpg or simply: gpg file.txt.gpg.

If the file was signed (as seen above), something like this would appear on the command line as well:

gpg: Good signature from "Kasane Teto <teto@pote.to>"
Primary key fingerprint: [fingerprint]

Signing Files ~

Signing a file cryptographically proves that you're the author, and that the file hasn't been tampered with. There are three primary ways to sign a file in GnuPG:

Signing and encrypting go hand-in-hand. If you're sending sensitive data, you can combine both operations into a single command by chaining flags (e.g., gpg --sign --encrypt --recipient [name/email/id] file.txt).

Verifying Signatures ~

To verify a file's signature (without outputting the data), issue: gpg --verify file.txt.gpg. In the other hand, to verify a detached signature (whether .sig or .asc), issue the command: gpg --verify file.txt.sig. Same command, same assignment; only verification.

Abbreviations ~

Writing long commands can be annoying. Fortunately, there exist many abbreviations for common options:

Long Short
--armor -a
--decrypt -d
--detach-sign -b
--encrypt -e
--list-public-keys or --list-keys -k
--list-secret-keys -K
--recipient -r
--sign -s
--symmetric -c

Keyservers ~

Manually exporting and importing public keys as .asc files can be tedious, so the purpose of a public keyserver is to act as a giant directory for PGP keys, allowing you to easily upload your public key and download others' public keys online.

Configuring a Keyserver ~

To use keyservers, you should define a default one in your $GNUPGHOME/gpg.conf file. Two popular options are keys.openpgp.org (preferred for its modern email verification procedures) and keyserver.ubuntu.com (an older but still usable alternative). You can add a line like this:

keyserver hkps://keys.openpgp.org

Uploading Keys ~

To broadcast your public key to a keyserver, you can use the traditional command: gpg --send-keys [id/fingerprint].

However, if you are using a modern keyserver like keys.openpgp.org, keys sent this way won't be searchable by email until you verify your address. You can trigger a verification email directly from the terminal by piping your exported key:

gpg --export --armor miku@dayo.jp | curl -T - https://keys.openpgp.org

Open the link, send a verification email, check your inbox, and click the confirmation link to finish publishing your key. Always remember, some older keyservers are append-only; once you upload a key, it can't be deleted from the keyserver, only updated or revoked!

Searching and Downloading Keys ~

If you need someone's public key and you know their email address, you can search the keyserver for it using: gpg --search-keys akita@ne.ru. GnuPG will present a numbered list of matching keys; type the number you want, and it will be imported automatically.

If you already know their exact key ID or fingerprint, you can fetch it directly without searching: gpg --receive-keys [id/fingerprint].

Refreshing Keys ~

People update their public keys over time (e.g., adding new signatures from the Web of Trust, extending the expiration date, or revoking the key if compromised). To synchronize your local keyring with the latest changes from the keyserver, simply issue: gpg --refresh-keys.

Integrations ~

You can configure a wide range of software that have GnuPG integration (e.g., email clients or password managers) to use PGP encryption, so you don't have to run gpg --encrypt and gpg --decrypt every time.

aerc ~

For the longest time, I haven't used PGP to encrypt my emails simply because I didn't know how to implement it in aerc, my preferred email client (as seen on my Dotfiles). This guide assumes you've already made a personal key pair with your correspondent email address, and you've already set up aerc (see: aerc Setup Guide).

First, you must configure how GnuPG prompts you for your password when trying to decrypt an email later on. Open the $GNUPGHOME/gpg-agent.conf file (where the $GNUPGHOME environment variable is usually ~/.gnupg/ by default), and add this line for a terminal-based password prompt:

pinentry-program ~/.local/bin/dmenu-pass

dmenu-pass is a script in my Dotfiles that uses dmenu on X11 but falls back to pinentry-curses in the TTY. You may use other pinentry programs, such as pinentry-gtk or pinentry-qt, depending on your setup, but note that for some retarded reason, aerc's TUI (as of this date) breaks apart when trying to decrypt using a terminal prompt (such as pinentry-tty or pinentry-curses).

Now add the following lines to $XDG_CONFIG_HOME/aerc/aerc.conf (where $XDG_CONFIG_HOME is ~/.config/ by default) under the [general] subsection to integrate GnuPG and allow pinentry to halt aerc's interface in order to claim keyboard focus inside a raw terminal environment:

[general]
pgp-provider = gpg
use-terminal-pinentry = true

Now, you may create a file for the public key which you can attach to your emails for others to send you encrypted emails back with, keep it somewhere obvious for easy access (like ~/ maybe?), since you'll be attaching it a lot for new contacts:

gpg --armor --export miku@dayo.jp > ~/public-miku.asc

To send encrypted mail to someone else however, you first need to import their public key:

gpg --import public-teto.asc

Finally, start aerc. After composing an email, aerc (by default) gives you the "Send this email?" prompt, in which you first add your public key as an attachment (press a then enter the aforementioned ~/public-miku.asc public key path), then issue these commands to toggle encryption (default keybinding: x) and signing (default keybinding: s), respectively:

:encrypt
:sign

Incoming encrypted email is automatically decrypted. If you manually want to do so, simply pipe the received email to GnuPG (default keybinding for :pipe: |):

:pipe gpg -r teto@pote.to -d

You will be prompted to enter your key passphrase, so just do that.

pass ~

pass is a simple shell script that uses GnuPG to encrypt and manage passwords. To set it up, just initialize a new password database using pass init [fingerprint]! You can check the pass manpage (or follow the examples on its website) for instructions on using it.