Skip to content

GPG and File Encryption

GPG (GNU Privacy Guard) is the open-source implementation of the OpenPGP standard (RFC 4880). It provides encryption, decryption, digital signing, and key management using asymmetric cryptography. Unlike PKI/TLS which relies on Certificate Authorities, GPG uses a web of trust model where individuals vouch for each other’s keys.


PKI (TLS/HTTPS): GPG (OpenPGP):
Trust anchor = Certificate Trust anchor = Key signatures
Authority (CA) from people you already trust
CA signs your cert You sign someone's key (after
→ everyone trusts you verifying their identity in person)
because they trust the CA → people who trust you now have
a reason to trust their key too
Centralised trust hierarchy Decentralised web of trust
Best for: Best for:
- HTTPS/TLS for websites - Email encryption (S/MIME alternative)
- Machine-to-machine auth - File encryption and signing
- Short-lived certs (ACME) - Code signing (Git commits, packages)
- Sensitive document exchange

Terminal window
# Install GPG
apt install gnupg # Debian/Ubuntu
brew install gnupg # macOS
# Generate a new key pair (interactive)
gpg --gen-key
# Choose: RSA and RSA (sign + encrypt), 4096 bits, 2 years expiry
# Generate with full customisation (expert mode)
gpg --full-generate-key
# Options: RSA/DSA/Ed25519+CV25519, key size, expiry date, UID
# Modern: use ed25519 + cv25519 (fast, compact, strong)
gpg --expert --full-generate-key
# Choose: (9) ECC and ECC → (1) Curve 25519 for signing
# Then add encryption subkey (10) ECC → (12) Curve 25519
# List your keys
gpg --list-keys # public keys
gpg --list-secret-keys # private keys
gpg --list-keys --keyid-format long # show full key ID
gpg --list-keys --fingerprint # show fingerprint (use to verify identity)
GPG key structure:
Primary key (master key)
→ Used for: certifying other keys (signing), identity
→ Should have long expiry or no expiry
→ Keep offline / on a hardware key for safety
Subkeys
→ Signing subkey: used for signing messages/commits
→ Encryption subkey: used for encrypting messages to you
→ Authentication subkey: used for SSH authentication
→ Subkeys can have short expiry (1 year); rotate independently
→ If a subkey is compromised, revoke and replace without losing your identity

Terminal window
# Export your public key (share this with others)
gpg --armor --export [email protected] > alice-public.asc
# --armor = ASCII text output (base64-encoded; safe to paste anywhere)
# Export private key (keep this secure - encrypted with your passphrase)
gpg --armor --export-secret-keys [email protected] > alice-private-KEEP-SAFE.asc
# Import someone else's public key
gpg --import bob-public.asc
# Import from a key server
gpg --keyserver keyserver.ubuntu.com --recv-keys 0xABCD1234DEADBEEF
gpg --keyserver keys.openpgp.org --search-keys [email protected]
# Upload your public key to a key server
gpg --keyserver keyserver.ubuntu.com --send-keys [email protected]
# Generate a revocation certificate (do this immediately after key creation)
gpg --gen-revoke [email protected] > alice-revocation.asc
# Store this securely - if your private key is compromised, publish this revocation
# Revoke a key (when compromised or abandoned)
gpg --import alice-revocation.asc
gpg --keyserver keyserver.ubuntu.com --send-keys [email protected]
Terminal window
# Verify someone's fingerprint (in person, over phone, etc.)
gpg --fingerprint [email protected]
# Fingerprint: A1B2 C3D4 E5F6 ...
# → Bob reads this to you verbally; if it matches, you know the key is his
# Sign their key (adds your certification)
gpg --sign-key [email protected] # local signature (not exportable)
gpg --lsign-key [email protected] # exportable signature
# Set trust level (controls automatic trust decisions)
gpg --edit-key [email protected]
gpg> trust
# 5 = ultimate (your own keys only)
# 4 = full (you've verified thoroughly)
# 3 = marginal (somewhat verified)
# 2 = don't trust
gpg> save
# View the web of trust for a key
gpg --check-sigs [email protected]

Terminal window
# Encrypt a file for a recipient (only they can decrypt with their private key)
gpg --armor --encrypt --recipient [email protected] secret.txt
# Creates: secret.txt.asc
# Encrypt for multiple recipients (any of them can decrypt)
gpg --armor --encrypt \
--recipient [email protected] \
--recipient [email protected] \
secret.txt
# Encrypt with a passphrase (symmetric - no key needed to decrypt, just the password)
gpg --armor --symmetric secret.txt
# Uses AES-256 by default; prompts for passphrase
# Encrypt to yourself (useful for personal encrypted storage)
gpg --armor --encrypt --recipient [email protected] private-notes.txt
# Encrypt and sign in one step (recipient gets encrypted + signed content)
gpg --armor --encrypt --sign --recipient [email protected] document.txt
Terminal window
# Decrypt a file (prompts for your private key passphrase)
gpg --decrypt secret.txt.asc
gpg --output secret.txt --decrypt secret.txt.asc # write to file
# Decrypt and verify signature simultaneously
gpg --decrypt --output document.txt signed-encrypted.txt.asc

Terminal window
# Sign a file (creates a detached .sig file alongside original)
gpg --armor --detach-sign file.tar.gz
# Creates: file.tar.gz.asc (the signature)
# Distribute: file.tar.gz + file.tar.gz.asc together
# Inline signature (signature embedded in the file - ASCII)
gpg --armor --sign message.txt
# Creates: message.txt.asc (contains original content + signature)
# Clearsign (human-readable content + signature - for emails/text)
gpg --clearsign announcement.txt
# Creates: announcement.txt.asc (readable content headers + sig block at bottom)
Terminal window
# Verify a detached signature
gpg --verify file.tar.gz.asc file.tar.gz
# Good signature: "Good signature from 'Alice <[email protected]>'"
# Bad signature: "BAD signature" - file was modified after signing
# Verify and extract an inline-signed file
gpg --output original.txt --decrypt signed.txt.asc
# Verify a clearsigned file
gpg --verify announcement.txt.asc
# What GPG checks when verifying:
# 1. Signature is mathematically valid (not tampered)
# 2. Signing key is known (in your keyring)
# 3. Signing key hasn't been revoked
# 4. Signing key hasn't expired
# Warning if signer's key is not trusted via web of trust
Terminal window
# Example: verifying a Debian ISO
# 1. Import the Debian project key
gpg --keyserver keyring.debian.org --recv-keys DF9B9C49EAA9298432589D76DA87E80D6294BE9B
# 2. Verify the checksums file is signed by Debian
gpg --verify SHA512SUMS.sign SHA512SUMS
# 3. Verify the ISO matches the checksum
sha512sum -c SHA512SUMS 2>/dev/null | grep debian-12.iso

Terminal window
# Configure GPG key for Git signing
gpg --list-secret-keys --keyid-format long
# Copy the key ID: e.g., 3AA5C34371567BD2
git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true # sign all commits by default
git config --global tag.gpgsign true # sign all tags
# Sign a specific commit (if not configured globally)
git commit -S -m "feat: add login endpoint"
# Sign a tag
git tag -s v1.0.0 -m "Release 1.0.0"
# Verify a commit signature
git log --show-signature -1
git verify-commit HEAD
# Verify a tag
git verify-tag v1.0.0
# GitHub: add your GPG public key to your account
# Settings → SSH and GPG keys → New GPG key → paste output of:
gpg --armor --export [email protected]

Terminal window
# Use your GPG authentication subkey as an SSH key
# Enable gpg-agent SSH support in ~/.gnupg/gpg-agent.conf:
echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf
echo "default-cache-ttl-ssh 3600" >> ~/.gnupg/gpg-agent.conf
# Set SSH_AUTH_SOCK to use gpg-agent instead of ssh-agent
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent
# List the SSH public key derived from your GPG auth subkey
gpg-connect-agent 'keyinfo --ssh-list --ssh-fpr' /bye
ssh-add -L # shows the GPG auth key as an SSH public key
# Add it to a server's authorized_keys:
ssh-add -L >> ~/.ssh/authorized_keys # (on the target server)
# Or use yubikey with GPG for hardware-backed SSH
# (same workflow - gpg-agent handles the hardware key interaction)

Terminal window
# Move primary key off computer to a hardware key (YubiKey)
# First, generate keys on the computer, then transfer:
gpg --edit-key [email protected]
gpg> keytocard # moves primary key to card
gpg> key 1 # select subkey 1
gpg> keytocard # moves signing subkey to card
# Repeat for encryption and auth subkeys
# After transfer, private key stubs remain on computer
# Actual private key operations happen on the hardware key
# YubiKey requires PIN entry per session (can't be extracted)
# Check card status
gpg --card-status
# Set PIN (default: 123456 for user PIN, 12345678 for admin PIN)
gpg --card-edit
gpg/card> passwd # change PIN

Terminal window
# Scenario 1: Send an encrypted + signed file to Bob
gpg --armor --encrypt --sign --recipient [email protected] --sender [email protected] report.pdf
# → sends report.pdf.asc to Bob
# Scenario 2: Encrypt an archive for backup (symmetric, passphrase)
tar czf - /home/alice/documents | gpg --symmetric --armor > documents-backup.tar.gz.asc
# Decrypt:
gpg --decrypt documents-backup.tar.gz.asc | tar xzf -
# Scenario 3: Verify a downloaded package's signature (RPM)
rpm --import https://repo.example.com/RPM-GPG-KEY-example
rpm --checksig package.rpm # should say "signatures OK"
# Scenario 4: Sign a release tarball (for a software project)
gpg --armor --detach-sign myproject-1.0.0.tar.gz
sha256sum myproject-1.0.0.tar.gz > myproject-1.0.0.tar.gz.sha256
# Publish: myproject-1.0.0.tar.gz + .asc + .sha256
# Scenario 5: Encrypt multiple files into a single archive
tar czf - secret1.txt secret2.txt | gpg --symmetric --armor > secrets.tar.gz.asc