The Secret-Key Hash That Powers API Security
A regular SHA-256 hash proves that data hasn’t been corrupted. But it doesn’t prove who created it. Anyone can hash the same message and get the same result. HMAC solves that by mixing a secret key into the hash computation. Only someone with the correct key can produce the right HMAC — and only someone with the key can verify it.
That’s why Stripe sends an X-Stripe-Signature header with every webhook. That’s why AWS signs API requests with HMAC-SHA256. That’s why GitHub webhook payloads come with an X-Hub-Signature-256. It’s all HMAC under the hood.
This tool computes HMAC using SHA-256, SHA-384, or SHA-512 through the browser’s Web Crypto API. Your message and secret key never leave the page.
Debugging Webhook Signatures
Here’s the scenario. You’ve set up a Stripe webhook endpoint. Stripe sends a POST request with a signature in the headers. Your server is supposed to compute the HMAC of the request body using your webhook secret and compare it to Stripe’s signature. If they match, the request is legitimate. If they don’t, someone’s sending you forged payloads.
But your verification keeps failing, and you can’t figure out why. Is your secret key wrong? Is there extra whitespace in the payload? Is the timestamp messing things up?
Paste the exact raw payload as the message, enter your webhook secret, select SHA-256, and compute the HMAC. Compare the result to the signature in the header. If it matches here but not in your code, the bug is in your implementation (probably a character encoding issue or extra newline). If it doesn’t match here either, double-check your secret key.
JWT Signature Verification
JWTs signed with HS256 use HMAC-SHA256 under the hood. The signature is an HMAC of header.payload (the first two Base64URL sections joined by a dot) using the signing secret.
If you’re debugging a JWT and want to verify the signature manually, decode the token with the JWT Decoder, then compute the HMAC of the header.payload string with your signing secret here. The output should match the third segment of the JWT (after Base64URL decoding).
HMAC vs. Plain Hash
With a plain SHA-256 hash, an attacker who modifies your data can just recompute the hash. There’s no secret involved, so the hash proves integrity but not authenticity.
HMAC adds a key. Even if an attacker knows the hash algorithm and can see the HMAC output, they can’t forge a valid HMAC for a modified message without the secret key. That’s why HMAC is the standard for API authentication and message signing.
The Hash Generator on Toolsvu produces plain hashes (MD5, SHA-1, SHA-256, SHA-512) when you don’t need a key. For password hashing specifically, use the Password Hasher (PBKDF2) which adds salting and key stretching.
Matching Command-Line Tools
HMAC is standardized (RFC 2104). The hex output here matches openssl dgst -sha256 -hmac "secret", Python’s hmac.new(), Node.js’s crypto.createHmac(), and every other correct implementation. If your results don’t match, check for trailing newlines, character encoding differences, or hex vs. Base64 output format mismatches.
SHA-256 is the default for nearly everything. SHA-384 and SHA-512 are only needed when a specific protocol demands them. All computation runs client-side through the Web Crypto API.