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:
secvsssb:secis your secret primary key,ssbis 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
| What | Command |
|---|---|
| List your secret keys | gpg --list-secret-keys |
| List public keys in your keyring | gpg --list-keys |
| Generate a new keypair | gpg --full-generate-key |
| Encrypt for a recipient | gpg --output file.gpg --encrypt --recipient "Name" file |
| Encrypt with a shared passphrase | gpg --symmetric file |
| Decrypt | gpg --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 signature | gpg --verify file.sig file |
| Export your public key | gpg --armor --export you@example.com |
| Import someone’s public key | gpg --import their-key.asc |
| Trust an imported key | gpg --sign-key "Their Name" |
| Generate a revocation certificate | gpg --output revoke.asc --gen-revoke "Name" |
| Back up your private key | gpg --output backup.gpg --export-secret-keys "Name" |