HMAC Generator
Compute an HMAC signature from a message and secret key using SHA-1, SHA-256, SHA-384, or SHA-512 - output as hex and base64, with one-click copy.
- Free, no account
- No watermark
- No usage limit
About the HMAC Generator
Your HMAC won't match the one your server sent, and you've already checked the secret key three times. The value is almost never the problem. What breaks it is how that key gets turned into raw bytes, and that's the one setting most free HMAC tools don't even give you a toggle for.
This tool gives you that toggle. Drop in a message and a secret key, tell it whether the key is plain text, hex, or base64, choose your hash (SHA-1, SHA-256, SHA-384, or SHA-512), and it signs the message and shows the result as both hex and base64 with a copy button on each. It's free, there's no sign-up, and the key and message never leave your browser.
Want to confirm it's correct before trusting it with a real problem? Set SHA-256, put The quick brown fox jumps over the lazy dog in the message and key in the secret box. The hex reads f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8, the published HMAC-SHA256 test vector. You'll get that exact signature back every time, which is the whole point of an HMAC.
The page exists for one moment: you're staring at a signature mismatch and can't work out why. Wrong algorithm? Key read as text when the server wants bytes? Reproduce the server's exact computation here, every knob visible side by side, and the culprit usually turns up in under a minute.
How to use the HMAC generator
- Paste your message into the top box. This is whatever's being signed, a webhook body, an API request payload, a raw token string.
- Enter your secret key, then set the Key is toggle to match what your key actually is. Plain text by default, or Hex or Base64 if the secret stands in for raw bytes. This one toggle causes more mismatches than everything else combined, so get it right first.
- Pick the hash algorithm. SHA-256 is the default and the right answer almost every time. SHA-1 is there for legacy systems, SHA-384 and SHA-512 for a wider margin.
- Read the signature, shown as both hex and base64 since different services want different formats. Copy whichever one you need with the button beside it.
Nothing to submit. The signature recomputes the instant you change the message, key, encoding, or algorithm. And because the math runs in your browser using the same native cryptography it uses for HTTPS, you can load the page, cut your internet, and it keeps signing.
The setting that fixes most mismatches
Most online HMAC tools hand you one key box and quietly assume it's plain text. That's fine right up until your signing secret is a base64 or hex string standing in for raw binary, and then every signature you generate is wrong and the tool has no idea, because to it a key is just a key. HMAC signs with bytes, not characters, so the real question is how your typed key becomes bytes.
Text (UTF-8) is the default and what you want most of the time. Type key and it encodes those three characters as the bytes 6b 65 79. Stripe's whsec_... secrets, most webhook signing secrets, any human-readable passphrase, all of these are text.
Hex is for a secret that's written as hex but represents raw bytes. If a service hands you a1b2c3d4... and calls it hex-encoded, don't sign the literal letters, decode them first. Typing 6b6579 in Hex mode produces the identical key bytes as typing key in Text mode.
Base64 does the same job for base64-encoded keys, which is common with real cryptographic key material that's binary underneath. a2V5 in Base64 mode decodes to those same three bytes.
Now the part I actually built this for. The tool shows a live byte count right next to the toggle, and that number is your sanity check. Typed as text, key is 3 bytes, and 6b6579 read as hex is those same 3 bytes. If you flip to Hex and the count suddenly doubles, you're signing the hex string as text by accident, the classic version of this mistake. When two systems disagree on an HMAC, check the key encoding before you touch anything else.
Why hex and base64 can be the same signature
The second thing that costs people an hour is comparing the right signature in the wrong encoding. An HMAC is raw bytes. Hex and base64 are just two ways of writing those same bytes down, and on screen they look completely unrelated.
So the tool shows you both at once, on purpose. Hex (base 16) is the common default, GitHub uses it, most command-line tools print it, and an HMAC-SHA256 always comes out as 64 hex characters. Base64 is shorter and turns up in HTTP headers and cloud APIs.
The trap is a plain string comparison. If your code computes hex and the server sent base64, a === b fails even though the cryptography is flawless, and you'll blame the key or algorithm when neither is wrong, so get both sides into the same encoding before you compare them. Need to convert a signature between the two? The Base64 encoder/decoder does that.
Verifying a Stripe or GitHub webhook
This is the job most people show up for, so let me get specific, because the docs skate past the fiddly parts.
GitHub is the clean one. It signs the raw request body with HMAC-SHA256 and sends the result in the X-Hub-Signature-256 header, prefixed with sha256=. To reproduce it here, paste the raw body as the message, the webhook secret as a text key, pick SHA-256, and match the hex to the header (drop its sha256= prefix). The catch that bites everyone: hash the raw, unparsed body. If your framework parses the JSON and you re-serialize it before hashing, the spacing or key order shifts and the signature won't line up. Grab the raw bytes before any middleware touches them.
Stripe adds one twist that trips people up. Its Stripe-Signature header looks like t=<timestamp>,v1=<signature>, and what got signed isn't the body alone. It's the timestamp, then a literal dot, then the raw body: <timestamp>.<raw_body>. So the message box needs the full 1614556800.{"id":"evt_..."} string, not just the JSON. Leave off the timestamp prefix and you'll hunt a mismatch for an hour. That timestamp is also how Stripe lets you reject stale requests and block replays.
Frequently asked questions
Which hash algorithm should I actually pick?
SHA-256 unless a spec tells you otherwise. It's the modern standard for API and webhook signing, fast, supported everywhere, with no practical attack against it. SHA-384 and SHA-512 buy a wider margin for long-lived sensitive data, and on 64-bit hardware SHA-512 can even run faster than SHA-256 because it works in 64-bit words. The one rule you can't break is that both ends use the same one. HMAC-SHA256 on your side and HMAC-SHA1 on theirs will never match, however perfect the key.
Is HMAC-SHA1 broken because SHA-1 is broken?
No, and this one confuses a lot of people. SHA-1's failure is about collision resistance, which is what matters for certificates. HMAC leans on different properties, so HMAC-SHA1 is still considered practically secure, and legacy systems like older AWS signatures and OAuth 1.0 use it safely. Don't start anything new on it though. SHA-256 is right there and sidesteps the whole argument.
What key length should I use?
Longer than feels necessary. For HMAC-SHA256, aim for a random secret of at least 32 bytes, matching the hash's output size. Generate it from a secure random source, not a word you can remember, and rotate it now and then like any credential. A short guessable key lets an attacker forge any signature you'd accept, which sinks the whole scheme.
When I verify a signature in my own code, is there anything this tool can't show me?
Yes, one important thing: use a constant-time comparison. Don't check signature === expected with normal string equality. A plain comparison bails out the instant it hits a wrong character, and that tiny timing difference leaks which characters were right, one at a time, a real exploitable attack. Reach for crypto.timingSafeEqual in Node, hmac.compare_digest in Python, or hmac.Equal in Go instead.
Does my secret key get sent anywhere?
No. The whole computation happens in your browser using the same native cryptography it uses for HTTPS. Your key and message aren't uploaded, logged, or stored, and there's no server on the other end at all, which is why the page keeps working with your internet off. That is exactly what you want for test values. For a live production secret, honestly the safest move is to never paste it anywhere and let your backend do the real signing.
Why is my output blank?
Because the key box is empty. An HMAC needs a key, that's what separates it from a plain hash, so with nothing to sign with the tool leaves the output blank rather than faking a result. Type any secret and it fills in right away. If you want a keyless digest, that's a hash generator instead.