Skip to content

HMAC Generator

Reproduce the signature a webhook sender computed, so you can find out whether the mismatch is the secret, the encoding, or the exact bytes being signed.

Runs in your browser Security & crypto

Signature

Compare against a received signature

Constant-time comparison, so this page cannot leak the answer through timing either.

When the signature does not match

It is almost never the algorithm. In rough order of likelihood:

  1. 1 The body was re-serialised. Signatures cover the raw bytes. If your framework parsed the JSON and you re-encoded it to verify, key order and whitespace have already changed. Capture the body before any middleware touches it.
  2. 2 The signed payload is not just the body. Stripe signs timestamp.body; others prepend the method and path. Read the sender's documentation for the exact concatenation.
  3. 3 The secret is hex or Base64, not text. A secret displayed as hex must be decoded to bytes before use. Using the printable characters as the key produces a valid-looking signature that never matches.
  4. 4 Encoding mismatch. Hex against Base64, or uppercase against lowercase. Compare lengths first: SHA-256 is 64 hex characters or 44 Base64 characters.
  5. 5 A trailing newline. Copying a payload out of a terminal usually adds one, and it changes everything.
In your own code, compare with a constant-time functionhash_equals() in PHP, hmac.compare_digest() in Python, crypto.timingSafeEqual() in Node. A plain === leaks the signature one byte at a time.