A gentle introduction to GPG
This is the first of a two-part series on GNU Privacy Guard, better known as GPG. The next post talks about the problem I wanted to solve and how I solved it, but this one distills a crash course in the fundamentals of GPG that I had to give myself along the way.
Choosing a keyring
All of the important GPG commands work in a context consisting of:
- a (possibly empty) set of keyrings, each of which is
- a (possibly empty) set of primary keys, each of which is associated with
- a (possibly empty) set of subkeys.
Most commonly, you'll just have one keyring, the default keyring,
which is searched for in the GPG home directory.
The default home directory is ~/.gnupg
, but you can override it
with option --homedir <dir>
or environment variable GNUPGHOME
.
I like to use the environment variable pointing to a temporary home directory to experiment with commands. The directory needs to be secure (mode 700) before GPG will touch it.
You can add keyrings to the working set with option --keyring <file>
.
You can remove the default keyring from the working set with option
--no-default-keyring
.
Commands versus options
All GPG commands are subcommands of the gpg
program, like Git,
but unlike Git, both long options and commands
are prefixed with double hyphen (--
).
Commands generally fall into one of three categories:
- key management
--list-keys
,--list-secret-keys
,--show-keys
--export
,--export-secret-keys
,--export-secret-subkeys
--import
--quick-generate-key
,--quick-add-key
,--quick-set-expire
--delete-keys
,--delete-secret-keys
,--delete-secret-and-public-key
- message operations
--sign
,--clear-sign
,--detach-sign
--verify
--encrypt
--decrypt
- read-eval-print loop (REPL)
--edit-key
You will likely never need to call the message operations yourself. They will typically be called on your behalf by other programs, like Git.
The REPL has its own set of subcommands. The GPG manual does a good job explaining them.
Understanding key listings
Let's look at the output of --list-keys
, or -k
(lowercase k):
> gpg -k
/home/jfreeman/.gnupg/pubring.kbx
---------------------------------
pub rsa4096 2022-12-13 [C] [expires: 2027-04-21]
02F62E6173430F646CAF0709B2AA4D29C21E79C4
uid [ultimate] John Freeman <jfreeman08@gmail.com>
sub ed25519 2025-04-21 [S] [expires: 2027-04-21]
sub rsa4096 2024-04-06 [S] [expires: 2027-04-21]
sub ed25519 2025-04-21 [S] [expires: 2027-04-21]
--list-keys
prints a table for each keyring.
In each table, the title shows the path to the keyring file.
The first row has the pub
or public primary key.
The next row is its fingerprint (more on that later).
Then one or more rows of uid
or user ID,
and their level of trust (in this case, "ultimate").
Then one or more rows of sub
or public subkey.
Each key row includes the key algorithm (e.g. ed25519),
the key expiration (if any), and the key capabilities,
abbreviated according to the following list:
- [C]ertify: can sign keys. Necessary to create new subkeys.
- [S]ign: can sign messages and verify signatures. Necessary to sign Git commits.
- [E]ncrypt: can encrypt and decrypt messages.
- [A]uthenticate: can authenticate? Nobody knows.
Now let's look at the output of --list-secret-keys
, or -K
(uppercase k):
> gpg -K
/home/jfreeman/.gnupg/pubring.kbx
---------------------------------
sec# rsa4096 2022-12-13 [C] [expires: 2027-04-21]
02F62E6173430F646CAF0709B2AA4D29C21E79C4
uid [ultimate] John Freeman <jfreeman08@gmail.com>
ssb ed25519 2025-04-21 [S] [expires: 2027-04-21]
ssb# rsa4096 2024-04-06 [S] [expires: 2027-04-21]
ssb# ed25519 2025-04-21 [S] [expires: 2027-04-21]
The output is very similar.
The main differences are sec
for the secret primary key
and ssb
for secret subkey.
The hash (#
) after the key type indicates that that part of the key
(public or secret) is missing.
Identifying keys
Many GPG commands accept a key parameter,
e.g. gpg --export-secret-subkeys <subkey>
.
There are a few different ways to identify a key.
The GPG manual has a long section exhaustively describing all of them,
but I'm going to cover just the ones I use most often.
The safest way to uniquely identify a key is with its fingerprint, a 160-bit code typically printed as 40 hex digits. There are two equivalent ways to see the fingerprints of all keys:
> gpg --fingerprint --fingerprint
> gpg -k --with-fingerprint --with-subkey-fingerprint
/home/jfreeman/.gnupg/pubring.kbx
---------------------------------
pub rsa4096 2022-12-13 [C] [expires: 2027-04-21]
02F6 2E61 7343 0F64 6CAF 0709 B2AA 4D29 C21E 79C4
uid [ultimate] John Freeman <jfreeman08@gmail.com>
sub ed25519 2025-04-21 [S] [expires: 2027-04-21]
BEC9 7872 D437 6DEB CAE0 9E7B B539 B343 B4A8 25B9
sub rsa4096 2024-04-06 [S] [expires: 2027-04-21]
5AFA 6448 3652 2B65 A575 F9B9 FD28 38F2 297E 3D45
sub ed25519 2025-04-21 [S] [expires: 2027-04-21]
8565 18D5 4D16 BC03 6F7F 3CB4 60C4 1AE3 84BE 106A
These both include spaces in the fingerprints, though.
To get them as long strings that are easy to copy,
I prefer this command, which I alias as gpgl
:
> gpg --list-secret-keys --keyid-format long --with-subkey-fingerprint --list-options show-unusable-subkeys
/home/jfreeman/.gnupg/pubring.kbx
---------------------------------
sec# rsa4096/B2AA4D29C21E79C4 2022-12-13 [C] [expires: 2027-04-21]
02F62E6173430F646CAF0709B2AA4D29C21E79C4
uid [ultimate] John Freeman <jfreeman08@gmail.com>
ssb ed25519/B539B343B4A825B9 2025-04-21 [S] [expires: 2027-04-21]
BEC97872D4376DEBCAE09E7BB539B343B4A825B9
ssb# rsa4096/FD2838F2297E3D45 2024-04-06 [S] [expires: 2027-04-21]
5AFA644836522B65A575F9B9FD2838F2297E3D45
ssb# ed25519/60C41AE384BE106A 2025-04-21 [S] [expires: 2027-04-21]
856518D54D16BC036F7F3CB460C41AE384BE106A
(--list-options show-unusable-subkeys
makes it list expired keys too.)
Sometimes, a key can be identified by its long key ID, which is the low 64 bits (last 16 hex digits) of its fingerprint. In the past, it was even common to use the short key ID, which is the low 32 bits (last 8 hex digits) of the fingerprint. They are rarer now, because of successful collision attacks against them.
Finally, a key can be identified by a substring match of one of its user IDs. A user ID is a (name, email, comment) tuple attached to a primary key. These are even more often ambiguous, though, and some commands require a fingerprint.
⚠️ WARNING: When identifying a subkey by fingerprint, it is often necessary to append an exclamation point (
!
). Otherwise, GPG will interpret it as a reference to its primary key.
Scripting
Many GPG commands expect to be connected to a terminal
(which can be chosen with environment variable GPG_TTY
).
When you want to script GPG commands,
it is important to be aware of these special options:
--with-colons
: Make--list
commands write machine-readable output that is stable across versions. This makes it easy to select fields with awk, e.g.awk -F: '/^fpr/ { print $10 }'
.--batch
: Never prompt. Disable interactive commands.--batch
commands often expect keys to be identified by fingerprint.--yes
: Assume "yes" to most questions.--command-fd 0
: Read prompt input, e.g.--edit-keys
subcommands, from stdin.