Loading HMAC Generator…
0 bytes of UTF-8. A signature is over bytes, so a re-indented JSON body or a CRLF where the sender used LF gives a different tag.
No key yet
The characters of the secret, encoded as UTF-8 bytes.
Comparing the two strings with === is the bug this tool cannot show you. On this page the comparison is harmless, because both values are already in front of you. In a webhook handler it is not: a comparison that stops at the first wrong character takes measurably longer the more of the prefix an attacker guessed right, which is enough to recover a valid tag one character at a time. Servers need a constant-time compare —crypto.timingSafeEqual in Node, hmac.compare_digest in Python.
An HMAC proves that whoever produced the tag held the key, and nothing else. It does not say when the message was sent, so a replay of a genuine signed request is still genuine: that is why providers put a timestamp in the signed material and refuse anything older than a few minutes. It also gives no non-repudiation, because both sides hold the same secret and either could have produced any tag — a digital signature is what you want when that matters.
A digest tells you that two piles of bytes are identical. It cannot tell you who produced them, because anyone can hash anything. HMAC closes that gap by folding a shared key into the hashing itself, in the two-pass construction RFC 2104 specifies: the receiver recomputes the tag from the body it was handed and its own copy of the key, and a value that matches could only have come from somebody holding that key.
That is what the header on an incoming webhook is for. Stripe, GitHub, Shopify and Slack all sign their request bodies this way, and the instruction in every one of their integration guides is the same — recompute the tag before you read a single field of the payload, because an unsigned request is just a POST from a stranger.
Almost every failed comparison comes down to signing something subtly different from what went over the wire. The sender signed a raw body; a framework that parses JSON and re-serialises it changes the spacing, the key order or the Unicode escaping, and the tag moves. Copying a payload out of a log viewer can convert line endings from LF to CRLF, which is another set of bytes again.
The key has the same problem in a smaller space. A secret printed as 64 hexadecimal characters is 32 bytes, not 64, and treating those characters as text produces a perfectly well-formed tag for a key nobody else has. This tool asks how the key is written for exactly that reason, and reports how many bytes it decoded to so the mistake is visible before the comparison fails.
Authenticity is not freshness. A signed request captured once stays valid forever unless something else expires it, which is why providers sign a timestamp alongside the body and reject anything more than a few minutes old — a check the signature cannot perform for you.
Nor is it proof of who sent it, in the sense a court would want. Both ends hold the same secret, so either could have produced any tag, and neither can prove the other did. That property is called non-repudiation and only asymmetric signatures have it. When more than one party needs a key, remember that every additional holder is another place the whole scheme can leak from.
Once the tag is computed, how it is compared matters. A plain string equality returns as soon as it finds a difference, so a wrong guess that shares the first four characters takes measurably longer to reject than one that shares none. Repeated a few thousand times against a live endpoint, that timing difference is enough to reconstruct a valid tag character by character without ever knowing the key.
The fix is a comparison whose duration depends only on length: timingSafeEqual in Node, hmac.compare_digest in Python, hash_equals in PHP. On this page it does not matter, because both values are already on your screen — but the habit belongs in the handler you are debugging.
Check the key encoding first, then the body. Capture the raw request bytes before any middleware touches them, confirm the hash the provider documents, and try the secret as text and as decoded hex. One of those four is nearly always the culprit.
Not in the same way. HMAC keeps its security even when its underlying hash has collisions, so existing HMAC-SHA1 deployments are not urgently unsafe. Nothing new should choose it, though, and this tool omits it so it cannot be picked by accident.
Only up to the hash block size — 64 bytes for SHA-256, 128 for SHA-512. RFC 2104 says a longer key is hashed down first, so a 200-character secret is silently reduced. Thirty-two bytes from a real random source is the sensible target.
For checking what your own code should produce, yes. For anything live, no: a secret pasted into any web page has been outside the systems that were meant to hold it, and the right response to that is rotating the key rather than hoping.