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 message can 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.timingSafeEqual in Node, hmac.compare_digest in 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.