Verify OpenPGP Files Safely with gpgv

A lot of software distribution boils down to one question: does this file really come from the project it claims to? gpgv answers it, verifying an OpenPGP signature against a keyring you name explicitly and handing you an exit status a script can trust. Allow about ten minutes for a normal package check, plus more time to obtain the trusted keyring from the project's own documented source. This guide describes the gpgv installed here as GnuPG 2.4.4.

1. Check the installed command

gpgv is the verification-only companion to gpg. It does not create signatures or manage keys. It reads a signed file, or a detached signature alongside the data, and returns a status that automation can use.

$ command -v gpgv
/usr/bin/gpgv
$ gpgv --version
gpgv (GnuPG) 2.4.4
libgcrypt 1.10.3

The exact library version and copyright lines can differ. The useful checkpoint is that the command exists and its version is known before you compare its output with a project's instructions.

Checkpoint: You have the verifier's path and version. No elevated privileges are needed to inspect the command or verify a file in a directory you can already read.

2. Obtain the data, signature and trusted key

A detached signature normally comes as two files: the signed data, such as package.tar.xz, and a signature such as package.tar.xz.asc or package.tar.xz.sig. Get both from the same release source. Get the public key or trusted keyring from the project's documented channel, and compare its fingerprint through a second trusted channel where the project offers one.

The key is a trust input, not a detail to grab from any mirror that happens to be handy. A successful check proves the signature matches one of the keys in the keyring you supplied. It does not, by itself, prove that key belongs to the project unless you authenticated it separately.

Use a path that you can inspect and control. For the examples below, replace the three placeholders with real paths:

$ ls -l /path/to/package.tar.xz /path/to/package.tar.xz.asc /path/to/project-trustedkeys.gpg
$ sha256sum /path/to/project-trustedkeys.gpg
<compare this fingerprint or checksum with the project's published value>

Do not paste a checksum or fingerprint into a command merely because it turned up in an untrusted download directory. That comparison is a human trust decision; gpgv cannot make it for you.

3. Verify a detached signature with an explicit keyring

Pass the signature first and the data file second. The --keyring option tells gpgv which allowed public keys it may use:

$ gpgv --keyring /path/to/project-trustedkeys.gpg \
    /path/to/package.tar.xz.asc /path/to/package.tar.xz
gpgv: Signature made ...
gpgv:                using RSA key <fingerprint>
gpgv: Good signature from "<project identity>"

The timestamp, algorithm, fingerprint and identity are data from your signature, so they vary. The decisive human-readable result is Good signature, and the decisive machine-readable result is exit status 0.

$ printf 'exit status: %s\n' "$?"
exit status: 0

Keep the keyring path explicit in scripts. That also stops an accidental dependency on whichever default keyring happens to exist in the invoking account's GnuPG home.

Checkpoint: The output says Good signature and the command returned 0. You have verified the exact data file named in the command, not merely a similarly named one sitting next to it.

4. Understand the default keyring trap

If you omit --keyring, this version looks in the GnuPG home directory for trustedkeys.kbx, preferably, or trustedkeys.gpg. The home is normally ~/.gnupg, or the directory selected by GNUPGHOME or --homedir. The moment you add any --keyring option, gpgv stops looking for that default keyring entirely.

That is what makes two commands with similar wording behave differently:

$ GNUPGHOME=/path/to/verification-home gpgv /path/to/package.tar.xz.asc /path/to/package.tar.xz
$ gpgv --keyring /path/to/first.gpg --keyring /path/to/second.gpg \
    /path/to/package.tar.xz.asc /path/to/package.tar.xz

The first command uses the default keyring in the selected home. The second uses both named keyrings and does not add the default keyring on top. Use multiple --keyring options only when you have a clear reason to combine trusted key sets.

There are no gpgv configuration files. Do not troubleshoot a missing key by copying random files into ~/.gnupg. Inspect the path, keyring format and fingerprint first. Changing a shared home directory can affect other GnuPG operations, so normally use an explicit, dedicated keyring for a repeatable verification job.

5. Read failures as security results

A missing key is not a failed signature. It means the verifier cannot establish the signature with the keyring it was given:

$ gpgv --keyring /path/to/wrong-or-incomplete.gpg \
    /path/to/package.tar.xz.asc /path/to/package.tar.xz
gpgv: Can't check signature: No public key
$ printf 'exit status: %s\n' "$?"
exit status: 2

The wording can vary, and fatal errors use error codes other than 0 or 1. A missing key is never an invitation to fetch an unverified replacement: go find the project's official key distribution and check its fingerprint instead.

If the data changed after signing, the command reports BAD signature and returns 1:

$ gpgv --keyring /path/to/project-trustedkeys.gpg \
    /path/to/package.tar.xz.asc /path/to/package.tar.xz
gpgv: BAD signature from "<project identity>"
$ printf 'exit status: %s\n' "$?"
exit status: 1

Warning: do not install or unpack the file after a bad signature. Re-download both files, check the paths are correct, and investigate the release source if the result persists.

One version-specific boundary matters here: gpgv assumes every key in its keyring is trusted, and does not check whether those keys are expired or revoked. That is exactly why keyring provenance and fingerprint verification belong before this command runs, not after.

6. Verify attached and stdin forms when required

For an attached OpenPGP file, pass the single signed file:

$ gpgv --keyring /path/to/project-trustedkeys.gpg /path/to/signed-document.asc

For a detached signature, the data argument may be - when the signed data arrives on standard input:

$ fetch-command | gpgv --keyring /path/to/project-trustedkeys.gpg \
    /path/to/package.tar.xz.asc -
gpgv: Good signature from "<project identity>"

Keep the pipeline's exit status visible in scripts. A shell pipeline can otherwise report the status of the last command rather than the verifier. Use your shell's pipeline-status mechanism, or write the data to a named temporary file and verify that file directly when auditability matters.

7. Avoid accidental output changes

Verification normally reports to standard error and leaves the input untouched. The optional --output option is for extracting signed text; it is usually not useful for detached signatures. The manpage warns that an existing output file gets overwritten.

Do not add --output to a routine detached-signature check. If you deliberately extract an attached cleartext or binary message, choose a new destination first and check it does not already exist. Treat overwriting an existing file as irreversible, and make a backup before proceeding.

Done means