HMAC Generator

Sign a message with a secret key using HMAC-SHA256, SHA-1, SHA-384, SHA-512 or legacy MD5, then check it against an expected signature such as a webhook header.

Generator Web & Dev Updated Oct 4, 2026
Learn how this works
How to Use
  1. Choose the hash: SHA-256 is what GitHub, Stripe, Slack and most APIs use. SHA-1 and MD5 are for older systems.
  2. Enter the secret key and say how it is written: plain text (UTF-8), hex bytes or Base64.
  3. Enter the message exactly as it was signed. For a webhook that is the raw request body, byte for byte.
  4. Pick the output style (hex, HEX, Base64 or Base64url), then press Copy or click the result.
  5. To verify, paste the signature you received into Expected signature. A whole header such as sha256=… or Stripe’s t=…,v1=… works too.
  6. Read Show Work for the padded key, both hash passes and the byte-by-byte comparison.
Input
UTF-8 text
UTF-8 text
optional: hex, Base64, sha256=…, t=…,v1=…
Keys and messages stay in this page: nothing is uploaded, saved or put in the address bar.
Presets
HMAC
—
The two hash passes are drawn here.
Key
—
Message
—
Block size
—
Verify
—

Worked Example

RFC 4231 test case 2: HMAC-SHA256 with the key “Jefe” and the message “what do ya want for nothing?”.

1. The key is 4 bytes (4a 65 66 65). SHA-256 uses 64-byte blocks and 4 ≤ 64, so K′ is the key followed by 60 zero bytes.
2. Inner pad: every byte of K′ XOR 0x36. 4a ⊕ 36 = 7c, 65 ⊕ 36 = 53, 66 ⊕ 36 = 50, and each zero byte becomes 36: 7c 53 50 53 36 36 …
3. Pass 1: SHA-256 of those 64 bytes followed by the 28-byte message (92 bytes) = a2e485863d27f9d864ac8d802432a1ed477d8c4c6f349d16d4e7e917c629cad7.
4. Outer pad: K′ XOR 0x5c gives 16 39 3a 39 5c 5c …
5. Pass 2: SHA-256 of the outer pad followed by the 32-byte inner hash (96 bytes) = 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843, the value printed in RFC 4231.

A webhook check: GitHub’s documentation signs the body “Hello, World!” with the secret It's a Secret to Everybody, and the header is sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17.

The common mistake: hashing the key and message glued together. SHA-256(“Jefe” + message) is 689a1a0fa183259e022c4d77a4eb0810a94e9090d132c5f8732ccce2b36e3621, which matches nothing a server expects, and that construction is open to length-extension forgery. The second most common mistake is signing parsed-and-reprinted JSON instead of the raw body: one extra newline turns 757107ea… into 8fde2e97….

Show Work

Enter a key and a message to see the key padding, both hash passes and the result.

Formulas

HMAC (RFC 2104)
HMAC = H((K′ ⊕ opad) ‖ H((K′ ⊕ ipad) ‖ m))
Two nested passes of the same hash H
Key normalisation
K′ = (len(K) > B ? H(K) : K) + zeros to B
B = 64 bytes (MD5, SHA-1, SHA-256) or 128 (SHA-384, SHA-512)
Pads
ipad = 0x36 × B, opad = 0x5c × B
0x36 = 00110110, 0x5c = 01011100: they differ in 4 of 8 bits
Output length
L = 16 · 20 · 32 · 48 · 64 bytes
MD5, SHA-1, SHA-256, SHA-384, SHA-512 (hex is 2L characters)
Base64 length
chars = 4 × ⌈L / 3⌉
HMAC-SHA256: 44 with padding, 43 as Base64url
Bytes hashed
pass 1 = B + n, pass 2 = B + L
The outer pass is always short: 96 bytes for SHA-256

Why HMAC Has Two Passes

MD5, SHA-1 and SHA-2 are Merkle–Damgård hashes: the output is the hash function’s internal state after the last block. Knowing H(secret + message) therefore lets anyone carry on hashing from that state and compute H(secret + message + padding + extra) without the secret. This length-extension trick is not theoretical: in 2009 Thai Duong and Juliano Rizzo used it to forge signed requests to Flickr’s API, which signed calls with MD5(secret + arguments).

Mihir Bellare, Ran Canetti and Hugo Krawczyk presented HMAC at CRYPTO ’96 with a proof that it is secure if the underlying compression function behaves as a pseudorandom function. It became RFC 2104 in February 1997 and the US standard FIPS 198 in 2002 (revised as FIPS 198-1 in 2008). RFC 4231 (2005) added the official test vectors for HMAC-SHA-224, -256, -384 and -512 used in the presets above. HMAC now sits under TLS 1.2’s record protection and key derivation, HKDF, JSON Web Tokens, AWS request signing and the one-time codes in authenticator apps.

About This Tool

This tool computes HMAC-SHA256, HMAC-SHA1, HMAC-SHA384, HMAC-SHA512 and legacy HMAC-MD5 for a key and message given as text, hex or Base64, and prints the result as hex, uppercase hex, Base64 or Base64url. Paste a received signature, or the whole header, and it is compared byte for byte without an early exit; when it does not match, the page says whether the length points at a different hash or a missing Stripe timestamp.

The hash functions are this page’s own JavaScript, so every intermediate value (the padded key, both pads and the inner hash) can be shown, and it all runs in your browser. Keys and messages are never uploaded, stored or written into the address bar. Messages up to 4 MB can be signed.

It suits developers wiring up webhooks, testing an API signature scheme or checking an implementation against the RFC vectors.

Related tools: Hash Generator, JWT Decoder, and TOTP Code Generator.

Frequently Asked Questions

What is the difference between an HMAC and a plain hash?

Anyone can compute SHA-256 of a message, so a plain hash only detects accidental changes. An HMAC also needs the secret key, so only someone holding the key can produce a valid one. Simply hashing key + message is not a safe substitute: SHA-256 of “Jefe” followed by the message is 689a1a0f…, a value an attacker can extend with extra data without knowing the key (a length-extension attack). HMAC’s two nested passes block that.

Why does my HMAC not match the webhook signature?

Almost always the bytes differ. Sign the raw request body, not JSON that your framework parsed and re-serialised (spacing and key order change). Watch for a trailing newline: “Hello, World!” with GitHub’s documented secret gives 757107ea…, with a newline it gives 8fde2e97…. Also check hex versus Base64, and that Stripe signs the timestamp, a full stop and the body, not the body alone.

What happens when the key is longer than the block size?

It is hashed first. SHA-256 works on 64-byte blocks, so RFC 4231 test case 6 turns its 131-byte key into the 32-byte SHA-256 of that key, then pads it with zeros to 64 bytes. SHA-384 and SHA-512 use 128-byte blocks. RFC 2104 recommends a key at least as long as the hash output: 32 random bytes for HMAC-SHA256.

Are HMAC-SHA1 and HMAC-MD5 still safe?

HMAC does not depend on the hash being collision-resistant, so the collision attacks that broke MD5 (2004) and SHA-1 (2017) do not directly break their HMACs. RFC 6151 (2011) says the known attacks on HMAC-MD5 are not practical but that new protocols should not use it. HMAC-SHA1 still protects TOTP codes and older APIs; choose HMAC-SHA256 for anything new.

Why must signatures be compared in constant time?

A normal string comparison stops at the first different character, so a response that takes slightly longer reveals how many leading characters were right, and an attacker can guess a signature one byte at a time. Use hash_equals() in PHP, hmac.compare_digest() in Python or crypto.timingSafeEqual() in Node. This page compares every byte the same way.

How do I use the HMAC Generator?

Simply pick your options and read the result, which refreshes the instant you change something. There is nothing to submit and nothing to wait for.

Is it free? Does it work without internet?

Yes to both. It is free with no sign-up, and once the page has loaded it keeps working even with no internet.

Where does my data go?

Nowhere — every calculation runs on your own device. Nothing you enter is uploaded, logged, or stored.

Common Use Cases

GitHub webhooks

GitHub sends X-Hub-Signature-256: sha256= followed by 64 hex characters, the HMAC-SHA256 of the raw body under your webhook secret.

Stripe events

The Stripe-Signature header carries t= (a Unix time) and v1=, the HMAC-SHA256 of “t.body”. Reject events whose t is more than 5 minutes old.

JWTs signed with HS256

The third part of an HS256 token is the Base64url HMAC-SHA256 of “header.payload”: 43 characters for 32 bytes.

AWS Signature Version 4

AWS derives a signing key with four chained HMAC-SHA256 steps (date, region, service, “aws4_request”) before signing the request.

One-time passwords

A TOTP code is HMAC-SHA1 of a counter that ticks every 30 seconds, cut down to 6 digits.

Last updated: