HMAC Generator

Backend engineers debugging a failing Stripe webhook, API designers drafting a request-signing scheme, and security reviewers double-checking a JWT HS256 token all need the same thing: a trustworthy HMAC computed from a known key and message without pasting secrets into a random website. This tool runs entirely in your browser via the Web Crypto API, supports SHA-1 through SHA-512, and can verify an existing signature in constant time. Key encoding (UTF-8, hex, base64) is explicit so a mismatched format never produces a silently wrong tag.

Preset examples
Computed signature
—

Verify mode

Paste an existing signature (hex or base64). The tool recomputes the HMAC and compares in constant time.

How the Tool Computes and Verifies Signatures

The key string is first decoded according to the selected encoding — UTF-8 bytes for text, hex pairs for a hex key, or standard base64 for a base64 key — then imported into the Web Crypto API as a raw HMAC key with the chosen hash. The message is encoded as UTF-8 bytes and signed with SubtleCrypto.sign("HMAC", key, messageBytes). The resulting ArrayBuffer is converted to hex (lowercase, two-digit groups) and base64 (standard alphabet with padding) simultaneously, so both output forms are always available without a second computation.

Verification is strict: the pasted signature is normalized (whitespace stripped), then classified — pure hex of even length is treated as hex; anything else is attempted as base64 and re-encoded to hex for comparison. The final check loops over every character of the expected and candidate hex strings, XOR-ing the char codes and accumulating a bitmask so equal-length mismatches do not short-circuit on the first differing byte. A length mismatch returns false immediately; only a full constant-time match returns true.

Why encoding matters

A hex key of "deadbeef" and the UTF-8 string "deadbeef" produce completely different HMACs because the byte sequences differ (4 bytes vs 8 bytes). When a provider shows you a secret in a dashboard, note whether the dashboard labels it hex or plain text; selecting the wrong encoding here is the single most common cause of “valid key, wrong signature.”

Message must be exact

HMAC signs the literal bytes you provide. If a webhook provider signs the raw body and your framework re-serializes JSON (reordering keys, changing whitespace), the recomputed HMAC will not match. Paste the raw body exactly as it appears on the wire — including any trailing newline — into the message field.

Before and after: fixing a mismatched webhook signature

Before (key pasted as UTF-8, message re-serialized)
// key was hex string treated as text
hmac = sign("secretkeyhex", JSON.stringify(parsedBody))
// parsedBody = { b: 2, a: 1 }  (re-serialized, order changed)
// Expected tag from Stripe: 3a7f...
// Computed tag:              9c21...  ✗
After (hex key decoded, raw body preserved)
// decode hex → 4-byte key
keyBytes = hexDecode("7365637265746b6579686578")
// sign the raw request body, not re-serialized JSON
hmac = sign(keyBytes, rawBodyBytes)
// Expected tag: 3a7f...
// Computed tag: 3a7f...  ✓

Use Cases

Developer Production Workflow
Debug a failing Stripe / GitHub / Slack webhook receiver Sign API request bodies with HMAC-SHA256 before sending Verify a JWT HS256 signature during a security audit
Prototype an AWS SigV4-style signing scheme Rotate webhook secrets and verify old vs new tags Compare hex vs base64 outputs when integrating a new provider
Confirm a library’s HMAC matches Web Crypto Validate short-lived signed URLs for S3 or CDN access Audit webhook handlers for constant-time comparison bugs

Common Mistakes

  • Treating a hex-encoded secret as UTF-8 text. The byte sequences differ, so every signature will be wrong even though the “key looks right.” Select the correct key encoding before computing.
  • Re-serializing the JSON body before signing. Providers sign the raw bytes; JSON.stringify or a pretty-printer changes whitespace and key order. Use the exact body the provider sent.
  • Including a timestamp in the message but forgetting to prepend it (or the other way around). Match the provider’s scheme: some sign the unix timestamp, a period, then the body; others sign the body alone and send the timestamp separately.
  • Using standard base64 in a URL query parameter. The + and / characters break URL parsing; switch to base64url encoding for any signature that travels in a URL or JWT header.
  • Choosing HMAC-SHA1 for a new integration. It still works, but SHA-256 is the modern default and is accelerated on current CPUs. Reserve SHA-1 for legacy compatibility only.
  • Leaking the secret key in client-side JavaScript. HMAC is for server-to-server or server-to-edge trust; embedding the key in a browser bundle lets anyone sign arbitrary messages.

Best Practices

  • Default to HMAC-SHA256 for new designs. It is the industry standard for webhooks and request signing, offers a 256-bit tag, and benefits from AES-NI acceleration on modern servers.
  • Always compare signatures in constant time (no early return on the first differing byte). Most crypto libraries already do this; if you hand-roll the comparison, accumulate a bitmask across the full length.
  • Include a timestamp in the signed payload and enforce a replay window (±5 minutes is typical). This limits the damage if an attacker captures a valid tag and tries to reuse it later.
  • Rotate webhook secrets periodically and support two active secrets during the transition window so you can verify tags from both the old and new key without downtime.
  • Store secrets in environment variables or a secrets manager, not in source control. The example presets here use obvious placeholder values — never reuse them in production.
  • Log the signing key’s fingerprint (first 8 hex chars of the SHA-256 of the key), not the key itself, when debugging production signature mismatches. This lets you confirm which key was active without exposing the secret.

Frequently Asked Questions

What is HMAC and how is it different from a plain hash?

HMAC (Hash-based Message Authentication Code) mixes a secret key into the hash computation so only parties holding the key can produce or verify a valid tag. A plain SHA-256 of a message can be recomputed by anyone, so it proves integrity but not authenticity. HMAC proves both: an attacker who sees the message and the tag still cannot forge a new tag without the key.

Which HMAC algorithm should I use for webhooks?

HMAC-SHA256 is the default for Stripe, GitHub, Slack, and most modern webhook providers. It offers a 256-bit tag, broad library support, and hardware acceleration on common CPUs. Use HMAC-SHA512 only when a protocol explicitly requires it (some financial and enterprise APIs do), and treat HMAC-SHA1 as legacy — it still appears in older APIs but should not be chosen for new designs.

How do I verify a Stripe webhook signature?

Stripe signs the raw request body plus a timestamp with HMAC-SHA256 using your endpoint secret. Extract the v1 value from the Stripe-Signature header, compute HMAC-SHA256 of the string timestamp-dot-rawBody (a unix timestamp, a literal period, then the exact request bytes) with your whsec_ key, and compare the hex or base64 tag. This tool does exactly that: paste the body as the message, the secret as the key, pick SHA-256, and use Verify mode to confirm the signature you received.

Why is my HMAC signature not matching?

The three usual culprits are: (1) the message was modified in transit — proxies sometimes re-serialize JSON, changing whitespace; always sign the raw body, not a re-parsed object. (2) The key encoding is wrong — a hex key pasted as UTF-8 (or vice versa) produces a completely different tag. (3) A timestamp or nonce was prepended by the provider but not included in your local computation. Match the provider’s exact signing scheme byte-for-byte.

Can I use HMAC to authenticate API requests?

Yes. A common pattern is to sign a canonical string — method, path, timestamp, and a hash of the body — then send the timestamp and tag in headers. The server recomputes the HMAC with the shared secret and rejects the request if the tags do not match or the timestamp is outside a replay window. AWS Signature Version 4, Square, and Coinbase APIs all use variants of this scheme.

Does this tool store my secret key?

No. All computation runs in the browser using the Web Crypto API (SubtleCrypto). The key and message are held in memory for the current tab only; nothing is written to localStorage, cookies, or any remote server. Closing the tab discards everything. You can verify this by disconnecting from the network — the tool keeps working.

What is base64url and when should I use it?

base64url is a URL-safe variant of base64 that replaces + with − and / with _ and strips padding = characters. Use it when the signature will appear in a URL query parameter, JWT header, or filename — standard base64 characters break URL parsing or filesystem rules. Most webhook providers accept either encoding; check their documentation and match the format they expect in the header.