Skip to the tool workspace
Browser 123

How-to Keys and encryption

How to verify a signed file or software release

Check a download against its .sig or .asc with gpg --verify, on a real GnuPG release. What Good signature, BAD signature, No public key and the not certified warning each mean, and where trust actually comes from.

A signed release comes as two files: the download itself and a small signature next to it, usually .sig or .asc. A third thing has to come from somewhere else: the project's public key, and a fingerprint you can trust. With all three, one command tells you whether the file is byte for byte what the key's owner signed.

The direct wayGnuPGLinux · macOS · Windows

If you can install GnuPG, use it. On Debian or Ubuntu it is sudo apt install gnupg, on a Mac brew install gnupg, and on Windows it comes with Gpg4win.

gpg --import signature_key.asc
gpg --verify gnupg-2.5.24.tar.bz2.sig gnupg-2.5.24.tar.bz2

Signature first, file second. Then compare the fingerprint it prints with the one the project publishes. That second step is the one that counts.

What you need, and where it comes from

Thing Example Where it comes from
The file gnupg-2.5.24.tar.bz2 The download page or any mirror
The signature gnupg-2.5.24.tar.bz2.sig Next to the file
The public key signature_key.asc The project's own site, its source repository, or a keyserver
The fingerprint 6DAA 6E64 A76D 2840 … Somewhere you trust that a mirror can't change

The signature can travel with the file because it is useless without the key. The key is where all the trust sits, so get it from the project itself, not from the mirror that served the download.

Verifying a real release: GnuPG 2.5.24

GnuPG signs its own tarballs. These are the files from https://gnupg.org/ftp/gcrypt/gnupg/, and the key file from https://gnupg.org/signature_key.asc, checked with GnuPG 2.4.7 on Debian 13.

curl -O https://gnupg.org/ftp/gcrypt/gnupg/gnupg-2.5.24.tar.bz2
curl -O https://gnupg.org/ftp/gcrypt/gnupg/gnupg-2.5.24.tar.bz2.sig
curl -O https://gnupg.org/signature_key.asc

Before you have the key

Run the check first, and gpg tells you which key it needs:

gpg --verify gnupg-2.5.24.tar.bz2.sig gnupg-2.5.24.tar.bz2
gpg: Signature made Wed Sep 23 11:40:27 2026 UTC
gpg:                using EDDSA key 6DAA6E64A76D2840571B4902528897B826403ADA
gpg: Can't check signature: No public key
gpg: Signature made Thu Sep 24 00:40:06 2026 UTC
gpg:                using EDDSA key AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD
gpg: Can't check signature: No public key

This release carries two signatures, from two maintainers. "No public key" is not a failure: nothing was checked, so it says nothing either way. The exit code is 2.

Import the key

gpg --import signature_key.asc
gpg: key 528897B826403ADA: public key "Werner Koch (dist signing 2020)" imported
gpg: key E98E9B2D19C6C8BD: public key "Niibe Yutaka (GnuPG Release Key)" imported
…
gpg: Total number processed: 6
gpg:               imported: 6

The file holds six keys, one per person or role that signs GnuPG releases. Four lines are left out above. Importing a key doesn't mean trusting it: anyone can make a key with any name on it.

Verify again

gpg --verify gnupg-2.5.24.tar.bz2.sig gnupg-2.5.24.tar.bz2
gpg: Signature made Wed Sep 23 11:40:27 2026 UTC
gpg:                using EDDSA key 6DAA6E64A76D2840571B4902528897B826403ADA
gpg: Good signature from "Werner Koch (dist signing 2020)" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 6DAA 6E64 A76D 2840 571B  4902 5288 97B8 2640 3ADA
gpg: Signature made Thu Sep 24 00:40:06 2026 UTC
gpg:                using EDDSA key AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD
gpg: Good signature from "Niibe Yutaka (GnuPG Release Key)" [unknown]
gpg: Signature notation: manu=2,2.5+1.12,2,2
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: AC8E 115B F73E 2D8D 47FA  9908 E98E 9B2D 19C6 C8BD

Exit code 0. Two lines matter here, and they say different things.

Good signature, and the warning under it

"Good signature" is the maths. The file hasn't changed by a single byte since it was signed, and it was signed by the private key matching the public key you imported.

"WARNING: This key is not certified with a trusted signature!" is about the key, not the file. GnuPG has no record that this key belongs to Werner Koch, because nothing in your keyring vouches for it. The [unknown] after the name says the same. Everybody sees this warning the first time, with every project. It doesn't mean something is wrong. It means one check is still yours to do.

That check is the fingerprint. If an attacker replaced the tarball on a mirror, they could replace the .sig and serve their own key too, under the same name, and gpg would print "Good signature" for that just as happily. What they can't do is make their key's fingerprint match the real one.

Establish the key, once
  1. Find the fingerprint somewhere other than the place you got the key. For GnuPG, its signature key page lists each key with its fingerprint. Other projects put it in their README, their security page or their documentation.

  2. Compare it, all 40 characters, with the Primary key fingerprint line. Not the short key ID: those can be forged.

  3. If it matches, tell GnuPG you checked, with your own key (make one first if you have none):

    gpg --quick-lsign-key 6DAA6E64A76D2840571B4902528897B826403ADA
    gpg --quick-lsign-key AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD
    

    A local signature stays in your keyring and is never published.

After that, the same command's Good signature lines read:

gpg: Good signature from "Werner Koch (dist signing 2020)" [full]
gpg: Good signature from "Niibe Yutaka (GnuPG Release Key)" [full]

with no warning under them, for this release and every later one signed by those keys. Skipping the local signature is fine too, as long as you read the fingerprint each time.

A website is only as good as its HTTPS

Getting the key and the fingerprint from the same site you downloaded from is weaker than it looks: whoever controls the site controls both. It still beats a mirror, and it beats no check at all. The strongest sources are the ones an attacker would have to compromise separately: a key bundled with the previous release you already trust, a signed announcement, the project's repository, or a maintainer in person.

When it fails

To see a failure, one byte of a copy of the tarball was changed:

gpg --verify gnupg-2.5.24.tar.bz2.sig tampered.tar.bz2
gpg: Signature made Wed Sep 23 11:40:27 2026 UTC
gpg:                using EDDSA key 6DAA6E64A76D2840571B4902528897B826403ADA
gpg: BAD signature from "Werner Koch (dist signing 2020)" [unknown]
gpg: Signature made Thu Sep 24 00:40:06 2026 UTC
gpg:                using EDDSA key AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD
gpg: BAD signature from "Niibe Yutaka (GnuPG Release Key)" [unknown]

Exit code 1. One changed byte in 8.4 MB is enough. A BAD signature means the file isn't the one that was signed: a cut-off download, a different build or version, an archive someone repacked, or an attack. Download it again from the project's own site before you assume the worst, and don't run it in the meantime.

gpg says Exit code Means Do this
Good signature and the not certified warning 0 The file matches the key Compare the fingerprint with the published one
Good signature with [full] or [ultimate] 0 The file matches a key you already checked Nothing. You're done
BAD signature 1 The file, or the signature, has changed Download again; don't use it
Can't check signature: No public key 2 Nothing was checked Get the key it names, check its fingerprint, import it

If you run only the signature, gpg --verify gnupg-2.5.24.tar.bz2.sig, gpg looks for the file by dropping .sig and says so (assuming signed data in 'gnupg-2.5.24.tar.bz2'). Naming both is clearer.

When the project signs a checksum file instead

Many projects sign one list of hashes rather than every file. Node.js does: each release has a SHASUMS256.txt, a detached SHASUMS256.txt.sig, and a clearsigned SHASUMS256.txt.asc. Its README lists the release keys by fingerprint and gives a keyserver command for each. Checked here on the v26.12.0 release:

gpg --keyserver hkps://keys.openpgp.org --recv-keys C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C
gpg --verify SHASUMS256.txt.sig SHASUMS256.txt
gpg: Signature made Wed Oct 14 15:21:34 2026 UTC
gpg:                using RSA key C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C
gpg: Good signature from "Richard Lau <[email protected]>" [unknown]
gpg:                 aka "Richard Lau <[email protected]>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: C82F A3AE 1CBE DC6B E46B  9360 C43C EC45 C17A B93C

That proves the list. Then check your download against it. The list covers 34 files, so tell the checker to skip the ones you didn't download:

sha256sum -c --ignore-missing SHASUMS256.txt        # Linux
shasum -a 256 -c --ignore-missing SHASUMS256.txt    # macOS
node-v26.12.0-headers.tar.gz: OK

Both steps are needed: the signature on its own says nothing about your file, and the hash on its own says nothing about who published it. Checking hashes, including on Windows, has its own guide: check a file against its checksum.

Verifying the .asc does not verify the .txt next to it

gpg --verify SHASUMS256.txt.asc checks the text inside the .asc, and says so when a file of the same name sits beside it: WARNING: not a detached signature; file 'SHASUMS256.txt' was NOT verified! When both are offered, verify the detached .sig against the .txt, and check hashes against that same .txt.

Detached, clearsigned or inline

A signature comes in three shapes, and you can tell them apart by looking.

Shape Made with Looks like Verify with
Detached gpg --detach-sign (-b) A separate .sig, or .asc starting BEGIN PGP SIGNATURE gpg --verify file.sig file
Clearsigned gpg --clearsign Readable text between BEGIN PGP SIGNED MESSAGE and the signature gpg --verify file.asc
Inline gpg --sign (-s) One binary .gpg holding the data and the signature gpg --verify file.gpg, then gpg -d -o file file.gpg to get the data out

Releases are almost always detached, because the download stays untouched: the tarball you check is the tarball you unpack. Clearsigning suits text people read anyway, such as checksum lists and security advisories. Inline signing wraps the data, so you can't use the file until you unwrap it.

In a script

gpg --verify exits 0 on a good signature from any key in your keyring, with or without the warning. In a build script or a Dockerfile, use gpgv with a keyring holding only the keys you checked:

gpg --dearmor < signature_key.asc > gnupg-keys.gpg
gpgv --keyring ./gnupg-keys.gpg gnupg-2.5.24.tar.bz2.sig gnupg-2.5.24.tar.bz2
gpgv: Signature made Wed Sep 23 11:40:27 2026 UTC
gpgv:                using EDDSA key 6DAA6E64A76D2840571B4902528897B826403ADA
gpgv: Good signature from "Werner Koch (dist signing 2020)"
gpgv: Signature made Thu Sep 24 00:40:06 2026 UTC
gpgv:                using EDDSA key AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD
gpgv: Good signature from "Niibe Yutaka (GnuPG Release Key)"

It exits 1 on a BAD signature, so a script running with set -e stops there, and no other key you happen to hold can satisfy it. Commit the keyring file next to the script, after checking its fingerprints once.

In the browser

The same check works in a tab, with nothing uploaded. Keep the project's key in this browser first, then verify against it.

Keep the project's key
  1. Drop the key file on PGP Import Keys, or paste it under Key block.
  2. Turn on Keep this key in this browser, then press Read this key. Every key in the block is listed with its full fingerprint (GnuPG's signature_key.asc reads as six).
  3. Compare the fingerprints with the published ones. Stop here if they don't match.
  4. Press Import this key (for a block of several, the button gives the number of keys).
Check the download
  1. Drop the download on PGP Verify File and the .sig or .asc on Drop the signature file.
  2. Press Verify file.
  3. Read the headline. Valid signature from and a name is GnuPG's Good signature. Check that the fingerprint under Signed by is the one the project publishes: like gpg --verify, the tab accepts a signature from any key you keep. Signature is not valid is GnuPG's BAD signature.
PGP Verify File after checking example-tool-1.4.2.tar.gz against its detached signature: the headline Valid signature from Example Tool Releases, and the Signature, Signed by with the full fingerprint, Key status and Signed at rows below it

A release and its .sig, checked in the browser against the release key.

There is no separate step for vouching for a key, and no "not certified" warning: a key you kept is a key the tab checks against. So the fingerprint comparison before you import is the whole of that step. Keys can't be kept in a private window or with site data blocked for this site, and then there is nothing to verify against.

Without the signer's key, the tab says "Signature could not be checked", shown as a warning rather than a failure, with the key ID the signature claims: the last 16 characters of the fingerprint gpg prints in its using … key line.

PGP Verify File with no matching key: the headline Signature could not be checked, the Signature row reading Not checked, Signed by reading Not in your keyring with the Claimed key ID C38FCC8B40E3C002, and an Import the signer's key link

No key, so nothing was checked. The tool names the key it needs.

Its Import the signer's key link opens PGP Import Keys. "Not checked" is the honest answer here, like gpg's "No public key": it says nothing either way about the file.

Then use the file

With a good signature from a fingerprint you checked, the file is the one the project released. Unpack it, install it, or pass it on with the .sig beside it, so the next person can run the same check. Keep the imported key, in GnuPG or in the browser: the next release will most likely be signed by the same one, and the check takes a second.

Questions people ask

gpg says Good signature but also WARNING: This key is not certified with a trusted signature. Is the file safe?

The file is exactly what that key signed. The warning says GnuPG has no record that the key belongs to the person it names, because you haven't told it so. Compare the Primary key fingerprint line with the fingerprint the project publishes. If they match, the check is done. To make the warning go away for that key, sign it locally with gpg --quick-lsign-key.

What does Can't check signature: No public key mean?

The signature is fine as far as anyone knows. You don't have the key that made it, so nothing was checked. The line above it names the key's fingerprint. Get that key from the project's website, check its fingerprint, import it and run the same command again.

What is the difference between a .sig and an .asc file?

Usually only the encoding. A .sig is normally a binary signature and an .asc is the same thing as ASCII-armored text, starting with BEGIN PGP SIGNATURE. Both are verified with gpg --verify signature file. An .asc can also be a clearsigned message that carries its own text, which is verified on its own.

The project only publishes a SHA256SUMS file. Is a checksum as good as a signature?

Only if it is signed, or published somewhere more trustworthy than the download. A checksum next to the file on the same mirror proves the download wasn't corrupted, not that nobody swapped both. A signed checksum file gives you both: verify its signature, then check the file's hash against it.

Do I need the signer's private key?

No, and you should never have it. Verifying needs only the public key. The private key stays with whoever signs the releases.

Tools in this guide

Everything runs on your device

All guides

/
123 items
Runs in your browser·Your files stay on this device
Out0 B
Ready—