MD5, SHA-1 and SHA-256 checksums: why yours does not match
You hash a string in the browser, a colleague hashes the same string in a terminal, and the digests differ. Nothing is wrong with either hash function. The two inputs were different byte sequences, and a hash only ever sees bytes.
Same text, different bytes
A hash function takes bytes, so every step that turns text into bytes is part of the input. These are the usual causes of a mismatch, roughly in order of how often they bite:
- A trailing newline.
echo abc | sha256sumhashesabcplus a newline, four bytes instead of three. Useprintf abc | sha256sumorecho -n abcto hash exactly what you typed. - Line endings. A file checked out with CRLF line endings on Windows and the same file with LF on Linux are different files to a hash. One flipped byte per line changes the whole digest.
- A byte-order mark. Some editors and PowerShell redirections write
EF BB BFat the start of a UTF-8 file. It is invisible in the editor and part of the input to the hash. - The encoding.
éisC3 A9in UTF-8 andE9in Latin-1; Chinese characters take three bytes in UTF-8 and two in GBK. A tool that hashes UTF-16, which is what some Windows APIs default to, produces a third answer. The hash generator below always encodes text as UTF-8. - Hidden whitespace. A trailing space copied from a web page or terminal is enough.
When a checksum does not match, compare lengths before anything else. The status line of the tool reports how many bytes the digests were computed over; if you expected 3 and it says 4, you have found the newline.
Digest sizes and values you can check
Run these against any implementation as a smoke test:
| Algorithm | Digest size | Hex length | Digest of the empty string |
|---|---|---|---|
| MD5 | 128 bits | 32 | d41d8cd98f00b204e9800998ecf8427e |
| SHA-1 | 160 bits | 40 | da39a3ee5e6b4b0d3255bfef95601890afd80709 |
| SHA-256 | 256 bits | 64 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
For the three bytes abc, MD5 is 900150983cd24fb0d6963f7d28e17f72, SHA-1 is
a9993e364706816aba3e25717850c26c9cd0d89d and SHA-256 is
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad. SHA-384 and SHA-512 have 96 and
128 hex digits. Changing a single input bit changes about half the output bits, so a digest that looks
nearly right is just as wrong as one that looks entirely different.
What is actually broken
“MD5 is broken” has a narrow meaning: it is possible to construct two different inputs with the same MD5. That was first shown in 2004, and chosen-prefix collisions have since been used to forge a certificate and in real malware. SHA-1 fell to a published collision in 2017. Both still detect random corruption perfectly well, which is why you see them next to old downloads.
What they no longer do is stop an attacker. If someone can craft a file, a matching MD5 or SHA-1 proves nothing. Where an attacker is in the picture, use SHA-256 or stronger, and remember the second weakness: a checksum published on the same page as the download is only as trustworthy as the page. An attacker who can replace the file can replace the checksum. A signature, or a checksum fetched over a separate trusted channel, is what adds trust. The tool prints a note beside the table that MD5 is a legacy checksum for this reason.
Which to pick
| Purpose | Suggestion |
|---|---|
| Detecting transfer corruption, no attacker | any of them; SHA-256 is the default that ends the discussion |
| Integrity against deliberate tampering | SHA-256 or stronger, plus a signature or a separate channel |
| Cache keys and de-duplication | SHA-256; MD5 only if nobody can craft colliding inputs |
| Storing passwords | Argon2id, scrypt, bcrypt or PBKDF2, never the above |
| Authenticating a message with a secret | HMAC-SHA-256 |
MD5 comes from RFC 1321 in 1992; SHA-256, SHA-384 and SHA-512 belong to the SHA-2 family standardized from 2001. SHA-3 uses a different internal construction and is not in the tool below, which offers MD5, SHA-1, SHA-256, SHA-384 and SHA-512. If an existing system uses MD5 only for de-duplication or cache keys with no attacker able to submit files, leaving it alone is fine. If inputs come from people you do not trust, move on: the change is usually a function name and a column that grows from 32 to 64 characters.
None of these store passwords
A password hash is meant to be slow. MD5 and SHA-256 are built to be fast, so an attacker with a leaked table tries billions of guesses per second on a GPU, and an unsalted fast hash also falls to precomputed lookup tables. Use a purpose-built scheme such as Argon2id, scrypt, bcrypt or PBKDF2, with a per-user salt, through a maintained library. Hashing a password with SHA-256 first does not fix this.
Keyed hashes and length extension
To authenticate a message with a secret, use HMAC. The tempting SHA-256(secret + message) is vulnerable
to length extension: given the digest and the message length, an attacker can compute the digest of the
message plus extra data without knowing the secret. This affects MD5, SHA-1, SHA-256 and SHA-512 but not
the truncated variants such as SHA-384. HMAC removes the problem for all of them. The generator below
computes plain digests and does not offer HMAC.
Hex or Base64 is the same digest
Hex and Base64 are two spellings of the same bytes. A SHA-256 is 64 hex digits or 44 Base64 characters
ending in =. Browsers’ Subresource Integrity attribute uses the Base64 form, written like
sha384-..., while most published checksums use hex, so the tool shows both side by side. Compare hex
case-insensitively: A9993E36 and a9993e36 are the same value.
Hashing a file
sha256sum file.iso # Linux
shasum -a 256 file.iso # macOS
md5 file.iso # macOS, MD5
Get-FileHash file.iso -Algorithm SHA256
certutil -hashfile file.iso SHA256
In the browser, crypto.subtle.digest('SHA-256', bytes) covers SHA-1 and the SHA-2 family but not MD5, and it
only works on a secure origin (https or localhost). The tool computes MD5 with code in the page and the
rest through that API, so on plain http you get MD5 and a notice that the others are blocked. File
contents are read into memory in your tab and never uploaded, and it asks you to confirm before reading
anything above 25 MB. If a file hash will not match the publisher’s, repeat the check on the original download, not on a copy that went through
a mail client or a text editor.