HMAC generator
Generate HMAC-SHA1, SHA-256, SHA-384 and SHA-512 codes from a secret and a message, in hex or Base64 — ideal for debugging webhooks.
- Runs in your browser
- Nothing you enter is sent
- Free, no account
Paste the checksum published next to a download. Comparison ignores case and any spaces.
What HMAC adds to a hash
A plain hash proves nothing about who produced it, because anyone can compute one. HMAC mixes a shared secret into the hash in a specific nested construction:
HMAC(K, m) = H((K ⊕ opad) ‖ H((K ⊕ ipad) ‖ m)) The two padded copies of the key are what make it secure: the construction is provably resistant to length-extension attacks, which is why you must use HMAC rather than simply hashing a secret and a message together.
Debugging a webhook signature
This is what most people are here for. When a provider says your signature does not match, the cause is almost always one of these, in order of likelihood:
- The body is not the raw body. Your framework parsed the JSON and re-serialised it, changing key order or whitespace. Capture the bytes before any parsing.
- A prefix is missing. Stripe signs
timestamp + "." + payload. GitHub signs the body alone but prefixes the header value withsha256=. Read the exact construction in their docs. - The key format is wrong. A secret that looks like hex may be intended as UTF-8 text, or the reverse. Try all three formats in the selector above and see which reproduces the expected signature.
- Encoding of the output. Some providers send hex, others Base64. The value looks completely different but may be the same bytes.
- A trailing newline. One extra byte changes the whole code.
Common provider constructions
| Provider | Algorithm | Signed value | Output |
|---|---|---|---|
| Stripe | HMAC-SHA256 | t.payload | Hex |
| GitHub | HMAC-SHA256 | Raw body | Hex, prefixed sha256= |
| Shopify | HMAC-SHA256 | Raw body | Base64 |
| Slack | HMAC-SHA256 | v0:timestamp:body | Hex, prefixed v0= |
| AWS SigV4 | HMAC-SHA256, chained | A derived key over four stages | Hex |
Always verify against the provider's current documentation — these constructions do change, and the details are exactly where the bugs live.
Comparing signatures safely
In production code, never compare with ==. A byte-by-byte comparison returns early
on the first mismatch, and the timing difference is measurable over enough requests, which lets
an attacker recover a valid signature one byte at a time. Use your platform's constant-time
comparison: hmac.compare_digest in Python,
crypto.timingSafeEqual in Node, hash_equals in PHP.
The comparison on this page is a debugging aid, so a plain equality check is fine here.
Also verify the timestamp
A valid signature does not make a request fresh. Without a timestamp check, an attacker who captured one webhook can replay it forever. Reject anything older than a few minutes, and include the timestamp inside the signed value so it cannot be altered — which is exactly why Stripe and Slack sign it alongside the body.
Pasting a production secret
The secret stays in this tab: WebCrypto runs locally, and the page cannot make a network request. Use a test key where you have one — not because of this page, but because anything on screen can be captured by a screenshot tool or seen over your shoulder.
Questions
What does HMAC add over a plain hash?
A key. Anyone can compute SHA-256 of a message, so a bare hash proves nothing about who sent it. HMAC mixes a shared secret into the hash in a specific nested construction, so only a holder of the key can produce a valid code.
My webhook signature will not match. What is wrong?
Nearly always the message bytes. Sign the exact raw body — before any JSON parse and re-serialise — including a trailing newline if there is one. Many providers also prefix a timestamp and a dot before the payload.
Which key format should I pick?
Whatever the provider gave you. Stripe and GitHub secrets are UTF-8 text; AWS derives a binary key you would supply as hex or Base64. Choosing the wrong format silently produces a wrong code.
Is it safe to paste a production signing secret here?
The secret stays in the tab: WebCrypto runs locally and the page has no network access. It is still worth using a test key where you can, because anything on screen can be shoulder-surfed or captured by a screenshot tool.
Should I compare signatures with a simple equality check?
Not in production code — use a constant-time comparison to avoid leaking timing information. On this page the comparison is for debugging, so a plain check is fine.