HMAC Generator

Compute an HMAC signature from a message and a secret key in your browser, with SHA-256, SHA-1, SHA-512 or MD5, printed as both hex and Base64.

🌐 Español

🔒 Private by design: your text is processed locally in your browser and never uploaded to any server.

A keyed hash proves who, not just what

A plain digest tells you a message has not changed. It does not tell you who produced it, because anybody can compute one. HMAC closes that gap by folding a shared secret into the hash computation, twice, through a defined construction of inner and outer padded keys. The result proves two things at once: the message is intact, and whoever produced the signature held the key.

That is why it sits under almost every webhook you will ever integrate. The provider signs the raw body with a secret only the two of you know and sends the digest in a header; your server recomputes it and compares. Request-signing schemes work the same way. The whole mechanism is public, repeatable and offline, which is exactly what makes a page like this useful for debugging one.

Message first, secret second

The layout here is the site’s two-textarea comparison shell, reused because no single-box tool can express a message and a key. The labels come with it and are not descriptive of this page. Original text holds the message you are signing. Changed text holds the secret.

Order matters more than it looks. HMAC is not symmetric, so swapping the two boxes does not produce the same digest, it produces a valid signature of the wrong thing. If a comparison fails and both values look right, check that they are in the boxes you think they are.

From two pasted boxes to a digest you can compare

  1. Paste the exact message into Original text. For a webhook, that means the raw body as received, not a pretty-printed copy.
  2. Paste the shared secret into Changed text.
  3. Set Hash algorithm. It opens on SHA-256 (recommended), which is what current providers use; SHA-1, SHA-512 and MD5 are there for systems that predate that consensus.
  4. Click the action button, which carries this page’s own name, HMAC Generator.
  5. Read the digest in hex and in Base64, then click Copy to clipboard.

Signing the message {"id":42} with the key whsec_test under SHA-256 returns the hex digest b3ea21c5b739c2a5e8d465ddb8d3cf2b8686ac6a82343abee0d2d2209d149f12, and above it the report notes a 9-byte message and a 10-byte key. Every value in this paragraph came from running the module and checking it against Node’s own crypto.createHmac, not from writing down something plausible.

Hex, standard Base64, and the Base64URL a JWT carries

The same digest is printed twice because providers disagree about how to transport it. The hex form is 64 characters for SHA-256, 40 for SHA-1, 128 for SHA-512 and 32 for MD5, and it is the more common choice in signature headers. The Base64 form is shorter and turns up in older APIs and in anything token-shaped.

Be careful with that second one. The Base64 of the example above is s+ohxbc5wqXo1GXduNPPK4aGrGqCNDq+4NLSIJ0UnxI=, and the plus signs and the trailing equals sign are the giveaway: that is standard Base64. A JWT’s third segment is Base64URL, which replaces plus with minus, replaces slash with underscore, and drops the padding entirely. So this page’s output will never look identical to a token’s signature segment until you make those substitutions. The JWT Generator actually imports this page’s Base64 helper and then runs exactly that conversion on top of it, which is a neat way to see that the difference really is only in the alphabet. Decode a token’s header with the JWT Decoder to confirm which HS algorithm it declares before you try to reproduce its signature by hand.

Where HMAC-MD5 fits, and the gap that made it hand-written

Three of the four algorithms go through the browser’s own Web Crypto API, the same built-in implementation the SHA-256 Hash Generator uses, though that page asks it for a plain unkeyed digest while this one imports the secret as a key and signs with it. MD5 could not go that route, because Web Crypto deliberately does not offer MD5 at all. HMAC is a construction applied on top of a hash rather than a mode of the hash itself, so supporting it meant writing the inner and outer padding by hand around a small MD5 library.

That is the sort of code that fails quietly, and the first attempt did fail: the library’s convenient string API re-encodes its input as UTF-8, which corrupts the raw pad bytes that have to flow back into the hash untouched. Switching to its raw-buffer API fixed it, and the fix was only visible because every algorithm was checked against Node’s own implementation before shipping rather than eyeballed. Note also that this stays a keyed construction and not a password hash. For storing a password, a deliberately slow function is the right instrument, which is what the Bcrypt Hash Generator & Verifier provides.

Whitespace, re-serialised JSON, and other reasons a check fails

When a signature comparison fails, the message is the usual culprit. Frameworks that parse a JSON body and hand you an object have already destroyed the exact bytes that were signed, because re-serialising changes spacing and can reorder keys. Capture the raw body before parsing, or read it from a request log, and paste that. Pretty-printing a payload with the JSON Formatter & Validator will change its digest for the same reason, so keep the formatted copy for reading and sign the original.

Two smaller traps are worth naming. Trailing newlines count, and a copy out of a terminal often adds one. And the secret must be the raw secret string, not a decoded or re-encoded version of it, unless the provider explicitly documents otherwise.

Nothing typed here is transmitted anywhere. The keyed computation happens in the tab, which for a page whose second input is a live credential is the only sensible arrangement, and it is the same reasoning behind the TOTP Code Generator, whose one-time codes are an HMAC of the clock. The rest of the hashing and encoding set lives in the developer tools category, and the complete developer tools guide puts them in order.

See it in action

Screenshot of the HMAC Generator tool with the sample input “Everything on SysFenix runs in your browser. You…”, Hash algorithm set to SHA-256 (recommended)
HMAC Generator mid-process: the sample input “Everything on SysFenix runs in your browser. You…”, Hash algorithm set to SHA-256 (recommended).
Screenshot of the HMAC Generator result screen showing the generated output “=== HMAC GENERATOR === Algorithm: HMAC-SHA256 Me…”
The finished result: the generated output “=== HMAC GENERATOR === Algorithm: HMAC-SHA256 Me…”. The download link is a local blob URL — the file never leaves your device.

Frequently asked questions

The report prints byte lengths where I expected to see my message and key. Is that all it does with them?

No, both values are used in full; only the display is trimmed. The report shows the UTF-8 byte length of each so you can confirm you pasted into the right boxes, and then prints the digest. A signing secret is usually a reusable credential, and echoing it back into a panel that people screenshot and copy from is a needless way to leak it.

Does the Base64 line match the signature segment of a JWT?

Not character for character, and this trips people up. This page prints standard Base64, with plus and slash characters and trailing equals signs. A JWT signature is Base64URL, which swaps plus for minus, swaps slash for underscore and drops the padding. Apply those three substitutions and the two strings become identical.

I clicked with the second box empty and got a report rather than an error. Why?

Because every outcome here is deliberately written as report text. The button becomes clickable as soon as either box has anything in it, so an empty secret is an easy accident, and the report says plainly that no secret key was provided and names the box to fill. The shared layout replaces any real thrown error with one fixed sentence, so an explanation that arrived as an exception would never reach you.

My digest does not match the signature header the provider sent. Where should I look first?

Almost always at the message, not the key. Sign the exact raw request body you received, byte for byte, because a JSON parse and re-serialise round trip changes whitespace and key order and therefore changes the digest completely. After that, check whether the provider documents hex or Base64, and whether the header packs several comma-separated fields around the digest rather than holding the digest alone.

Is HMAC-MD5 broken in the same way a plain MD5 hash is?

Not in the same way, no. The collision attacks that finished MD5 as a general-purpose hash do not directly break the keyed HMAC construction built on top of it, which is why HMAC-MD5 survived in production far longer than bare MD5 did. That is an argument for interoperating with a system that already uses it, not an argument for choosing it. Anything new should use SHA-256.

Can I paste a signature in and have the tool tell me whether it matches?

No, this page only computes. Paste the message and key, generate the digest, then compare it against the value you received. When you move that check into your own code, compare with a constant-time function rather than a plain equality test, because a normal string comparison leaks how much of the signature was correct through its timing.

Related tools