Encrypt, Sign and Verify Files Safely with gpg

A signature only proves anything once you check the fingerprint through a second channel yourself. This guide encrypts a file with a passphrase, creates a detached OpenPGP signature, and verifies it against the original file, using GnuPG 2.4.4, installed here as gpg and gpg2.

Allow about 15 minutes if you already have a signing key, longer if you need to create one. You need a shell, the gpg package, and a file that is safe to use for testing. These examples do not require elevated privileges. Do not put a real passphrase in a shell history, a shared script, or a process list.

1. Check the installed command

Confirm which binary will run and record its version:

$ command -v gpg
/usr/bin/gpg
$ gpg --version | head -n 1
gpg (GnuPG) 2.4.4

gpg2 is an alias on this machine, but scripts should normally use gpg. GnuPG reads its configuration and keys from ~/.gnupg unless you select another home directory, so the examples below use your existing keyring only where explicitly stated.

Checkpoint: if gpg --version fails, install or repair the package through your normal system administration process before continuing. Do not work around a missing binary by copying an unrelated executable.

2. Encrypt one file with a passphrase

Symmetric encryption uses one passphrase for both encryption and decryption. Create a disposable input file, then encrypt it to a new destination:

$ printf '%s\n' 'Example message for GnuPG.' > notes.txt
$ gpg --symmetric --output notes.txt.gpg notes.txt
Enter passphrase: type a strong passphrase
Repeat passphrase: type it again
$ file notes.txt.gpg
notes.txt.gpg: PGP symmetric key encrypted data

The original notes.txt remains in place. The encrypted output is binary by default, which is fine for a file but not for pasting into email or a ticket; add --armor when you specifically need ASCII-armoured output, for example notes.txt.asc.

Warning: encryption does not prove who created a file. Anyone who knows the passphrase can create another encrypted file that looks just as legitimate. If provenance matters, sign the data as well.

3. Decrypt to a deliberate output path

Decrypt into a new file so a failed operation does not replace your only copy:

$ gpg --decrypt --output notes.recovered.txt notes.txt.gpg
Enter passphrase: type the same passphrase
gpg: AES256.CFB encrypted data
gpg: encrypted with 1 passphrase
$ cmp --silent notes.txt notes.recovered.txt && echo 'recovered file matches'
recovered file matches

--decrypt writes to standard output when --output is omitted, which is useful in a pipeline but harder to inspect than an explicit destination. GnuPG never uses the filename embedded in an encrypted message when this command is used.

If the passphrase is wrong, the command fails and the recovered file may be absent or incomplete. Keep the original encrypted file until the comparison succeeds. If you no longer need the test files, remove only the specific files you created after checking their paths. Do not delete your keyring or an entire home directory as a clean-up shortcut.

4. Inspect keys and fingerprints

Public keys identify recipients and signing keys. List the keys known to your account, then show fingerprints:

$ gpg --list-keys
$ gpg --fingerprint
$ gpg --list-secret-keys

An empty listing is valid if this account has no keys. A fingerprint is the value to compare through a separate trusted channel before importing or trusting somebody else's key. Do not identify a key solely by a short key ID or a display name.

The normal listing is for people, not parsers. For scripts, use the stable colon format:

$ gpg --with-colons --list-keys

Tip: for unattended processing, the manpage recommends combining --batch with --status-fd and --with-colons. Do not parse translated human-readable output.

5. Create a detached signature

This step needs a secret signing key. Replace YOUR-KEY-FINGERPRINT with a fingerprint from gpg --list-secret-keys --fingerprint:

$ gpg --armor --detach-sign \
    --local-user YOUR-KEY-FINGERPRINT \
    --output notes.txt.asc notes.txt
Enter passphrase: type the signing-key passphrase
$ head -n 1 notes.txt.asc
-----BEGIN PGP SIGNATURE-----

A detached signature does not contain a copy of the data. Keep notes.txt beside notes.txt.asc and transfer both files. ASCII armour makes the signature text-friendly; it does not make it any less sensitive to tampering.

Warning: exporting a secret key is a security-sensitive operation. Do not use --export-secret-keys merely to move a signature between machines. Prefer a protected key or smartcard, and keep secret-key backups offline and controlled.

6. Verify the signature with both filenames

Always provide the signature and the exact data it is meant to cover:

$ gpg --verify notes.txt.asc notes.txt
gpg: Good signature from "Example signer" [unknown]

The wording and trust label vary with the keyring. What matters is a good signature from the expected fingerprint and signer. A good cryptographic signature alone does not establish that the key belongs to the person or project you expected, so compare the fingerprint and review the key's trust context.

Do not run gpg --verify notes.txt.asc alone and rely on GnuPG guessing the data file. The manual warns against that historical suffix-matching behaviour. If the data was read from standard input, pass - explicitly as the second filename.

7. Make verification scriptable

Human output can change with locale and release. A script should separate status output from normal output and record the process status:

status_file=$(mktemp)
if gpg --batch --status-fd 1 --verify notes.txt.asc notes.txt >"$status_file" 2>gpg.verify.log; then
    grep -q '^\[GNUPG:\] VALIDSIG ' "$status_file" && printf '%s\n' 'signature is cryptographically valid'
else
    printf '%s\n' 'signature verification failed' >&2
    exit 1
fi

This is only a starting pattern. A production program should consume the documented status protocol, check the full fingerprint, and handle missing keys, bad signatures and operational errors separately. Remove the temporary status file after the script has consumed it. Never treat a successful process exit as proof that the signature came from a particular person without checking the signer identity.

Done means