Five values on this page, one digest underneath all of them
A colleague sends a fingerprint with colons in it, the CI log prints 64 plain characters, and the integrity attribute in your HTML holds something ending in an equals sign. Nothing matches, so the natural conclusion is a corrupted download. All three describe the identical 32 bytes written three ways.
SHA-256 produces 256 bits. How those bits reach a screen is a display choice made by whoever wrote the tool:
| Form | Length | Written by |
|---|---|---|
| Lowercase hex | 64 characters | sha256sum, openssl dgst, Git object ids, most REST APIs |
| Uppercase hex with colons | 95 characters | Certificate viewers, ssh-keygen output, Windows certutil |
| Base64 | 44 characters, ending in = | Subresource integrity, Java MessageDigest, the AWS x-amz-checksum-sha256 header |
| Double SHA-256 hex | 64 characters | Bitcoin block headers, transaction ids, Base58Check |
| Truncated hex | 12 characters here | Docker image ids, cache keys, short build tags |
The rack above prints all five at once for one reason. Most reported SHA-256 failures are an encoding mismatch rather than a bad file, and seeing the alternatives side by side settles it in a second instead of after an hour of re-downloading.
The sha256- prefix in an integrity attribute is Base64, not hex
Subresource integrity is where the encoding trips people most often. A script tag loading a library from a CDN carries a hash the browser checks before running the file:
<script src="https://cdn.toolexe.com/lib/parser.min.js"
integrity="sha256-2mMTz4V0mKfr0kZ1sQhWpVdRUdpCLRfMbYbYb0nX0kM="
crossorigin="anonymous"></script>The sha256- prefix names the algorithm. What follows is the Base64 row from the rack, never the hex one. Paste 64 hex characters there and the browser blocks the script with a console error about a hash mismatch, while the file itself is perfectly fine.
Two behaviours are worth knowing before you rely on it. The check applies to the response body after decompression, so a CDN switching from gzip to brotli changes nothing. But a CDN that minifies differently, or serves a rolling latest tag, breaks the page outright the moment the bytes shift by one character. Pin an exact version alongside every integrity attribute, and never point one at a URL somebody else revises.
The matching command line, when you want to generate one without this page:
openssl dgst -sha256 -binary parser.min.js | openssl base64 -AWhy Bitcoin runs SHA-256 twice
The fourth row hashes the digest again, SHA-256(SHA-256(input)). Bitcoin does this everywhere, in block headers, in transaction ids, in the checksum bytes of a legacy address, and the reason is a property of the plain construction.
SHA-256 belongs to the Merkle-Damgard family, which carries its internal state straight into the output. Hand somebody SHA-256(secret + message) plus the length of the secret, and they extend it into a valid digest for secret + message + padding + anything without ever learning the secret. Hashing the result a second time hides the state and closes the hole.
Two details catch people reading blockchain data. Bitcoin displays transaction ids in reverse byte order, so the value in a block explorer is the double digest read backwards. And the double digest applies to the raw serialized bytes, not to a hex string of them, which is why hashing the pretty printed hex in a terminal returns an answer nobody recognises.
If you reached for double hashing to protect an API signature, reach for HMAC-SHA256 instead. It solves the same state leak with a reviewed construction, and every language ships it in the standard library.
What a certificate fingerprint covers, and what it does not
The colon row matches what a browser certificate panel shows, and what openssl x509 -fingerprint -sha256 prints. The digest covers the DER encoding of the whole certificate, so it changes on renewal even when the key stays identical and the domain never moves.
This matters for pinning. Pin a certificate fingerprint in a mobile app and the app stops trusting your own server on the next renewal, which with a 90 day certificate arrives fast. Pinning the public key digest survives renewal, since the key is the part being reused. Both leave you locked out if the key is ever rotated in an emergency, which is why a backup pin is standard practice.
To reproduce the row for a live host:
openssl s_client -connect toolexe.com:443 -servername toolexe.com < /dev/null 2>/dev/null \
| openssl x509 -noout -fingerprint -sha256SSH does it differently again. ssh-keygen -lf prints SHA256: followed by Base64 with the padding stripped, so the value in a known_hosts prompt is the Base64 row minus its trailing equals sign.
Where SHA-256 is the wrong tool
Storing passwords is the biggest one. SHA-256 is built for speed, and a rented GPU rig runs billions of guesses per second against it. Adding a salt stops a rainbow table and does nothing about the throughput. Password storage needs a function designed to be slow and memory hungry: Argon2id first, with bcrypt and scrypt as accepted alternatives, all of which handle their own salting.
A few more places where a plain digest disappoints:
- Signing an API request. Use HMAC-SHA256 with a shared key. A bare digest of the body proves nothing about who sent it.
- Deduplicating near identical files. One flipped bit rewrites every character of the output, so a digest answers identical or not, never how close.
- Short identifiers. The truncated row suits a cache key and nothing security related. Twelve hex characters is 48 bits, and birthday collisions arrive around 16 million entries.
- Hiding a small value. Digests of phone numbers, email addresses or card numbers fall to brute force in minutes, because the input space is tiny.
Where SHA-256 belongs: release checksums, content addressing, integrity attributes, Merkle trees, commit ids, exact match deduplication, and as the hash inside HMAC and TLS signatures.
Matching this page from a terminal
Every row is reproducible locally, worth doing once so you trust the output later:
sha256sum release.tar.gz # Linux, lowercase hex
shasum -a 256 release.tar.gz # macOS
certutil -hashfile release.tar.gz SHA256 # Windows, uppercase hex
Get-FileHash release.tar.gz -Algorithm SHA256 # PowerShell
printf 'abc' | sha256sum # text with no trailing newlineThe last line is the one people get wrong. echo abc appends a newline, so it hashes four bytes rather than three and returns a digest nothing else agrees with. Reach for printf, or echo -n, whenever you compare a text digest against this page.
What this page will not do
- Files past roughly 2 GB are refused. Web Crypto needs the whole file in memory at once, so a disk image belongs with
sha256sum. - No streaming progress on the hash itself. The bar tracks reading the file, then the digest happens in one call.
- No keyed output. HMAC-SHA256 needs a secret and lives on its own page.
- No reversing. Nothing recovers an input from a digest, here or anywhere, and a site claiming otherwise is running a lookup table of common strings.
- Line endings are hashed as they arrive. A file saved with CRLF on Windows and LF on Linux produces two different digests from identical looking text.
Hashing calls crypto.subtle.digest in your browser and files are read through FileReader, so nothing is sent anywhere. Load the page once, cut the connection, and it keeps working.
