HMAC generator
Compute a keyed HMAC over a message using SHA-1, SHA-256, SHA-384, or SHA-512.
What this does
HMAC (Hash-based Message Authentication Code) combines a message and a secret key into a fixed-size tag using a hash function. This tool computes HMAC-SHA1, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512 with the browser's SubtleCrypto. The key can be entered as UTF-8 text, hex, or Base64, and the tag can be shown as hex or Base64, so you can reproduce whatever format the system you're integrating with uses.
Everything runs locally. Nothing about the key or message is transmitted.
When you'd use it
- Verifying an incoming webhook signature (Stripe, GitHub, Shopify, Slack, and many others sign their payloads with HMAC-SHA256).
- Signing an outgoing API request that uses a shared-secret scheme (for example AWS SigV4-style or older HMAC auth).
- Generating a tamper-evident token or a signed URL parameter.
- Debugging a signature mismatch by isolating which input — body, key encoding, output encoding — is wrong.
Worked example
Message id=42&amount=1000, key topsecret (as
UTF-8 text), HMAC-SHA256, hex output:
641bedff29f7168bc87504b5927bf20598fcf9cd5a4f5022b8317310f0de54a9
Switch the output to Base64 and the same 32 bytes become
ZBvt/yn3FovIdQS1knvyBZj8+c1aT1AiuDFzEPDeVKk= — this is exactly
the choice that trips people up when a provider's header is Base64 but
their example code prints hex. Change the
key encoding to "Hex" without changing the key text and the result changes
completely, because topsecret is no longer valid hex and the
bytes it decodes to are different. Reproduce the provider's documented
example first; once that matches, your real payload will too.
Limits and gotchas
- Sign the exact bytes. For webhooks, that usually means the raw request body before any JSON parsing or reformatting.
- Key encoding is not cosmetic. The same characters interpreted as text, hex, or Base64 are three different keys.
- Output encoding must match the value you compare against, including any provider prefix like
sha256=. - Production comparisons must be constant-time. Don't ship a plain
===check. - HMAC-SHA1 is still cryptographically acceptable for HMAC use, but choose SHA-256 for anything new unless a spec forces SHA-1.
- This is authentication, not encryption. The message is not hidden; HMAC only detects tampering and proves origin.
Frequently asked questions
- What is the difference between a hash and an HMAC?
- A plain hash of
messagecan be recomputed by anyone. An HMAC mixes a secret key into the hashing process, so only someone who holds the key can produce or check the value. That is what makes it a message authentication code: it proves both that the message wasn't altered and that it came from someone with the key. - My HMAC doesn't match the webhook's signature header.
- The usual causes: (1) hashing the wrong bytes — many providers sign the exact raw request body, so re-serialising the JSON changes it; (2) wrong key encoding — the secret might be hex or Base64, not text; (3) the provider prefixes the value (for example
sha256=) or uses Base64 while you used hex; (4) a timestamp or other field is concatenated with the body before signing. Check the provider's signing recipe precisely. - Why can't I just use == to compare two HMACs in my own code?
- A normal string comparison returns as soon as it finds a differing byte, which leaks timing information an attacker can use to recover the correct value byte by byte. Use a constant-time comparison function (
crypto.timingSafeEqualin Node,hmac.compare_digestin Python). Comparing here in the browser for debugging is fine; the production check needs to be constant-time. - Is the secret I type sent anywhere?
- No. The key and message are passed to the browser's SubtleCrypto HMAC implementation locally. Nothing leaves the page. Still, prefer a test key over your real production secret when you can.