Protect your digital identity with PGP
Wanting to improve my privacy, I've been looking for reliable ways to encrypt and sign data I share to other people. At first, I naturally gravitated toward x509 certificates (the same technology that secures HTTPS and much of today's internet). While they work well in may scenarios, they also rely on a fairly centralized trust model built around certificate authorities.
That's what led me to PGP. Well known in the cybersecurity community, PGP takes a completely different approach where anyone can create their own cryptographic identity, publish their public key, and build trust without depending on a central authority. I found this decentralized model more elegant and empowering.
As an example, PGP was used by Edward Snowden to encrypt the NSA files transfered Laura Poitras who revealed them in the CITIZENFOUR documentary. It is also used by Proton Mail to automatically sign emails and ensure their authenticity.
In this guide, I'll talk through everything you need to get started with GnuPG (GPG), an open-source implementation of the OpenPGP standard. We'll cover how PGP works, how to generate and manage your keys securely, the commands you'll see every day, and how to publish your public key so others can securely communicate with you.
This guide is written for Linux users. If you're on Windows, you might want to consider changing your operating system before worrying about encrypting your data.
What is PGP ?
PGP stands for Pretty Good Privacy. It's an encryption standard (formalized as OpenPGP, RFC 4880) that provides three things:
- Encryption: so only the intended recipients can read a message or file
- Digital signatures: so a recipent can verify that a message or file really came from you and wasn't altered.
- Key management: a system of public/private key pairs, trust, and key servers that lets people find and verify each other's keys without a central authority.
PGP relies on asymetric cryptography. Each user has a key pair made of a public key (which you share with the world) and a private key (which you keep secret, usually protected by a passphrase).
- To encrypt a file for someone, you use their public key. Only their private key can decrypt it.
- To sign a file, you use your private key. Anyone with your public key can verify the signature.
Common use cases:
- Encrypting emails and files.
- Signing software releases, commits, and packages
- Signing/encryption sensitive files before storing or moving them
- Identity verification within decentralized "webs of trust".
GnuPG is the standard open-source tool that implements OpenPGP, and it's what we'll use throughout this guide.
Installing GnuPG on Linux
Most distributions ship with GPG preinstalled, however, here's how to install or update it manually.
Debian / Ubuntu
sudo apt update
sudo apt install gnupg gnupg2 scdaemon pcscd
scdaemon and pcscd are needed later if you plan to use a smart card or YubiKey.
Fedora / RHEL / CentOS
sudo dnf install gnupg2 gnupg2-smime pcsc-lite
Arch Linux
sudo pacman -S gnupg pcsc-tools
Verify the installation
gpg --version
You should see an output similar to:
gpg (GnuPG) 2.5.x
libgcrypt 1.12.x
Key management
Creating a PGP master key with associated subkeys
The best practice for a serious PGP setup is to have:
- A master key used to certify (issue/revoke) subkeys and sign other people's keys. This key must be kept offline on a secure drive and almost never touched.
- A signing subkey used for signing
- An encryption subkey used for decryption
- Optionnaly an authentication subkey usable for SSH authentication.
This way, if your day-to-day machine is compromised, only the subkeys are exposed, you can revoke and reissue them without losing your master identity.
Option A: Interractive method
gpg --expert --full-generate-key
- Choose
(8) RSA (set your own capabilities)
- Toggle off
Sign and Encrypt, leaving only Certify, this is your master key.
- Set key size (
4096) ans expiration
- Enter your User ID and passphrase.
Now add subkeys to that master key:
List your keys and retrieve your master's key ID (format: B4C44BFC...)
gpg --list-keys
gpg --expert --edit-key <MASTER_KEY_ID>
Inside the gpg> prompt:
gpg> addkey
- Chose
(4) RSA (sign only), set expiration and confirm. This becomes your signing subkey
- Repeat with
addkey again, chose (6) RSA (encrypt only), set expiration and confirm. This is your encryption subkey.
- Optionnaly, repeat once more choosing `(8) RSA (set your own capabilities), toggle only Authenticate on, for an authentication subkey.
Save and exit
gpg> save
Verify the structure
gpg --list-secret-keys --keyid-format=long
You should see one sec (certify-only master) and multiple ssb (subkeys) marked [S], [E] and [A] respectively.
Option B: Batch / parameters file method
For scripted or reproductive key generation, GnuPG supports an unattended mode driven by a parameters file.
Create params.txt file:
%echo Generating a master certify-only key
Key-Type: RSA
Key-Length: 4096
Key-Usage: cert
Subkey-Type: RSA
Subkey-Length: 4096
Subkey-Usage: sign
Name-Real: Joe Dohn
Name-Email: joe@dohn.com
Expire-Date: 1y
Passphrase: ChangeThisPassphrase!
%commit
%echo Master key with signing subkey generated
Generate the key:
gpg --batch --gen-key params.txt
This creates the master key plus one signing subkey in a single pass. To add the encryption and authentication subkeys afterward (batch mode doesn't support adding multiple subkeys types in one file), use the interactive --expert --edit-key + addkey steps shown above, or script it with --quick-add-key.
gpg --quick-add-key <MASTER_KEY_ID> rsa4096 encrypt 1y
gpg --quick-add-key <MASTER_KEY_ID> rsa4096 auth 1y
Important: Delete the parameters file afterward since it contains your passphrase in plaintext:
shred -u params.txt
Backing up the master key and moving it offline
Export everything before removing the master private key from your working machine:
Full backup (master + all subkeys), keep this offline and encrypted
gpg --export-secret-keys --armor <MASTER_KEY_ID> > master.asc
Revocation certificate, generate once and store safely, in case you lose the key
gpg --gen-revoke <MASTER_KEY_ID> > revoke-cert.asc
Subkeys only (safe to keep on your daily machine)
gpg --export-secret-subkeys --armor <MASTER_KEY_ID> > subkeys.asc
Then, on your daily machine, remove the master private key material (keeping only the subkeys) by deleting the secret key and re-importing subkeys.asc, or use gpg --edit-key with the keytocard / delkey workflow.
Adding a PGP key to a YubiKey
YubiKeys (and other OpenPGP-compatible smart cards) can store your subkeys in a tamper-resistant hardware, so the private key material never touches your computer's disk.
Prerequires
sudo apt install scdaemon pcscd yubikey-manager
Confirm the Yubikey is detected:
gpg --card-status
Move the subkeys to the Yubikey
Open your key for editing:
gpg --edit-key <KEY_ID>
Select and move each subkey one at a time:
gpg> key 1
gpg> keytocard
You'll be asked which slot to use (1) Signature key, (2) Encryption key or (3) Authentication key. Pick the one matching your subkey's usage.
Deselect the current key and repeat for the others:
gpg> key 1
gpg> keytocard
gpg> key1
gpg> key 2
gpg> keytocard
gpg> key2
gpg> key3
gpg> keytocard
gpg> save
keytocard moves (not copies) the private key material onto the card, make sure you already have your offline backup from the previous section before doing this, since the key is removed from the disk afterward.
Set PINs
gpg --card-edit
gpg/card> admin
gpg/card> passwd
Change the User PIN (used for everyday signing/decryption) and the Admin PIN (used for card management), and don't leave them at the factory defaults (123456/12345678).
Verify
Unplug and replug your Yubikey, then run:
gpg --card-status
gpg --list-secret-keys --keyid-format=long
Subkeys stored on the card will show ad ssb>(With a >) instead of ssb, indicating the private key lives on the mart card rather than on disk.
Useful commands
Signing a file
To create a detached signature (a separate .sigfile, leaving the original untouched):
gpg --detach-sign --armor document.pdf
This produces document.pdf.asc. Use --sign instead to embed the signed content into a single (binary) file, or --clearsign for a human-readable signed text block.
Verifying a signature
gpg --verify document.pdf.asc document.pdf
GPG reports whether the signature is valid and which key/user ID signed it.
If you don't yet have the signer's public key imported, you'll get an error asking you to import it first (see below)
Importing a person's public key into GPG
From a file:
gpg --import someones-public-key.asc
From a webserver
curl -fsSL https://pgp.nobell.fr/pgp.txt | gpg --import
From a keyserver, by key ID or fingerprint:
gpg --keyserver hkps://keys.opengpg.org --recv-keys <KEY_ID>
After importing, it's good practice to verify the fingerprint out-of-band (in person, over a call, etc..) and then sign the key to mark it as trusted:
gpg --list-keys
gpg --edit-key <KEY_ID>
gpg> sign
gpg> trust
gpg> save
Encrypting a file using a person's public key
gpg --encrypt --sign --armor --recipient jane@example.com document.pdf
Here is another example of encrypting a message without writing anything to a file:
echo "Hello World \!" | gpg --encrypt --armor --recipient jane@example.com
Output:
-----BEGIN PGP MESSAGE-----
hQIMAxhpgYFNamt+AQ/9HSC0NxRqx/k33kElNnPOu7Y8X3Tb3peecf075yyeZ0kl
JtSDdg8EjHVb8fQbCwKAQGg8eG8lAEy7bLAAnrLFscvZ+BWjs8ctwT5etiThkQdy
PwA5rt7jdl8FxhLFm0tva87r1hJpHv1Q0eC5UBWgM17KiZTsRnXQJ5ZtbG5B9+dI
wejyYJVpOVZQQX6lxcEeX0TBqRFNSkkvXiLIP1AQIPtmopenbIiVuLAaVYD1Oc0i
c/XB9B0BKrpFvf5mOzw/4jEDgStaFFJ+IXqdT6Oylttru0CaJyBxchqTykj/DF2S
V2yMwMuwlL8ndEMag9grAoKIXFf3DOe0a3+ML8QCmSsScdGM2LuJsUohIITWzXQ4
HSKhJVsoI7FaUF8i8KEzLgIVpA3UNGTuqwCnQmrrOZPsHa/WXig2OQnQt2bfAQWw
UJsdTvRUVzrayfokzAv5hOHkI4T6PzhVMB5O7JZa/GODV/wsu53YgbZn5BskwRoe
LM5xorbdvPs1XN5UvgmftzT7b5xf2N5zFZoMOsZsQbdwZx90svlWqEn1wWypr6RM
mZLPwk/h6k/h6c034Wyvr412bv3TcKMxvZDNysWOmEFDm6jv+MarPBWVIcpIypM8
/TJg5Y5AQvjE5BAH+mN6qAwcO8ACpV29mvrQ2OV7+PbuKnrcfq7b7H7a1ZlXmpPS
SAH9Y1K3nrylEs8wi8q0l+x8HG495PPCKuwko9q/SMUTOHyISyEhYhBtFdgm+KJ6
89GLWxKhQjcKUddulyLx88uqtZEDyPudwg==
=rNxh
-----END PGP MESSAGE-----
Decrypting a file
gpg --decrypt document.pdf.asc > document.pdf
GPG automatically finds the right private key in your keyring (or on your smart card) and prompts for your PIN/passphrase.
Publishing your PGP public key
A key is only useful for encryption/verification if other people can find it. There are two common ways to publish it:
- Uploading the public key on a key server
- Publishing the key on a webserver using the Web Key Directory (WKD) standard.
Uploading your public key to keys.opengpg.org
keys.openpgp.org is a modern keyserver that only publishes key material (not arbitrary third-party signatures), and required verifying email addresses before they're shown publicly, which reduces spam and abuse.
Export your public key:
gpg --export --armor <MASTER_KEY_ID> > public-key.asc
Upload it directly with gpg
gpg --keyserver hkps://keys.openpgp.org --send-keys <MASTER_KEY_ID>
Or upload manually through the web form at keys.opengpg.org/upload, pasting the contents of public-key.asc
Either way, keys.openpgp.org with email each user ID on the key verification link. Until you click it, that email address won't be searchable on the server (though the key itself is still published). Click on the link from your inbox to complete verification.
Anyone can then fetch your key by email or fingerprint:
gpg --keyserver hkps://keys.opengpg.org --search-keys joe@dohn.com
Setting up PGP WKD on a nginx Docker container
Web Key Directory (WKD) lets people fetch your public key automatically just from your email address's domain, no keyserver lookup is needed. If your email address is joe@dohn.com, WKD publishes your key at a predictable HTTPS path under openpgpkey.dohn.com, and GPG/Thunderbird can fetch it transparently. However, for this to work, you will need to have full control over your email domain.
1. Compute the WKD hash for your email's local part
WKD uses a Z-base-32 encoding of a SHA-1 hash of the lowercased local part of the email (the part before @):
gpg --with-wkd-hash -k joe@dohn.com
This prints something like:
[...]
uid [ultimate] Joe Dohn <joe@dohn.com>
iamnotarealhash123456789abcdefghijklmno@dohn.com
The string before the @ is your WKD hash.
2. Prepare the directory structure
This part will be done on a public server like a VPS, not on your PC locally.
Create the folder structure:
mkdir -p wkd_data/.well-known/openpgpkey/<yourdomain.tld>/hu
Export your key in the raw binary format WKD expects (no ASCII armor) and name it after the hash:
gpg --export joe@dohn.com > wkd_data/.well-known/openpgpkey/<yourdomain.tld>/hu/<hash>
Optionnally add a policy file (can be empty) that some clients check for:
touch wkd_data/.well-known/openpgpkey/<yourdomain.tld>/policy
3. Write the nginx configuration
Create site.conf
server {
listen 443 ssl;
server_name openpgpkey.<yourdomain.tld>;
ssl_certificate /etc/letsencrypt/live/openpgpkey.<yourdomain.tld>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/openpgpkey.<yourdomain.tld>/privkey.pem;
root /usr/share/nginx/html;
location /.well-known/openpgpkey/ {
default_type application/octet-stream;
add_header Access-Control-Allow-Origin *;
try_files $uri =404;
}
}
The Access-Control-Allow-Origin header is required by the WKD spec so browser-based clients can query it cross-origin.
4. Run the Docker container
Install docker if not present
curl -fsSL https://get.docker.com | sh
Create a docker-compose.yml file
services:
wkd:
image: nginx:alpine
container_name: wkd
restart: unless-stopped
ports:
- 80:80
- 443:443
volumes:
- ./wkd_data:/usr/share/nginx/html:ro
- ./site.conf:/etc/nginx/conf.d/site.conf:ro
- /etc/letsencrypt:/etc/letsencrypt:ro # You will need to generate certificates using certbot, ask GPT for a guide.
Give ownership to nginx user on the wkd_data directory:
chown -R 101:101 wkd_data
Make sure openpgpkey.<yourdomain.tld> has a valid TLS certificate (via Certbot for example) and a DNS record pointing to your VPS. WKD required HTTPS.
Launch the container
docker compose up -d
5. Test it
From another machine:
gpg --locate-keys joe@dohn.com
Or test the raw content directly:
curl -v "https://openpgpkey.<yourdomain.tld>/.well-known/openpgpkey/<yourdomain.tld>/hu/<hash>?l=joe"
If it returns your binary key data, WKD is working. Anyone in the world can now fetch your public key just by knowing your email address, with no keyserver required.
you can for example try to import my key:
gpg --locate-keys jan@nobell.fr
Wrapping up
At this point you have a working GnuPG install, a properly structured master key with subkeys (optionally hardware-backed on a YubiKey), the core day-to-day commands for signing/verifying/encrypting/decrypting, and two independent ways for others to discover your public key. That's a solid, production-grade PGP setup. From here, the main thing left is habit, sign your commits, verify what you import, and keep that master key backup somewhere safe and offline. Don't forget to talk about PGP to your tech friends to share encrypted data with them 🙂.
Thank's for reading.