gpg Command in Linux: Complete Guide to GNU Privacy Guard Encryption and Parameters

gpg command in Linux and it perimeters

GPG is one of those tools I use constantly but only ever remember half the flags for, which is exactly why I keep coming back to write things like this down properly. GNU Privacy Guard is a full implementation of the OpenPGP standard — public-key encryption, digital signatures, and symmetric encryption, all built around a genuinely elegant trust model once you get past the initial learning curve.

I’ve verified every command in the sections below directly: key generation, listing, symmetric and asymmetric encryption/decryption, signing, verification, and export all ran successfully on a live system.

Public-Key Cryptography, Briefly

GPG is built on asymmetric cryptography: each identity has a key pair — a public key you share freely, and a private key you never share, ever. Anything encrypted with someone’s public key can only be decrypted with their private key. Anything signed with your private key can be verified by anyone using your public key, proving the message came from you and hasn’t been altered.

This solves two distinct problems: confidentiality (encryption — only the intended recipient can read it) and authenticity/integrity (signing — anyone can verify who sent it and that it wasn’t tampered with). GPG also supports symmetric encryption, which uses a single shared passphrase rather than a key pair — simpler, but requires securely sharing that passphrase out-of-band.

Installing GPG

Debian/Ubuntu:

sudo apt install gnupg

RHEL/Fedora:

sudo dnf install gnupg2

Check version:

gpg --version

I confirmed gpg (GnuPG) 2.4.4 on the test system used for verification in this article.

Generating a Key Pair

Interactive generation:

gpg --full-generate-key

You’ll be walked through:

  1. Key type (RSA and RSA is the default and safe general-purpose choice; ECC/Curve25519 options are also available on modern GPG for smaller, faster keys)
  2. Key size (3072 or 4096 bits for RSA)
  3. Expiration (I generally set 1-2 years and plan to renew, rather than “never expire,” since a key with no expiration that’s later compromised has no natural cutoff)
  4. Name, email, and comment (your User ID)
  5. A passphrase to protect the private key

For scripted/non-interactive generation (useful for automation, CI pipelines, or testing):

cat > keygen-batch.txt <<'EOF'
%no-protection
Key-Type: RSA
Key-Length: 3072
Subkey-Type: RSA
Subkey-Length: 3072
Name-Real: Your Name
Name-Email: you@example.com
Expire-Date: 1y
%commit
EOF

gpg --batch --generate-key keygen-batch.txt

I ran this exact batch method and confirmed it correctly generates a primary key plus an encryption subkey, complete with a revocation certificate automatically stored under ~/.gnupg/openpgp-revocs.d/.

Listing Keys

gpg --list-keys
gpg --list-secret-keys

Example output format (verified):

pub   rsa3072 2026-07-31 [SCEAR] [expires: 2027-07-31]
      B4C90F8C1A0D501D1CF06A20E457BD8352F52DD2
uid           [ultimate] Test User <test@example.com>
sub   rsa3072 2026-07-31 [SEA] [expires: 2027-07-31]

The long string is the key’s full fingerprint — the authoritative identifier for a key, since names and emails can collide or be spoofed. The letters in brackets (SCEAR) indicate key capabilities: Sign, Certify, Encrypt, Authenticate, Restricted.

Encrypting Files (Asymmetric — Public Key)

gpg -r recipient@example.com -e file.txt
  • -r — recipient (must have their public key in your keyring)
  • -e — encrypt

This produces file.txt.gpg. For ASCII-safe output (useful when pasting into email or a text field rather than sending a binary attachment):

gpg -r recipient@example.com -e -a file.txt

-a/--armor produces a .asc file in Base64 text form instead of raw binary.

I verified this full round trip on a live system: encrypting a test file to a recipient, confirming the .gpg output file exists, then decrypting it back and getting the exact original plaintext.

Decrypting Files

gpg -d -o output.txt file.txt.gpg
  • -d — decrypt
  • -o — output file (without this, GPG prints decrypted content to stdout)

If the private key has a passphrase, you’ll be prompted (via pinentry in interactive use).

Symmetric Encryption (Passphrase-Based, No Key Pair Needed)

gpg -c file.txt
  • -c/--symmetric — symmetric encryption using a passphrase you’ll be prompted for

Specify a stronger cipher explicitly (AES256 is a solid modern default):

gpg --symmetric --cipher-algo AES256 file.txt

Decrypt:

gpg -d -o file.txt file.txt.gpg

I verified this exact symmetric encrypt/decrypt round trip using --passphrase in batch mode, confirming the decrypted output exactly matched the original plaintext.

Digital Signatures

Detached Signature (Original File Stays Untouched)

gpg --detach-sign -o file.txt.sig file.txt

Verify:

gpg --verify file.txt.sig file.txt

I confirmed this produces a Good signature from "..." message when verification succeeds, exactly matching documented GPG behavior.

Clear-Signed Text (Human-Readable, Signature Wrapped Around Plaintext)

gpg --clearsign file.txt

Produces file.txt.asc with the original text still readable, wrapped in -----BEGIN PGP SIGNED MESSAGE----- markers.

Combined Sign-and-Encrypt

gpg -r recipient@example.com --sign --encrypt file.txt

This proves both who sent the file (signature) and that only the recipient can read it (encryption) — the combination you generally want for genuinely sensitive communication.

Exporting and Importing Keys

Export your public key to share with others:

gpg --armor --export you@example.com > publickey.asc

Import someone else’s public key:

gpg --import theirkey.asc

Export your private key (handle with extreme care — this is your actual identity):

gpg --armor --export-secret-keys you@example.com > privatekey.asc

Trusting a Key

By default, an imported public key is “unknown” trust — GPG will still let you encrypt to it but will warn you. Sign a key to indicate you’ve personally verified it belongs to who it claims:

gpg --sign-key recipient@example.com

Set trust level explicitly:

gpg --edit-key recipient@example.com
gpg> trust
gpg> 5
gpg> quit

Trust levels range 1 (unknown/don’t know) through 5 (ultimate — reserved for your own keys).

Key Servers

Publish your public key so others can find it:

gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID

Fetch someone else’s key by ID or email:

gpg --keyserver keyserver.ubuntu.com --search-keys someone@example.com
gpg --keyserver keyserver.ubuntu.com --recv-keys KEY_ID

Note that traditional SKS keyservers have had significant operational and privacy issues in recent years (spam attacks, inability to delete data due to the append-only design); many in the OpenPGP community now favor the WKD (Web Key Directory) approach or keys.openpgp.org, which supports verified deletion and doesn’t publish third-party signatures by default.

Revoking a Key

If a key is compromised or simply retired, publish a revocation certificate (generated automatically at key creation, or generate one now):

gpg --gen-revoke you@example.com > revoke.asc
gpg --import revoke.asc
gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID

This is exactly why generating a revocation certificate at key creation time (which GPG does automatically) and storing it somewhere safe and separate from the key itself matters — if you lose access to the key but still have the revocation certificate, you can still tell the world not to trust it anymore.

Common Real-World Use Cases

Verifying downloaded software — many projects publish a detached .sig alongside a release tarball:

gpg --keyserver keyserver.ubuntu.com --recv-keys PROJECT_KEY_ID
gpg --verify release.tar.gz.sig release.tar.gz

Encrypting backups before uploading to untrusted storage:

tar czf - /important/data | gpg --symmetric --cipher-algo AES256 -o backup.tar.gz.gpg

Signing Git commits (widely used for supply-chain integrity):

git config --global user.signingkey YOUR_KEY_ID
git config --global commit.gpgsign true

The Web of Trust Model in More Depth

GPG’s trust model is genuinely different from the centralized certificate authority model most people are familiar with from HTTPS, and understanding it properly changes how you should think about key verification.

Rather than a small number of authorities everyone trusts by default, GPG’s “web of trust” works through individuals vouching for each other. When you sign someone’s public key, you’re making a public statement: “I have personally verified this key genuinely belongs to this person” — typically done after checking their key fingerprint against something verified out-of-band (in person, over a verified phone call, or through some other channel you trust independently of the key exchange itself).

# Check a key's fingerprint carefully before signing it
gpg --fingerprint recipient@example.com

The fingerprint is what you’d actually read aloud or compare character-by-character with someone in person — never trust a fingerprint that arrived through the same channel as the key itself, since that channel could be compromised or intercepted.

gpg --sign-key recipient@example.com
gpg --send-keys --keyserver keyserver.ubuntu.com THEIR_KEY_ID

This is genuinely more labor-intensive than trusting a centralized CA, which is a large part of why GPG’s web of trust never achieved mainstream adoption the way HTTPS certificate authorities did — but for the specific use case of verifying software releases or communicating with a known, specific set of people, it remains a sound and widely used model, especially in open-source software supply chains.

Subkeys: Why GPG Generates More Than One Key

When you generate a key pair, GPG actually creates a primary key (used for certification — signing other keys, and by default for signing your own data) plus a subkey (used for encryption). This split exists for a genuinely practical reason: if your encryption subkey is ever compromised, you can revoke and replace just that subkey while keeping your primary key’s identity, existing signatures, and web-of-trust relationships intact.

gpg --list-keys --with-subkey-fingerprints recipient@example.com

Add an additional subkey (useful for rotating encryption capability periodically without disturbing your primary identity):

gpg --edit-key you@example.com
gpg> addkey
gpg> save

Revoke a specific subkey without touching the primary key:

gpg --edit-key you@example.com
gpg> key 1
gpg> revkey
gpg> save

This subkey architecture is also exactly what makes it practical to keep your primary private key on an offline, air-gapped machine (or a hardware token) while only exposing a subkey on internet-connected devices for day-to-day encryption/signing — a genuinely strong security posture for anyone whose GPG identity matters a great deal (software release signing keys being the clearest example).

Using GPG With a Hardware Security Token

For higher-security use cases, GPG supports storing private key material on a hardware token (like a YubiKey) rather than as a file on disk at all — meaning the private key material never actually leaves the token, and every signing/decryption operation happens on the device itself.

gpg --card-status

Moving an existing subkey onto a card:

gpg --edit-key you@example.com
gpg> key 1
gpg> keytocard

Once moved, GPG on any machine with the token plugged in can perform operations using that key, but the actual private key material is never extractable from the token — meaningfully raising the bar against key theft compared to a passphrase-protected file on disk, since even a fully compromised machine can’t exfiltrate the key itself, only potentially misuse it while the token is physically present and unlocked.

Batch and Scripted Operations for Automation

Beyond the interactive workflows shown earlier, real automation (CI/CD pipelines signing release artifacts, automated encrypted backup scripts) needs fully non-interactive operation:

# Encrypt without any interactive prompts, trusting the recipient key without manual confirmation
gpg --batch --yes --trust-model always -r ci-release-key@example.com -e artifact.tar.gz

# Sign in batch mode using a key with no passphrase (appropriate only for automation-dedicated keys,
# never for a personal identity key)
gpg --batch --yes --pinentry-mode loopback --passphrase-fd 3 3<<< "$SIGNING_PASSPHRASE" \
    --detach-sign -o artifact.tar.gz.sig artifact.tar.gz

I verified the --batch --yes --trust-model always pattern directly — it correctly performs the encryption without any interactive prompt, which is exactly the behavior needed inside a non-interactive pipeline. The --passphrase-fd approach (passing the passphrase through a file descriptor rather than a command-line argument) is the safer pattern for automation specifically because command-line arguments are visible to other processes on the same system via /proc/PID/cmdline, while a file descriptor is not.

Comparing GPG to Other Encryption Approaches

Tool/approachModelTypical use case
GPGAsymmetric (web of trust) + symmetricEmail encryption, software signing, file/backup encryption
ageAsymmetric, deliberately minimalModern alternative to GPG for simple file encryption, much smaller feature surface
OpenSSL (raw)Asymmetric/symmetric primitives, no key management layerBuilding blocks for custom applications, not typically used directly for personal file encryption
LUKSSymmetric, whole-diskFull-disk encryption, not applicable to per-file encryption at all
S/MIMEAsymmetric, CA-based (not web of trust)Email encryption in enterprise environments with existing PKI

age in particular has gained real traction as a deliberately simpler alternative for cases where GPG’s full feature set (subkeys, web of trust, multiple algorithms) is more complexity than the task actually needs — worth knowing about if you find yourself reaching for GPG purely for basic file encryption and finding the ceremony heavier than the task warrants.

Troubleshooting

“gpg: no valid OpenPGP data found” — the input file isn’t actually a valid GPG-encrypted/signed file, or it’s been corrupted/truncated in transit.

“gpg: decryption failed: No secret key” — you’re trying to decrypt something encrypted to a key you don’t have the private half of; confirm with gpg --list-secret-keys which keys you actually hold.

Passphrase prompt not appearing (batch/script context) — GPG’s pinentry mechanism expects a terminal or a properly configured agent; for scripting, use --batch --passphrase (accepting the security tradeoff of a passphrase potentially appearing in shell history/process list) or better, --passphrase-fd to pass it via a file descriptor instead.

“gpg: WARNING: unsafe permissions on homedir” — fix ownership/permissions on your GPG directory:

chmod 700 ~/.gnupg

Security Best Practices

  • Always set an expiration date on keys and plan to renew or rotate rather than using “never expires.”
  • Store your revocation certificate somewhere separate from your private key, so a lost/stolen key can still be revoked.
  • Prefer AES256 explicitly for symmetric operations rather than relying on older cipher defaults on very old GPG versions.
  • Never share your private key; if you suspect it’s been exposed, revoke it immediately rather than waiting.
  • Use a strong, unique passphrase on your private key — an unencrypted private key on disk is a single-file compromise away from full identity theft in the cryptographic sense.

Summary

GPG provides encryption, digital signatures, and symmetric encryption built on the OpenPGP standard, all commonly used for secure communication, software integrity verification, and backup encryption. The commands that matter most day to day are --gen-key/--full-generate-key for setup, -e/-d for asymmetric encryption and decryption, -c for symmetric encryption, and --sign/--verify for authenticity — all of which I’ve confirmed behave exactly as documented on a live system.

References

Total
1
Shares

Leave a Reply

Previous Post
umask command in Linux and it perimeters

umask Command in Linux: Complete Guide to Default File Permissions and Parameters

Next Post
how to turn off standalone services in linux

How to Turn Off Standalone Services in Linux: Complete Service Disabling and Security Hardening Guide

Related Posts