This article assumes you understand the basics of public key cryptography, the heart of the OpenPGP protocol.

This article also assumes you’re using MacOS and that you have the Homebrew Package Manager installed.

Unless otherwise specified, a “key” (primary key, subkey, etc.) refers to a keyPAIR of public and private keys. I think this is confusing. We should always say keyPAIR if we mean a pair of 1 public key and 1 private key.

Unfortunately, referring to a keypair as a “key” is standard. So in this article, I will abide.

I know you’re dying to get started sending cryptographically secure messages to all your buddies. So, let’s begin!

Installing GPG

brew install gpg

As of this day, the latest version is 2.2.3

Generating Keys

gpg --full-generate-key

The --full-generate-key option will guide you through each step with helpful dialogs.

The steps:

Choosing your key types.

By default, GPG uses 1 primary key for authentication/signing, and 1 subkey for encryption/decryption.

These two keys can utilize different cryptosystems. (E.g. a DSA key for signing and an ElGamal key for encryption/decryption.)

However, GPG uses RSA for both keys by default.

Unless you know what you’re doing, I would just stick with the RSA+RSA default. Choosing keysizes.

Your keys can be between 1024 and 4096 bits long.

The default keysize is 2048 bits long.

Shorter keysizes are less secure, but more performant.

Longer keysizes are more secure, but less performant.

I usually opt for the max keysize: 4096 bits. Setting an expiration date.

You can set your key to expire in N days, weeks, months, or years. Or, you can set it to never expire.

The default setting is — no expiration date.

If you are some high-profile person who’s constantly at risk of having keys stolen, then perhaps you’d like to set an expiration date. Otherwise, I see no reason to do this, so long as you guard your private keys properly. Fill out your user ID information.

Give your name, email address, and any comments you’d like to add.

This information will be attached to your keys. Set a passphrase.

For added security, gpg will prompt you for a passphrase every time you perform some operation that requires access to your private keys. Move your mouse and spam your keyboard.

All of the above cryptosystems rely on random numbers to generate keys.

Computers aren’t capable of generating truly random numbers. They rely on a plethora of inputs with wide value-ranges to generate numbers that seem random.

Mouse position and keystrokes are part of those inputs, among other things. To increase the randomness of your keys, manipulate these inputs during this section.

You’ve created your keys! In the output, you’ll see rows for your primary keypair’s public key, user ID, and sub-keypair’s public key.

Listing public keys

gpg --list-public keys

This provides information about the public keys in your “contacts list”.

This won’t list the contents of the public keys, only metadata such as cryptosystem, keysize, and date-of-creation.

It will also show the associated user ID for a given set of public keys.

The fingerprints of primary-public keys will also be shown.

According to Wikipedia,

A public key fingerprint is a short sequence of bytes used to identify a longer public key. Fingerprints are created by applying a cryptographic hash function to a public key. Since fingerprints are shorter than the keys they refer to, they can be used to simplify certain key management tasks.

Listing ALL public keys fingerprints (Primary Keys + Subkeys)

gpg --fingerprint --fingerprint

You must give the --fingerprint command twice.

Relevant snippet from gpg manpage.

Listing private keys

gpg --list-secret-keys

This provides information about the private keys you have.

For me, this output is identical to gpg --list-public-keys because I have all the private keys for each keypair I have.

It’s possible to only have the private key/public key of a given keypair. If you have that asymmetry, then your gpg --list-secret-keys would give different output from your gpg --list-public-keys .

Why is it called secret keys?

According to dave_thompson_085 on this serverfault post,

gpg calls private keys ‘secret’ because PGP dates from before people settled on the names ‘private’ key for the half of an asymmetric pair held by (ideally) only one party versus ‘secret’ key for a symmetric value usually held by two or more mutually trusting parties but nobody else.

Listing key IDs

gpg --list-public-keys --keyid-format none|short|0xshort|long|0xlong

Various gpg operations target keys. E.g. deleting keys, signing files with a specific private key, etc. To specify a key, you must provide some sort of key-identification.

Key IDs fulfill this purpose.

Key IDs are essentially abbreviations of fingerprints. Here’s a guide to key ID formats, given by Jens Erat on this superuser post.

Fingerprint: 0D69 E11F 12BD BA07 7B37 26AB 4E1F 799A A4FF 2279

Long key ID: 4E1F 799A A4FF 2279

Short key ID: A4FF 2279

A short key ID is the last 8 characters of the fingerprint.

A long key ID is the last 16 characters of the fingerprint.

0xshort and 0xlong simply prefix 0x to the short and long key IDs respectively.

E.g. a short key ID would be A4FF2279 . The corresponding 0xshort key ID

would be 0xA4FF2279 .

The 0x is sometimes used to explicitly note that the key ID is a hex value.

Encrypting a message (binary encoding)

gpg --encrypt --recipient 'some user ID value' <file>

The default encryption format is in binary. If you’re exchanging messages via entire files, this is fine.

However, if you wish to copy/paste your encrypted message on a website (go check out /r/GPGpractice!), binary won’t work.

You can’t copy/paste/type binary like normal text. If you want to interact with the encrypted message like normal text, you’ll need to encode the message in ASCII armor using the --armor option. (See below)

By default, the encrypted file’s name is <filename>.gpg . But you can specify a different filename using the --output option. (See below.)

Encrypting a message (ASCII Armor)

gpg --encrypt --armor --recipient 'some user ID value' <file>

The --armor option encodes the encrypted message in ASCII armor. This allows you to interact with your encrypted message like normal text.

With the binary encryption, you couldn’t copy/paste/type it onto a forum or in an email. You can’t do that with binary.

But with ASCII text, you can.

Specifying an encrypted file’s filename

gpg --output <file> --encrypt --recipient 'some userID value' <file>

The --output option can also be used with other operations, such as decrypting a file and signing a file.

Decrypting a message

gpg --decrypt <encrypted-file>

By default, gpg will print the decrypted message to STDOUT .

You can write the decrypted message to a file using the --output option, like how it was used with encryption above.

Writing the decrypted message to a file

gpg --output <file> --decrypt <encrypted-file>

Signing a file (binary encoding)

gpg --sign <file>

Signing a file produces a new, signed file that’s binary encoded.

IMPORTANT NOTE:

Signing and encrypting a single document is a common task.

People encrypt their documents to ensure that only a specific recipient can read them (encryption).

People sign their documents to assure others that the document was sent by the themselves — that the document-sender’s identify wasn’t spoofed (authentication)…

…AND to assure others that the document was not modified from the original (integrity check).

It’s common to perform both operations on a given document.

When you gpg --sign a document, the output is in binary.

So you might think that you’ve encrypted the document, in addition to signing it.

You technically have, but you practically haven’t.

I say technically, because the document was indeed encrypted with your private key. To read the signed document, it must be decrypted with your public key.

But from an encryption standpoint, how useless is that? Your public key is widely available. Basically anyone can decrypt it. That’s not secure at all.

Bad things might happen if you post sensitive documents, thinking that they’re properly encrypted…

TL;DR: Documents signed with gpg --sign are binary-encoded. The binary encoding might persuade you that a document is also encrypted. It’s not. But you might think it is. So, you might publicly post documents, thinking they’re encrypted. You might post sensitive information — that everyone can decrypt with your public key. That’s not secure at all.

Signing a file (readable message)

gpg --clearsign <file>

With normal gpg --sign , the outputted document is binary-encoded.

Often, you don’t want this. You want the message to immediately readable, without having to decrypt it with the corresponding public key.

In other words, you just want the message to be verifiable. You don’t care that everyone can see the message.

gpg --clearsign allows this. Like with gpg --sign , this embeds your signature into the outputted document.

But as the name implies, the contents of the document are clear. I.e. they’re still readable without any decryption.

Signing a file (detached signature — in binary)

gpg --detach-sign <file>

Sometimes, you want a signature to be separate from its corresponding file.

You’d likely do this with any file that isn’t a plaintext message. E.g. programs, videos, images, audio, etc.

Why? Because signing a file alters its contents.

Altering the contents of a program-file, video, etc. could break it.

Example: Signing a simple node.js program, rendering the signed file unexecutable.

Signing a file (detached signature — ASCII armor)

gpg --armor --detach-sign <file>

With a typical gpg --detach-sign <file> , the detached signature binary-encoded.

As mentioned above, binary is cumbersome for copy/pasting onto web forums, email, etc.

If you’d like to copy/paste/type your detached signature as such, encode it with ASCII armor using the --armor option.

Signing a file (specifying output file’s filename)

gpg --output <file> --sign <file>

You can use the --output option to specify the outputted file’s filename.

You can also do this with ASCII-armored/clearsign signatures and detached signatures.

Verifying a signed file (binary encoded)

gpg --verify <signed-file>

A signed-file that’s binary-encoded is unreadable (unless you can read binary!).

When using gpg --verify on a binary-encoded signed-file, it will only inform you of the file’s authenticity. It won’t decode the file for you.

If you want to decode the file/make it readable, you need to gpg --decrypt it.

Note: Running gpg --decrypt on a binary-encoded signed-file will also inform you of the file’s authenticity — as shown above.

I.e. Running gpg --decrypt on a binary-encoded signed-file does gpg --decrypt and gpg --verify .

Verifying a signed file (clearsigned)

gpg --verify <clearsigned-file>

Verifying a signed-file with ASCII-armor is identical to verifying the binary-encoded counterpart.

Though, what’s nice is that you don’t have to gpg --decrypt the ASCII-armor signed-file to read it, since it was readable to begin with. (That is the nature of clearsigned files.)

Verifying a file with a detached signature

gpg --verify <detached-signature-file> <corresponding-content-file>

The process is identical for both binary-encoded detached signatures and ASCII-armored detached signatures.