I’ve used GPG on and off for years without ever really getting it to stick. I usually encrypt a file here, verify a signature there, always by googling the exact flag. This note is me finally sitting down and writing out the workflow end to end, so the next time I need it I read this instead of trying to remember it or burning my AI token limits.

This isn’t a GPG manual. It’s the subset of commands that cover 90% of what a working engineer actually does with GPG: generate a key, encrypt/decrypt files two different ways, sign things, verify them, and hand your public key to someone else. If you need the full command reference, GnuPG’s own docs are good. This is the practical cut of it.

Setup: check for existing keys, generate one if you don’t have it

First, see if you already have a keypair:

$ gpg --list-secret-keys

If this is your first time running GPG, it’ll set up its home directory on the spot:

gpg: directory '/home/gourab/.gnupg' created
gpg: /home/gourab/.gnupg/trustdb.gpg: trustdb created

No keys yet, so generate one:

$ gpg --full-generate-key

It’ll walk you through key type, size, expiry, and your identity (name + email). I went with the default modern setup: Ed25519 for signing, Curve25519 for encryption. Ed25519 is faster and the keys are smaller, and there’s no real reason to reach for RSA anymore unless something you’re interoperating with demands it.

Check it worked:

$ gpg --list-secret-keys
[keyboxd]
---------
sec   ed25519 2026-07-15 [SC]
      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid           [ultimate] Gourab Sarkar (Hey, its me) <gourab.sarkar@example.com>
ssb   cv25519 2026-07-15 [E]

Two things worth knowing here, because the notation is dense:

  • sec vs ssb: sec is your secret primary key, ssb is a secret subkey. GPG splits capabilities across keys: the [SC] suffix on the primary means it can Sign and Certify (i.e. vouch for other keys); the [E] suffix on the subkey means it’s used for Encryption. This split exists so you can, for example, revoke or rotate your encryption subkey without touching your primary identity key.
  • [ultimate] trust: that’s the trust level GPG assigns to your own key by default, since you obviously trust yourself. Trust levels matter more once you start importing other people’s keys, and that’s discussed in later sections.

Asymmetric encryption: encrypt to someone’s public key

This is the “real” GPG use case: encrypt a file so that only the holder of a specific private key can open it. You need the recipient’s public key on your machine to do this. You’re encrypting to them, not with a shared secret.

$ ls
archive.zip  some-text.txt

$ gpg --output archive.zip.gpg --encrypt --recipient "Gourab Sarkar" archive.zip

$ ls
archive.zip  archive.zip.gpg  some-text.txt

--recipient takes a name, email, or key ID. GPG resolves it against whatever public keys are in your keyring. The resulting archive.zip.gpg is useless to anyone who doesn’t hold the matching private key, including you, unless you’re the recipient.

Symmetric encryption: encrypt with a shared passphrase

Sometimes there’s no recipient key, you just want to lock a file behind a passphrase and share that passphrase out of band (or it’s just for yourself, stashed somewhere). That’s symmetric mode:

$ gpg --symmetric archive.zip

It’ll prompt you for a passphrase twice and produce archive.zip.gpg. No keys involved here at all. Anyone with the passphrase and GPG can decrypt it. Worth knowing: GPG defaults to AES-256 for this, which is a perfectly good cipher, so there’s no need to override that.

Detached signatures: prove authorship and integrity

A signature proves two things at once: the file came from you (assuming your private key is actually private), and it hasn’t been modified since you signed it. A detached signature keeps the signature as a separate file instead of bundling it into the original. This becomes useful when you don’t want to touch the original file at all, e.g. signing a release tarball.

$ gpg --output archive.zip.sig --detach-sign archive.zip

This produces archive.zip.sig, a binary signature file that travels alongside archive.zip. To verify it:

$ gpg --verify archive.zip.sig archive.zip
gpg: Signature made Wed, Jul 15, 2026  3:53:41 PM IST
gpg:                using EDDSA key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
gpg: Good signature from "Gourab Sarkar (Hey, its me) <gourab.sarkar@example.com>" [ultimate]

Good signature is the line you actually care about. This means the file matches what was signed and the signing key is the one it claims to be.

The encrypt → sign → decrypt → verify loop

Chaining these together is a common pattern: encrypt a file for a recipient, sign the original separately so the recipient can verify authenticity after decrypting, and get rid of the plaintext until it’s needed again.

$ gpg --output archive.zip --decrypt archive.zip.gpg
$ gpg --verify archive.zip.sig archive.zip

Worth pinning down the distinction here since it trips people up: encryption protects confidentiality (nobody else can read it), signing protects integrity and authenticity (you can prove who made it and that it’s unaltered). They’re independent, and you can do either without the other, or both.

ASCII-armored signatures (.asc)

Binary signature files are annoying to paste into an email, a PR description, or a text field. --armor produces a base64-ish text block instead:

$ gpg --armor --output archive.zip.sig.asc --detach-sign archive.zip
$ cat archive.zip.sig.asc
-----BEGIN PGP SIGNATURE-----

iHUEABYKAB0WIQRCkLzktRsfRvKYbNUSNRmP2OA0agUCaldhFgAKCRASNRmP2OA0
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
=D6dw
-----END PGP SIGNATURE-----

It verifies exactly the same way, so --verify doesn’t care whether the signature is armored or binary:

$ gpg --verify archive.zip.sig.asc archive.zip
gpg: Signature made Wed, Jul 15, 2026  3:59:42 PM IST
gpg:                using EDDSA key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
gpg: Good signature from "Gourab Sarkar (Hey, its me) <gourab.sarkar@example.com>" [ultimate]

Rule of thumb: armor anything that has to survive being copy-pasted through a UI (Slack, a text box, an email body). Leave it binary for anything that’s just moving as a file.

Sharing your public key

None of the asymmetric stuff works for anyone else unless they have your public key. List what you’ve got first:

$ gpg --list-keys

Then export it in armored form so it’s shareable as plain text:

$ gpg --armor --export gourab.sarkar@example.com
-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEaldciBYJKwYBBAHaRw8BAQdArvu+pm3Lea7WUH7xXyOUwCLtER4UV15VJF/l
...
-----END PGP PUBLIC KEY BLOCK-----

Paste this wherever it needs to go, for instance, a keyserver, a README, a Slack message. It’s public by design; there’s nothing to protect here. The private key is the one that never leaves your machine.

Importing someone else’s public key

Everything above works one-sided: encrypting to someone or verifying their signature both require their public key sitting in your keyring first. If they sent you a .asc block or a key file:

$ gpg --import their-key.asc

Confirm it landed:

$ gpg --list-keys

You’ll now be able to use --recipient with their name/email and --verify against their signatures.

Trusting an imported key

The first time you encrypt to a freshly imported key, GPG will warn you:

gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.

That’s expected, because GPG has no way of knowing the key actually belongs to who it claims. Once you’ve verified the fingerprint out of band (call them, check it in person, whatever your paranoia level demands), tell GPG you trust it:

$ gpg --sign-key "Their Name"

This walks you through picking a trust level (marginal, full, ultimate). Do this and the warning goes away on future operations with that key.

Revocation certificate

A revocation certificate is how you tell the world “stop trusting this key” if your private key is ever lost or compromised. The catch: you need the certificate to revoke, and you can’t generate one without the private key. So if you lose the key, you’ve also lost your ability to revoke it. Generate it right after key creation and store it somewhere safe and offline (not next to your private key):

$ gpg --output revoke.asc --gen-revoke "Gourab Sarkar"

You won’t need this 99% of the time. The 1% is bad enough that skipping this step is a mistake people only make once.

Back up your private key

Your private key lives only in ~/.gnupg by default. Disk dies, laptop gets stolen, no backup, so every file you ever encrypted to that key is gone for good, permanently unrecoverable. Export and store it somewhere safe (encrypted external drive, password manager attachment, etc. Don’t store this on a random cloud folder in plaintext):

$ gpg --output private-key-backup.gpg --export-secret-keys "Gourab Sarkar"

Do this once, right after generating your key, and again if you add subkeys or change trust.

Cheatsheet

WhatCommand
List your secret keysgpg --list-secret-keys
List public keys in your keyringgpg --list-keys
Generate a new keypairgpg --full-generate-key
Encrypt for a recipientgpg --output file.gpg --encrypt --recipient "Name" file
Encrypt with a shared passphrasegpg --symmetric file
Decryptgpg --output file --decrypt file.gpg
Detached signature (binary)gpg --output file.sig --detach-sign file
Detached signature (armored/text)gpg --armor --output file.sig.asc --detach-sign file
Verify a signaturegpg --verify file.sig file
Export your public keygpg --armor --export you@example.com
Import someone’s public keygpg --import their-key.asc
Trust an imported keygpg --sign-key "Their Name"
Generate a revocation certificategpg --output revoke.asc --gen-revoke "Name"
Back up your private keygpg --output backup.gpg --export-secret-keys "Name"