How to use this HMAC Generator
Enter the message you want to sign, provide your secret key, and click
Generate HMAC. The page will create an HMAC SHA256 value that you can copy
or download for use in tests, request signing, webhook validation, or debugging.
- Paste or type the message you want to sign.
- Enter the secret key.
- Click Generate HMAC.
- Copy the generated signature or download it as a text file.
To reproduce a signature correctly, the exact input text, secret key, encoding, and signing
rules must match the system you are testing.
Example use case
Suppose an API requires you to sign a request body with a shared secret. You can paste
the exact payload here, enter the same secret used by the API, and generate the HMAC
value to compare against the expected signature.
Common scenarios
- Webhook signature testing
- API request signing
- Message integrity checks
- Auth flow debugging
- Cryptographic verification experiments
Why HMAC exists instead of hash(secret + message)
The obvious way to authenticate a message with a shared secret is to hash them together —
SHA256(secret + message) — and send the digest along. It looks correct and it
is badly broken. The reason is a property of the hash function's internal
design, not of your code.
SHA-256, SHA-1, and MD5 are Merkle–Damgård constructions: they
absorb the message block by block and emit their entire internal state as the digest. That
means the digest is the machine's resumable state. Given
SHA256(secret + message) and the length of the secret — which an attacker can
simply guess — they can resume hashing and compute a valid digest for
secret + message + padding + anything they append, all without knowing
the secret. This is a length-extension attack, and it lets an
attacker forge authenticated messages. (SHA-3, BLAKE2, and SHA-512/256 are not vulnerable,
but the widely used hashes are.)
How HMAC is built — the nested inner and outer hash
HMAC (RFC 2104) defeats length extension by hashing twice with the key mixed in at both
ends. Written out, it is
H((K ⊕ opad) ‖ H((K ⊕ ipad) ‖ message)), where
ipad is the byte 0x36 repeated and opad is
0x5c repeated across a full block.
The message is hashed with the inner-padded key first, and that result is hashed again with
the outer-padded key. Because an attacker only ever sees the outer hash of the
inner state — never the raw internal state — they cannot resume the computation, so the
length-extension trick simply does not apply. This is why you should never invent your own
keyed-hash scheme: HMAC already solved a subtle problem you would probably get wrong.
Verifying an HMAC requires a constant-time comparison
Generating the HMAC is only half the job. When you check an incoming signature, how
you compare the two values matters as much as the algorithm. A normal string or byte
comparison (==) returns as soon as it hits the first differing byte, so the
time it takes leaks how many leading bytes were correct.
An attacker who can measure that timing can recover a valid signature one byte at a time,
never needing the secret — a classic timing attack. Compare with a
constant-time function that always examines every byte:
crypto.timingSafeEqual in Node.js, hmac.compare_digest in Python,
hash_equals() in PHP. Never gate authentication on a plain equality check.
The canonical use: verifying webhook signatures
When Stripe or GitHub sends your server a webhook, it signs the request with a shared secret
and puts the HMAC in a header — Stripe-Signature, or
X-Hub-Signature-256 for GitHub. Your job is to recompute the HMAC over the
payload with the same secret and compare, in constant time, against the header.
The gotcha that breaks nearly every first attempt: you must sign the exact raw
bytes of the request body. If your framework parses the JSON and you re-serialise
it before hashing, a reordered key or a changed space produces a completely different
digest and every signature "fails." GitHub prefixes its value with sha256=, and
Stripe folds a timestamp into the signed string so old captured requests cannot be replayed
— read the provider's exact signing recipe rather than assuming.
Frequently Asked Questions
Why not just hash the secret and the message together?
Because SHA256(secret + message) is vulnerable to a length-extension
attack. SHA-256 emits its full internal state as the digest, so an attacker who guesses
the secret's length can append data and forge a valid digest without knowing the secret.
HMAC's nested inner/outer hashing prevents exactly this.
Is HMAC encryption? Can I recover the message from it?
No. HMAC is a keyed hash, not a cipher. It is one-way and produces a fixed-size tag, so
the original message cannot be recovered from it. Its purpose is to prove integrity and
authenticity, not to hide the message contents.
Why does my signature not match the provider's?
Almost always because the bytes differ. Sign the raw request body before any JSON
parse-and-re-serialise, watch for a trailing newline or charset difference, and confirm
whether the expected value is hex or Base64 and whether it carries a prefix like
sha256=. HMAC changes completely if a single input byte changes.
How should I compare two HMAC values in code?
With a constant-time comparison — crypto.timingSafeEqual,
hmac.compare_digest, or hash_equals(). A normal
== exits early on the first mismatched byte and leaks, through timing, how
much of the signature was correct, which an attacker can exploit to reconstruct it.
Is HMAC-SHA256 still safe even though MD5 and SHA-1 are broken?
Yes. HMAC's security depends on the hash's resistance to certain attacks in a way that
the collision breaks against MD5 and SHA-1 do not directly defeat — HMAC-SHA-1 remained
practically secure long after SHA-1 collisions appeared. HMAC-SHA256 is a solid modern
choice; use it rather than HMAC-MD5.
What algorithm does this page use, and does it send my secret anywhere?
It computes HMAC using SHA-256 in the browser, so your message and secret are processed
on your device. That also means it is a testing and debugging aid — verify signatures in
your backend with a constant-time comparison, not by pasting production secrets into web
pages.