← Guides

Developer Guide

HMAC-SHA256 in the Browser

HMAC authenticates with a local secret; a bare SHA-256 only fingerprints bytes. RFC-style vectors, empty-key rejection, 50 MB file cap, no MD5. Web Crypto only.

hmac sha256keyed hash vs checksumweb crypto hmachmac generator browsersha384 hmac

A digest answers “are these the same bytes?” A keyed hash answers “did someone who knows this secret produce this hex?” /tools/hash-generator does both in Web Crypto: SHA-1, SHA-256, SHA-384, SHA-512, and HMAC of those same algorithms. Turn HMAC on, type a key, hash text or drop a file up to 50 MB. An empty key is rejected. MD5 is not on the menu. The secret never leaves the tab — which is also why you should not screen-share it.

HMAC is not a longer checksum

SHA-256("hello") is always 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824. Anyone can recompute it. HMAC-SHA-256 with key secret and message hello is 88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b. Change the key to key and you get 9307b3b915efb5171ff14d8cb55fbcc798c6c0ef1456d66ded1a6aa723a58b7b instead. Same message, different authenticators.

Publish a bare SHA-256 next to a download. Use HMAC when you already share a secret with the sender (webhook signatures, internal job payloads). You cannot put the HMAC key on a public download page and still call it authentication.

Web Crypto importKey and sign

The tool UTF-8-encodes the key and the message, imports the key as HMAC with the selected hash, and signs. That matches crypto.subtle.importKey('raw', …, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']) plus crypto.subtle.sign. Node’s createHmac('sha256', 'secret').update('hello').digest('hex') should match the hex you see.

Uppercase hex is display-only. Toggling A–F does not re-hash. Compare case-insensitively in scripts if a vendor prints uppercase.

Algorithms, output length, and SHA-1

HMAC-SHA-256 is 256 bits (64 hex characters), same width as a SHA-256 digest. HMAC-SHA-384 is 96 hex characters; HMAC-SHA-512 is 128; HMAC-SHA-1 is 40. Prefer HMAC-SHA-256 or stronger for new work. SHA-1 HMAC still exists because some legacy APIs demand it (Git still uses SHA-1 in places). Do not start a new webhook on SHA-1.

MD5 HMAC is collision-broken territory and is omitted, same as MD5 digests. If a vendor only documents md5=… signatures, that is their bug, not a missing checkbox.

Files versus text, and the 50 MB cap

Text mode hashes UTF-8 bytes of the textarea. File mode hashes the dropped file’s raw bytes — not the file name, not a hex dump you pasted. A 4-byte file 00 01 ff 10 will not match SHA-256 of the string 0001ff10.

Files larger than 50 MB are rejected so the tab does not freeze. Multi-gigabyte ISOs belong in sha256sum or openssl dgst -sha256 -hmac. The 10 MB cap on /tools/image-resizer and the 5 MB cap on /tools/image-to-base64 are different tools.

JWT HS256 is HMAC too — different box

HS256 on a JWT is HMAC-SHA-256 over base64url(header) + '.' + base64url(payload) with the issuer secret, then Base64url of the MAC. /tools/jwt-debugger does that verify. /tools/hash-generator will not split a JWT for you. If you HMAC the whole compact token as a string you will not match alg: HS256.

HMAC is still fast. It is not Argon2id. Do not HMAC a user password and store the hex as a login verifier.

Common pitfalls

Leaving HMAC off and comparing a webhook to a bare SHA-256. Using an empty secret (the tool errors: HMAC needs a key). Hashing a pasted filename instead of dropping the file. Treating a matching HMAC as “the publisher is trustworthy” when the key leaked in a client bundle. Confusing this page with /tools/jwt-debugger.

  • Empty HMAC key → error, not a digest of “no key”.
  • SHA-256("hello") ≠ HMAC-SHA-256("hello", "secret").
  • 50 MB file cap; no MD5.

Worked examples

HMAC-SHA-256 of hello with key secret

Input
message: hello
key: secret
algo: SHA-256, HMAC on
Output
88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b

Reproduce with Node createHmac('sha256', 'secret').update('hello').digest('hex'). Wrong key, different hex.

HMAC-SHA-384 of the same pair

Input
message: hello
key: secret
algo: SHA-384, HMAC on
Output
7e1e620ca0068fd1fce00c1ad3f5c6dbb12874dd2fb9c26502d09d0d804f2c0ba1d921b9458416cba480417571001e18

96 hex characters. Width follows the inner hash, not the message length.

Frequently asked questions

Why does an empty HMAC key fail?

A keyed hash with no key is not defined in a useful way on this page. Paste a real secret. Bare checksums use HMAC off.

Does HMAC replace TLS?

No. TLS protects the pipe. HMAC authenticates a payload when both ends already share a key. Use both for webhooks.

Can I HMAC a file without uploading it?

Yes. Drop it on /tools/hash-generator, enable HMAC, enter the key. The 50 MB cap is local memory, not an upload quota.

Is this how I verify a JWT?

Only accidentally. JWT HS256 HMACs the signing input, not the whole compact string. Use /tools/jwt-debugger.

Why is there no MD5 HMAC?

MD5 is collision-broken and too often misused. The Hash Generator omits MD5 for digests and for HMAC.

Summary

Turn HMAC on when you share a secret; leave it off for public checksums. /tools/hash-generator runs SHA-1 through SHA-512 in Web Crypto, rejects an empty HMAC key, caps files at 50 MB, and will not compute MD5. Confirm hello / secret against 88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b, and verify JWTs on /tools/jwt-debugger instead of HMAC-ing the whole token.

Try it yourself

Put what you've learned into practice with our free browser-native tool.

Open Tool →