Verifying a download with SHA-256: when it does not match, look at the length first
A checksum answers exactly one of two questions: were the bytes accidentally changed, and did someone deliberately forge these bytes. Any digest answers the first; none of them answers the second. Here is the order to work through when you have a downloaded file and a published hash.
First decide whether that hash is worth anything
If the file and the published checksum come from the same server, the hash proves your download was not truncated or altered in transit — it does not prove the publisher handed you the original. Whoever can swap the file can swap the line of hex. What resists tampering is a signature: a .sig / .asc made with a private key, verified against a public key you obtained through another channel (a fingerprint printed on the site, a packaged distro image, a key server you already trust). GPG verification, macOS Gatekeeper and Windows code signing all have that two-channel structure; sha256sum has no such structure.
Short version: a checksum guards against accidents, a signature guards against adversaries.
Command line or web page
For a multi-gigabyte ISO where you already have the file, use the command line:
| Platform | Command | Notes |
|---|---|---|
| Windows PowerShell | Get-FileHash .\file.iso |
SHA-256 by default; -Algorithm MD5 to switch |
| Windows (built-in) | certutil -hashfile file.iso SHA256 |
The older route; its output has line breaks, watch copy/paste |
| Linux | sha256sum file.iso |
Plus sha1sum, md5sum, b2sum |
| macOS | shasum -a 256 file.iso |
openssl dgst -sha256 file.iso works too |
A web page is the better tool in three situations: the machine in front of you isn’t yours, you want every algorithm at once (to tell “different algorithm” from “different bytes”), or you want the published string compared for you. The hash and checksum calculator caps file input at 256 MB — not because the maths runs out, but because loading a whole file into one browser tab is slow and easy to crash. Above that, use the command above; the digest is identical.
When it does not match, check in this order
- Byte count. Digests eat bytes, not characters. A trailing newline, Windows writing
\r\n, the same Chinese sentence saved as UTF-8 versus GBK — all produce a different digest. Compare “bytes fed into the calculation” against the file size first. - Whether it is the same file.
file (1).isoleft by the downloader, a browser renamingx.exetox.exe.exe, two same-named files in different folders of an archive — more common than encoding surprises. - Whether it is the same algorithm. Length gives it away (table below). A 32-character string can never match a SHA-256.
- Whether the download completed. If the server advertised a
Content-Lengthand your file is smaller, it broke; re-download rather than hunting for a digest that matches.
The wording separates causes 3 and 4 on purpose: after you paste the published value, a length mismatch means a different algorithm, while length matches but the value differs means different bytes. That distinction saves a lot of pointless suspicion.
Length is each algorithm’s fingerprint
| Algorithm | Digest | Hex characters | Base64 characters | What to use it for now |
|---|---|---|---|---|
| CRC32 / Adler32 | 32 bits | 8 | — | Spotting accidental damage: zip, gzip, PNG, Ethernet frames |
| MD5 | 128 bits | 32 | 24 | Legacy fingerprints, cache keys, de-duplication |
| SHA-1 | 160 bits | 40 | 28 | Old git object ids, inherited signatures |
| SHA-256 | 256 bits | 64 | 44 | The default published on download pages |
| SHA-384 / SHA-512 | 384 / 512 bits | 96 / 128 | 64 / 88 | Extra margin, common in compliance specs |
| HMAC-SHA256 | 256 bits | 64 | 44 | Keyed code: webhooks, payment callbacks, JWT signing |
Thirty-two hex characters means MD5 (or a truncated something else); forty is almost certainly SHA-1; sixty-four is SHA-256 or HMAC-SHA256 — same length, and the difference is whether a key was involved. Case does not matter: the same digest in capitals is still the same digest, and comparison should ignore it.
What MD5 and SHA-1 broke, and what they are still fine for
“Broken” means collisions: you can construct two different inputs with the same digest. For MD5 that is a few minutes on a laptop; SHA-1 collisions were demonstrated in 2017 (the SHAttered PDF pair) and chosen-prefix variants are now cheap enough to forge certificate chains. That is a different problem from preimage attacks (given a digest, find any input that produces it), which remain out of reach for both.
So MD5 as a cache key, a local de-duplication fingerprint, or a quick “are these two files the same” filter is fine. MD5 as proof that software was not tampered with is not — an attacker can hand you a binary with the same MD5 as the genuine one. SHA-256 has no published attack and plenty of margin, which is exactly why download pages say sha256sum. When a legacy site publishes only MD5, use it, but downgrade what it proves to “the download is intact”.
Same bytes, three digests, difference visible at a glance (input is the five bytes of hello, UTF-8, no trailing newline):
CRC32 3610a686
MD5 5d41402abc4b2a76b9719d911017c592
SHA-256 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Why CRC32 is not a hash
Checksums are linear: change a few bytes, then append four carefully chosen ones, and the CRC32 comes out unchanged. That is how entries inside a zip can be edited while its checksum still passes. Adler32 is two sums modulo 65521, and it is weaker still on structured data. Both are excellent for framing and transport, and both are disqualified from “prove nobody touched this”.
HMAC is not “concatenate the key”
HMAC-SHA256(message, key) and sha256(key + message) both print 64 hex characters, so the mistake survives code review — but the second is exposed to a length-extension attack: SHA-2 is Merkle–Damgård, so with a public digest you can keep hashing extra blocks without knowing the key and get a “valid” code for the extended message. HMAC hashes the key-masked blocks twice, so an attacker can neither forge nor extend.
key = secret, message = hello (measured — same length, completely different values):
HMAC-SHA256 = 88aab3ede8d3adf94d26ab90d3bafd4a…
SHA-256(secret+hello) = b755d600ebe94c1a3f3d6aebbd7e12bc…
This is a frequent webhook integration bug. Do not invent a signing scheme: follow the provider’s stated HMAC construction, and when comparing digests avoid short-circuiting string comparison — timing differences are a channel too.
Limits of doing it in a browser
MD5, CRC32 and Adler32 are computed by hand-written JavaScript (browsers ship no MD5, and a CRC32 in JavaScript is thirty lines of table lookups), so they always work. The SHA rows and the HMAC rows go through crypto.subtle — the same primitive TLS uses — and it only exists in a secure context. Measured: opening the built page straight from disk (file://) gives isSecureContext === true and the SHA rows compute normally; https:// works; reaching the site over plain http:// via a LAN IP turns it off, in which case MD5/CRC32 still print and the SHA rows say so explicitly. That is the browser refusing, not the code taking a shortcut.
On privacy, one sentence only: the text and the bytes of the file you pick are handled in the tab you opened, the script contains no fetch, so what you type never leaves this device.
Related questions
Can a digest be reversed? No — it is a one-way compression: SHA-256 squeezes any length into 32 bytes. But it can be guessed: hash a table of known files or common passwords and look your value up (rainbow tables, dictionaries). That works brilliantly on short inputs, which is why password storage must use a salt plus a deliberately slow hash (Argon2id, bcrypt).
Should the filename be hashed too? A digest covers content only. To lock a set of files at once, use a manifest (sha256sum -c checksums.txt) rather than mixing names into a single digest.
Is a Base64 checksum usable? Yes, same bytes: SHA-256 is 64 characters in hex, 44 in Base64 (43 with the padding dropped). Normalise to one shape before comparing; the Base64 tool shows the byte count alongside.
Why does my ISO hash fine but the site’s md5sum.txt differ? Because you compared a SHA-256 against an MD5 line, or the vendor publishes both and you grabbed the wrong column. Length tells you which: 32 versus 64 characters.