HMAC Generator
Sign a message with a secret key. HMAC proves both that a message is intact and that it came from someone holding the key — which a plain hash cannot do. Your key never leaves this page.
{{ textCount }}
{{ textError }}
{{ r.value }}
{{ legacyNames }} {{ legacyNames.indexOf(' and ') === -1 ? 'is' : 'are' }} selected. Practical collisions are public for both, so treat the result as a checksum for spotting accidental corruption — not as evidence that a file is authentic.
Drop files here to hash them
Up to 100 files · any size · read in 4 MB chunks, never uploaded
Files are read, not uploaded. Each one is streamed through the hash function in chunks straight from disk, so a multi-gigabyte image never lands in memory and never leaves your device.
{{ f.error }}
{{ r.value }}
The key never leaves this page. It is not sent to a server, not saved to local storage, and not written into the exported files.
{{ hmacError }}
{{ hmacResult }}
HMAC is not a hash with the key glued on the front. It runs the key through the hash
twice with two different pads, which is what makes it resistant to the length-extension
attacks that break a naïve hash(key + message).
Drop the file you want to check
Hashed in your browser · never uploaded
{{ lengthProblem || vError }}
Hash matches
The {{ vResult.label }} digest of {{ vFile.name }} is identical to the hash you supplied, so this is the file that checksum describes. Letter case and surrounding whitespace were ignored.
Both {{ vResult.computed }}
Hash does not match
This file is not the one that checksum describes. Either the download is incomplete or corrupted, or the file has been altered. Downloading it again is the first thing to try.
Computed {{ vResult.cHead }}{{ vResult.cChar }}{{ vResult.cTail }}
Expected {{ vResult.eHead }}{{ vResult.eChar }}{{ vResult.eTail }}
First difference at character {{ vResult.diffAt + 1 }}.
Paste a hash into both boxes to compare them.
Hashes match
Both are the same {{ cmpResult.len }}-character value, so they identify identical content.
Ignored while comparing: letter case, spaces, line breaks, and any
sha256:-style prefix.
Both {{ cmpResult.a }}
Length {{ cmpResult.len }} — consistent with {{ cmpResult.guess }}.
Hashes are different
These are different lengths — {{ cmpResult.a.length }} and {{ cmpResult.b.length }} characters — so they are digests from two different algorithms, and comparing them tells you nothing about the content.
These two digests differ, so they describe different content.
Hash A {{ cmpResult.aHead }}{{ cmpResult.aChar }}{{ cmpResult.aTail }}
Hash B {{ cmpResult.bHead }}{{ cmpResult.bChar }}{{ cmpResult.bTail }}
First difference at character {{ cmpResult.diffAt + 1 }}.
Text, files and secret keys are hashed in your browser. Nothing you enter is ever uploaded to a server.
What HMAC is for
A plain digest proves a message has not changed. It cannot prove who produced it, because anyone can compute a digest. HMAC adds a shared secret, so a valid code proves both that the message is intact and that it was produced by someone holding the key. That is why webhooks are signed with HMAC rather than hashed.
Why not just hash the key and the message together?
Because hash(key + message) is vulnerable to a length-extension attack. The
Merkle–Damgård construction behind MD5, SHA-1 and SHA-2 leaks enough internal state in
its output that an attacker can append data to your message and compute a valid digest for the
longer message without ever learning the key. HMAC runs the key through the hash twice with two
different pads, which closes that hole.
The key's encoding matters
This is where most HMAC bugs come from. Stripe, GitHub and AWS publish signing secrets as hex or Base64 strings. If you hash those characters as literal text you are keying with the wrong bytes and every signature will differ from the one the sender computed — with nothing to tell you why. Set Key as to match the form your secret was given in.
Comparing MACs safely
In your own code, compare MACs with a constant-time function such as
hash_equals in PHP or crypto.timingSafeEqual in Node. A plain
=== returns as soon as it finds a differing byte, and that timing difference can leak
enough to forge a signature.
See also the full Hash Generator.
Frequently asked questions
What is HMAC?
A keyed message authentication code. It mixes a secret key into the hashing process, so only someone holding that key can produce the result — or check it. A plain digest proves a message is unaltered; an HMAC also proves who produced it.
How is it different from hashing the key and message together?
HMAC runs the key through the hash twice with two different pads. That construction resists length-extension attacks, which break the naive hash(key + message) approach: an attacker can append data to the message and compute a valid digest without knowing the key.
Should my key be text, hex or Base64?
Whatever form the key was given to you in. Webhook signing secrets are commonly published as hex or Base64, and hashing those characters as literal text yields a different, wrong MAC. This is the single most common HMAC mistake.
Which algorithm should I use?
HMAC-SHA256 unless something requires otherwise. HMAC-SHA1 and HMAC-MD5 are still in use and are not broken in the way plain SHA-1 and MD5 are, but there is no reason to choose them for new work.
Is my key sent anywhere?
No. It is used in your browser, is not written to local storage, and is excluded from the exported files. It is masked on screen by default so it does not linger in screenshots or a shared screen.