A matching checksum proves that a file is the same as the one the checksum describes, nothing more, and many teams treat it as proof that the software is genuine. Attackers who can change a download page can usually change the hash printed beside it, and a signature from an unknown key proves as little as no signature at all. This guide explains what each verification method actually establishes: plain hashes, signed checksum files, OpenPGP detached signatures, Authenticode, Apple notarisation, Sigstore bundles and build provenance attestations. It is written for administrators, platform engineers and security teams who download installers, ISOs, CLI binaries and agents by hand or in scripts, and who need a verification rule they can apply consistently. Package manager repository trust is covered separately in the Linux Package Signing and Repository Trust Guide.
Key takeaways
- A hash is an integrity check, not an authenticity check. It only means something if the expected value reached you over a channel the attacker could not also change.
- A signature is only as good as the key you trust. "Good signature" from a key you fetched from the same compromised page proves nothing; pin the publisher's fingerprint from an independent source.
- Signed checksum files are the common pattern. Verify the signature on SHA256SUMS first, then check the file against it, as Ubuntu documents for its ISOs.
- Keyless signing moves trust to identity. Sigstore and build attestations verify who built the file and from which repository, so you must state the expected identity, issuer and repository, not just "is it signed".
- Automate the rule, then measure it. Scripts that download tools should fail closed on a missing or mismatched signature, and every exception should be recorded.
What each method proves
Verification answers three different questions: is this the exact file the publisher released (integrity), did the publisher really produce it (authenticity), and was it built from the source and process the publisher claims (provenance). No single method answers all three, and none of them says the software is free of vulnerabilities or malicious intent. A signed backdoor is still a backdoor, as the XZ Utils backdoor showed: the malicious code shipped in release tarballs produced by a trusted maintainer identity.
| Method | What it proves | What it does not prove | Where the trust comes from |
|---|---|---|---|
| Hash published on the download page (SHA-256) | The file you hold matches that value; no corruption in transit | That the publisher produced it, if the page itself was altered | The web page and its TLS connection |
| Hash from a separate channel (release notes, mailing list, vendor portal) | Integrity, plus some authenticity if the channels are independent | Protection if both channels share an account or CMS | Independence of the two channels |
| Signed checksum file (SHA256SUMS + .gpg or .asc) | The list of hashes was signed by the holder of a specific key | That the key belongs to the publisher, unless you checked the fingerprint | The OpenPGP key fingerprint you pinned |
| Detached OpenPGP signature on the file | Same as above, per file | Same as above | The pinned key |
| Authenticode (Windows) | Signed by a certificate chaining to a trusted code signing CA; file unchanged since signing | That the signer is the vendor you expected, unless you check the subject | Windows root store and the signer certificate subject |
| Developer ID signing and notarisation (macOS) | Signed by a registered developer, scanned by Apple for known malicious content, unchanged since signing | That the developer is the one you meant to install | Apple's developer programme and notarisation service |
| Sigstore bundle (cosign verify-blob) | Signed by a stated OIDC identity issued by a stated issuer, recorded in a transparency log | Anything, if you do not specify the expected identity and issuer | The identity and issuer you require |
| Build provenance attestation (SLSA, GitHub artifact attestations) | Which builder, repository and workflow produced the artefact digest | That the source code itself was benign | The builder identity and source repository you expect |
Checksums: doing them properly
Use SHA-256 or stronger. FIPS 180-4, the Secure Hash Standard, defines the SHA-2 family, and NIST announced in December 2022 that SHA-1 should be phased out by 31 December 2030, having already advised against it where collision attacks matter, such as digital signatures. Microsoft's documentation for Get-FileHash states that MD5 and SHA1 are no longer considered secure and should be used only for simple change validation. A publisher that offers only MD5 is giving you a corruption check, nothing else. See the Hashing Guide and the Hash collision attacks guide for the background.
- Linux and macOS:
sha256sum -c SHA256SUMSchecks every listed file and prints OK or FAILED; on macOSshasum -a 256 filegives the digest. - Windows:
Get-FileHash -Path file -Algorithm SHA256; SHA256 is the default when -Algorithm is omitted.certutil -hashfile file SHA256also works on systems without PowerShell 7. - Compare by machine, not by eye: people check the first and last few characters. Compare the full string in the script, or use the -c mode that reads the checksum file.
- Get the expected value from somewhere else: if the hash and the file sit on the same page, an attacker who edits one edits both. Prefer a signed checksum file, a hash in a signed release note, or a value from the vendor's authenticated portal.
Checksums are still worth checking even when a signature exists. They catch truncated and corrupted downloads cheaply, and in a signed checksum file the hash is what binds the large ISO to the small signed text file.
OpenPGP signatures and the fingerprint problem
Most open source projects and Linux distributions sign either the file or a checksum list with an OpenPGP key. GnuPG's --verify command takes the detached signature as the first argument and the signed data as the remaining ones, and it will not read the signed data from standard input unless you pass "-" explicitly. The weak point is never the cryptography; it is the step where you decide that the key belongs to the publisher.
Ubuntu's own verification tutorial shows the full pattern. Download SHA256SUMS and SHA256SUMS.gpg, run gpg --keyid-format long --verify SHA256SUMS.gpg SHA256SUMS, import the named keys from the Ubuntu keyserver if missing, then list them with --with-fingerprint and compare the full fingerprints with the published values before trusting the "Good signature" result. Only then run sha256sum -c SHA256SUMS against the ISO. The tutorial also notes two details that matter in practice:
- Long key IDs only: Ubuntu uses 64-bit key IDs because short 32-bit IDs are insecure; collisions for short IDs are practical, so never identify a key by eight hex characters.
- The trust warning is not a failure: "This key is not certified with a trusted signature" only means you have not marked the key as trusted locally. A "BAD signature" result is the failure.
- Mirrors become safe: once the fingerprint matches, the signature holds even if the mirror served the files without TLS.
For automation, do not rely on the user's keyring. GnuPG provides gpgv, which compares signed data against a given set of trusted keys, and the --assert-signer option, which checks that a file was signed by a particular key. Ship the publisher's public key, pinned by fingerprint, inside your build or configuration repository so that changing the trusted key is a reviewed change.
Signing keys get stolen. The HashiCorp GPG key exposure after the Codecov breach forced HashiCorp to rotate the key that signs its product downloads, and anyone who had pinned the old key needed to update and to re-check what they had already installed. Subscribe to the security advisories of the projects whose keys you pin.
Platform code signing: Windows and macOS
Windows Authenticode
Installers and executables for Windows are normally signed with Authenticode. Get-AuthenticodeSignature -FilePath setup.exe returns a Signature object; when a file has both an embedded and a catalog signature, the catalog signature is used. Check two things, not one: that Status is Valid, and that the signer certificate subject is the organisation you expected. A valid signature from an unrelated company is a common trick, and a stolen certificate produces a perfectly valid signature until it is revoked, as the NVIDIA code signing certificates theft demonstrated. Microsoft's signtool verify /pa /v gives the same check with chain detail where the Windows SDK is installed.
macOS Gatekeeper and notarisation
Apple's platform security guide says Gatekeeper checks that software downloaded outside the App Store comes from an identified developer, has been notarised by Apple as free of known malicious content, and has not been altered since signing, and it asks the user to approve a downloaded app before it first opens. For an administrator, codesign --verify --deep --strict confirms the signature and spctl --assess --type execute --verbose reports whether Gatekeeper would accept the app and which developer signed it. As with Authenticode, read the Team ID and developer name; notarisation is a malware scan, not a statement that the software is the one you intended to install.
Platform signing pairs naturally with allowlisting: once you know which publishers you trust, enforce it. See the Application Allowlisting Guide and the Smart App Control and SmartScreen guide.
Keyless signing and build provenance
Newer projects increasingly publish Sigstore bundles and build attestations instead of, or alongside, OpenPGP signatures. The model is different: instead of a long-lived key, the signer authenticates with an OIDC identity, receives a short-lived certificate, and the signature is recorded in a public transparency log.
- Sigstore blobs: Sigstore's documentation verifies a file with
cosign verify-blob file --bundle file.sigstore.json --certificate-identity=... --certificate-oidc-issuer=.... The identity and issuer are the trust decision. Take them from the project's documentation (for example the release workflow path and https://token.actions.githubusercontent.com for GitHub Actions) and hard-code them. See the Sigstore Guide. - GitHub artifact attestations: GitHub documents
gh attestation verify PATH -R OWNER/REPOfor binaries and anoci://prefix for container images, with a separate offline verification procedure for air-gapped environments. The attestation tells you where and how the artefact was built. - SLSA provenance: the SLSA v1.0 verification guidance says to check the provenance signature against your roots of trust, confirm the subject digest matches the artefact, confirm the builder ID is one you trust at a given SLSA build level, and compare the canonical source repository, buildType and externalParameters against expectations, rejecting unrecognised parameters. The source repository check is what catches a build from an unofficial fork.
Provenance closes a gap that signatures leave open: a signature says who signed, provenance says what was built and from where. It still does not judge the code. Compromised build pipelines and maintainer accounts, as in the Trivy supply chain attack, can produce correctly attested malicious releases if the attacker controls the trusted workflow.
Choosing a verification rule
| Situation | Minimum rule | Better |
|---|---|---|
| Windows installer from a commercial vendor | Authenticode Status Valid and expected subject | Allowlist the publisher certificate in App Control or AppLocker |
| macOS app or pkg | Gatekeeper accepts; Team ID matches the vendor | Restrict to known Team IDs through MDM |
| Linux ISO or tarball | Signed SHA256SUMS, fingerprint pinned, sha256sum -c passes | Pinned key stored in your repository and checked with gpgv |
| CLI binary from a GitHub release | Sigstore bundle or attestation with expected identity and repository | Also pin the version and the SHA-256 in your tooling manifest |
| Only an MD5 or SHA-1 hash offered | Treat as unverified; obtain from a second channel or ask the vendor | Build from source or use a signed distribution package instead |
| No verification material at all | Risk acceptance by the system owner, recorded | Sandbox analysis, then mirror the approved file internally with its hash |
Small organisations: the realistic goal is one approved download location per tool, a documented check for each, and an internal share holding the verified copies so staff stop downloading from search results. Large organisations: centralise acquisition in an artefact repository that verifies on ingest, records the digest and the verification evidence, and is the only source endpoints and pipelines can reach. See the Software Supply Chain Security Guide.
How to implement
- Inventory where software comes from: list tools that are downloaded by hand or by scripts (installers, agents, CLIs, ISOs, firmware), and record for each what verification material the publisher offers.
- Write the rule per publisher: the expected Authenticode subject or Apple Team ID, the OpenPGP fingerprint, or the Sigstore identity, issuer and repository. Store it in version control with an owner.
- Verify on ingest, once: route downloads through an internal artefact store or software share that runs the check and keeps the digest and verification output as evidence.
- Fail closed in scripts: installation scripts and CI jobs should stop on a missing signature, a mismatch, or an unexpected signer, rather than warn and continue. Pilot in report-only mode for two weeks to find publishers whose material is incomplete.
- Handle exceptions explicitly: where a vendor provides nothing usable, record a risk acceptance, raise it with the vendor, and keep the approved digest so later copies can at least be compared.
- Plan for key rotation: when a publisher rotates or revokes a key or certificate, update the pinned value through review, and re-verify stored artefacts. Rolling back means restoring the previous pinned value only after confirming the publisher's announcement.
Detection and measurement
- Unsigned execution: alert on execution of unsigned or invalidly signed binaries from user download folders (on Windows, Sysmon event ID 1 records signature status where configured, and App Control audit events show what would be blocked).
- Mark-of-the-Web bypass attempts: downloads delivered inside container formats that strip the Zone.Identifier stream are a known technique (ATT&CK T1553.005).
- Script verification failures: log every failed verification in CI and installation scripts; a sudden failure can mean a compromised mirror or a rotated key.
Useful measures:
- Percentage of externally sourced tools with a documented verification rule.
- Percentage of installs that came from the internal verified store rather than a direct download.
- Number of open verification exceptions and their age.
- Verification failures per month, each with a recorded cause.
- Time to update pinned keys after a publisher key rotation or revocation notice.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev. 5 | SI-7 Software, Firmware, and Information Integrity; SI-7(15) Code Authentication | Verify integrity and authenticate code before installation |
| NIST SP 800-53 Rev. 5 | CM-14 Signed Components; SR-4 Provenance; SR-11 Component Authenticity | Signed components, provenance records and anti-counterfeit checks |
| NIST SP 800-53 Rev. 5 | CM-11 User-installed Software | Govern what users may download and install |
| NIST CSF 2.0 | PR.PS-02; GV.SC-05 | Software maintained according to risk; supply chain requirements with suppliers |
| MITRE ATT&CK | T1195.002 Compromise Software Supply Chain; T1553.002 Code Signing; T1036.001 Invalid Code Signature; T1204.002 Malicious File | Techniques that verification is designed to catch or limit |
| CIS Controls v8.1 | Control 2 Inventory and Control of Software Assets | Authorised software only, from known sources |
Common failure patterns
- Hash and file on the same page: the check passes for an attacker's file because the attacker also edited the hash.
- Importing the key from the download page: "Good signature" from a key fetched beside the file is circular. Pin the fingerprint from an independent source.
- Trusting any valid signature: checking Status Valid without checking the signer subject accepts malware signed with any stolen or cheaply obtained certificate.
- Keyless verification without an identity: accepting any Sigstore signature from any GitHub workflow proves only that someone signed it.
- Verify, then download again: scripts that verify one copy and install another, or that use curl piped into a shell, defeat the check entirely.
- Never updating pinned keys: revoked or rotated keys stay trusted, and new legitimate releases fail, which teaches people to skip the check.
Practitioner checklist
- Use SHA-256 or stronger; treat MD5 and SHA-1 only hashes as unverified.
- Obtain expected hashes from a signed file or an independent channel, never only from the download page.
- Pin OpenPGP keys by full fingerprint in version control and verify with gpgv or --assert-signer.
- Check the signer identity, not just signature validity, for Authenticode and macOS code signing.
- For Sigstore and attestations, require the expected identity, issuer and source repository.
- Route downloads through an internal store that verifies on ingest and keeps the evidence.
- Make scripts and pipelines fail closed on missing or mismatched verification.
- Track publisher key rotations and revocations and update pins through review.
Standards and references
- NIST: FIPS 180-4 Secure Hash Standard
- NIST: NIST Retires SHA-1 Cryptographic Algorithm
- SLSA: Verifying artifacts (v1.0)
- Sigstore: Verifying signatures with cosign
- GnuPG: Operational GPG Commands
- Ubuntu: How to verify your Ubuntu download
- Microsoft Learn: Get-AuthenticodeSignature
- Apple: Gatekeeper and runtime protection in macOS
Related NHI Mgmt Group resources: Code Signing Security Guide · Linux Package Signing and Repository Trust Guide · Sigstore Guide · Drive-by Downloads Guide · Software Supply Chain Security Guide