Hash & Security
HMAC Generator
Generate HMAC signatures with SHA-256, SHA-1 or SHA-512 using your secret key. Hex/Base64 output for verifying webhooks and API requests in your browser.
Leave this empty to hash the text box. Files stay in this browser.
Hex is compared without regard to case. A sha256= prefix, or a Stripe-style v1= value, is accepted. Base64 is compared exactly.
Provide a key and a message, then sign the payload.
HMAC output
—
Webhook signature example
GitHub sets X-Hub-Signature-256 to sha256= plus the lowercase hex HMAC-SHA256 of the raw request body. The key is the webhook secret, encoded as UTF-8, not as hex. The published sample uses the secret It's a Secret to Everybody and the body Hello, World!, which signs to sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17.
Stripe puts a timestamp and one or more signatures in Stripe-Signature. The signed string is the timestamp, a period, then the raw body. With secret whsec_test_secret, timestamp 1492774577, and body {"id":"evt_test"}, the payload is 1492774577.{"id":"evt_test"} and the header is t=1492774577,v1=22f7d74836ddad3284a2b80853dd2fe731655c1bfddcff3c3666a500cf3abd80.
Output actions
Ready to copy output once your result appears.
HMAC Generator mini guide
HMAC attaches a shared secret to a message and then hashes the combination, so the result changes when either the secret or the message changes. This page computes HMAC-SHA256, HMAC-SHA1, and HMAC-SHA512. The key can be typed as UTF-8 text, as hex, or as Base64. The message can be text or a file. Output is hex or Base64, and an expected signature can be compared, including the sha256= prefix used by GitHub and the v1= field used by Stripe.
How to use
- 1Enter the secret and choose how those characters become key bytes. UTF-8 uses the characters as text. Hex and Base64 decode them first. A hex string hashed as UTF-8 is a different key.
- 2Choose SHA-256, SHA-1, or SHA-512, then type the message or attach a file. A file is signed as raw bytes. Pick hex or Base64 to match the verifier you are talking to.
- 3Paste the signature you were sent into Expected HMAC. Generate HMAC reports match or no match. The GitHub and Stripe buttons load a worked example, including the header format.
What it is good for
- • Checking a webhook signature while you debug a secret or a body.
- • Reproducing an RFC 2104 or RFC 4231 test vector before you ship a signer.
- • Seeing how key encoding changes the MAC when the same characters are read as text or as hex.
Example
Input
Key Jefe, message what do ya want for nothing?, HMAC-SHA256
Output
5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
RFC 4231 test case 2. The key is the four UTF-8 bytes of Jefe. The same key and message under HMAC-SHA1, from RFC 2202, are effcdf6ae5eb2fa2d27416d5f184df9c259a7c79.
What HMAC is
HMAC is defined in RFC 2104. It is not the hash of the message, and it is not the hash of the secret glued on the front in whatever way a developer first tries. The construction is specific. If the key is longer than the hash function's block size, the key is hashed down first. The key is then padded with zeros out to one block. That block is combined with an inner pad and with an outer pad, two different repeated bytes. The inner hash covers the inner pad plus the message. The outer hash covers the outer pad plus that inner digest. The result is one digest whose length follows the underlying hash: 20 bytes for SHA-1, 32 for SHA-256, 64 for SHA-512. Anyone who knows the key can recompute it. Anyone who does not know the key should not be able to forge it.
The block size is why key length is not a casual detail. SHA-256 and SHA-1 use a 64-byte block. SHA-512 uses a 128-byte block. A key shorter than the block is zero-padded. A key longer than the block is reduced by hashing it. Two secrets that look similar as strings can land in different branches of that rule. This page passes the key bytes you decoded into Web Crypto's HMAC implementation, which applies those rules. You do not pad the key yourself. You do choose the decoding. That choice is the usual reason a local test and a partner's test disagree.
UTF-8 means the secret box is text, and the bytes are the UTF-8 encoding of those characters, with no trimming. A trailing space is part of the key. Hex means the box is decoded as pairs of digits, so the key 0b0b is two bytes, not four characters. Base64 is decoded the same way, after whitespace is removed. The twenty-byte key of repeated 0x0b used in the RFC samples is twenty copies of the byte 0b, which you enter as hex, not as the two characters 0 and b typed under UTF-8. Using the wrong encoding produces a stable, wrong MAC. The page will not guess which encoding you meant.
HMAC is a keyed construction. With no secret, you do not have an HMAC: hash the bytes on the SHA-256 generator, the SHA-1 generator, the SHA-512 generator, or the MD5 generator. A digest that arrived without a name can be triaged by width on the hash identifier before you decide which of those pages can reproduce it.
Test vectors
RFC 4231 test case 1 uses a 20-byte key of 0x0b repeated, and the data Hi There, with a capital H and a space. HMAC-SHA256 is b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7. HMAC-SHA512 is 87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854. Set Key encoding to Hex, paste 0b twenty times in a row with nothing else, set the message to Hi There, and select the algorithm. The hex output should match. RFC 2202 test case 1 is the same key and the same data under HMAC-SHA1, and the digest is b617318655057264e28bc0b6fb378c8ef146be00.
Test case 2 uses a key that is text. The key is Jefe and the data is what do ya want for nothing?, including the question mark and the spaces. Key encoding is UTF-8. HMAC-SHA256 is 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843. HMAC-SHA1, from RFC 2202, is effcdf6ae5eb2fa2d27416d5f184df9c259a7c79. HMAC-SHA512 is 164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737. These two cases are the ones to try first, because one forces a binary key and the other forces a textual key. If only the textual case works, the hex decoder is the broken piece.
The comparison field is case-insensitive for hex. You can paste the RFC value in uppercase. It also strips a leading sha256=, sha1=, or sha512= so a GitHub header can be pasted whole. A Stripe header is accepted when it contains v1= followed by the hex. Base64 output is compared as Base64, not folded to lowercase. If the verifier prints standard Base64 and you leave the page on hex, the line says no match. Switch the output format before you conclude the key is wrong.
Command-line equivalents
OpenSSL computes HMAC with openssl dgst -sha256 -hmac followed by the key as text. The Jefe vector is printf '%s' 'what do ya want for nothing?' | openssl dgst -sha256 -hmac Jefe. The printed hex should be the RFC 4231 value above. For SHA-1, change the flag to -sha1. For SHA-512, use -sha512. OpenSSL's -hmac switch takes the key as a string, which corresponds to UTF-8 on this page, not to hex. A binary key is awkward in that switch. The hex key encoding on this page is the easier way to feed the 0x0b test.
Language libraries disagree about whether the key argument is text or bytes. Node's createHmac accepts a string, which it encodes as UTF-8, or a Buffer, which it uses as raw bytes. Passing the hex characters as a string is the UTF-8 path. Passing Buffer.from(hex, 'hex') is the hex path. Python's hmac.new wants bytes for both the key and the message. If a service and this page disagree, print the key length in bytes on both sides. A 20-byte key and a 40-byte key that happens to look like the hex spelling of those 20 bytes will never match.
printf '%s' 'what do ya want for nothing?' | openssl dgst -sha256 -hmac Jefe printf '%s' 'Hi There' | openssl dgst -sha1 -hmac "$(printf '\\x0b%.0s' $(seq 1 20))"
Webhook signatures
GitHub signs the raw request body with HMAC-SHA256 and the repository webhook secret. The header X-Hub-Signature-256 is the ASCII text sha256=, then the lowercase hex digest. There is an older header, X-Hub-Signature, that uses HMAC-SHA1 and a sha1= prefix. Verify the SHA-256 header when both are present. The secret is UTF-8 text. The message is the raw body, not a re-serialized JSON object. GitHub's own documentation sample uses the secret It's a Secret to Everybody and the body Hello, World!. The header value is sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17. Load GitHub example fills those fields and runs the comparison.
Stripe signs a different string. The signed payload is the timestamp from the Stripe-Signature header, a period, and then the raw body. The header looks like t=1492774577,v1=hex, and it may contain more than one v1 while secrets are rotated. Each v1 is HMAC-SHA256 of that signed payload under the endpoint secret, written as hex. The example loaded by Load Stripe example uses the secret whsec_test_secret, the timestamp 1492774577, and the body {"id":"evt_test"}. The signed payload is 1492774577.{"id":"evt_test"}. The header is t=1492774577,v1=22f7d74836ddad3284a2b80853dd2fe731655c1bfddcff3c3666a500cf3abd80. This is a format sample you can reproduce here. It is not a signature copied from a live Stripe event. A real verification also checks that the timestamp is recent, which this page does not do. The page only shows whether the MAC matches.
Both providers require the raw body. A framework that parses JSON and later serializes it again will change key order or spacing, and the MAC will not match even with the right secret. When the page says no match, compare the byte length of the body you signed with the byte length the provider signed. Also confirm you did not include the header itself in the message. The header carries the MAC. It is not part of the input, except that Stripe's timestamp, which is inside the header, is copied to the front of the payload.
Security notes
HMAC proves that someone who knows the secret produced the tag. It does not hide the message. The body in a webhook is still readable by anyone who sees the request. It also does not store a password. Do not build a password database out of HMAC, and do not build one out of SHA-256, SHA-1, or MD5. Passwords need Argon2, bcrypt, or scrypt. The secret in an HMAC is a key you keep, not a password you hash and throw away. If the key leaks, every tag made with it can be forged until you rotate the key.
A string comparison of two MACs can leak timing. Production verifiers should use a constant-time compare supplied by the crypto library. This page prints match or no match for debugging, on your machine, against a value you pasted. That is the right tool for a fixture and the wrong tool for the only check in a public endpoint. Prefer HMAC-SHA256 for new webhooks. HMAC-SHA1 remains in old specifications and is still computed here so you can match them. HMAC-SHA512 is available when the other side asks for it. The tag length changes with that choice, and a verifier that expects 32 bytes will reject a 64-byte tag.
Rules and limitations
UTF-8 keys are not trimmed. Hex keys may contain spaces and an optional 0x prefix, and must contain an even number of hex digits. Base64 keys must decode cleanly. An empty secret is rejected. An empty message is allowed, because an empty body has a defined MAC.
A selected file replaces the message text and is read as bytes, up to 32 MB. Hex output can be upper or lower case. Expected HMAC accepts hex in either case, a sha256= style prefix, or a Stripe header that contains v1=. Base64 is compared exactly. The page does not check webhook timestamps.
Common problems
No match with a webhook is usually the raw body versus a re-parsed body, the wrong secret for that endpoint, or a hex secret typed with UTF-8 selected. Check the key length in bytes. GitHub's SHA-256 header and the older SHA-1 header are different algorithms. Selecting SHA-1 against an X-Hub-Signature-256 value will not match.
OpenSSL's -hmac flag takes a text key. It will not match this page's hex encoding unless the key really is text. The Jefe vector is the one to use for OpenSSL. The 0x0b vector is the one to use for the hex control.
Frequently asked questions
Which key encoding should I pick?
Use UTF-8 when the secret is a password or a webhook secret you were shown as text. Use hex when you were given an even-length string of digits that represents raw key bytes, such as the RFC's repeated 0b key. Use Base64 when the secret was issued as a Base64 blob. Hashing the hex characters as UTF-8 is a different key and will not verify.
How do GitHub and Stripe signatures differ?
GitHub HMAC-SHA256 signs the raw body and sends sha256= plus the hex in X-Hub-Signature-256. Stripe HMAC-SHA256 signs timestamp, a period, and the raw body, and sends t= and v1= in Stripe-Signature. Both need the raw body. This page can load either example and show match when the fields are filled from that example.
What is the HMAC-SHA256 of the Jefe test?
With the UTF-8 key Jefe and the message what do ya want for nothing?, RFC 4231 gives 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843. The same message under HMAC-SHA1 is effcdf6ae5eb2fa2d27416d5f184df9c259a7c79, from RFC 2202.
Is HMAC a password hash?
No. HMAC is a message authentication code. It needs the secret again to verify, and it is fast. Password storage needs a slow, salted function such as Argon2, bcrypt, or scrypt. Keep the HMAC secret out of the client you ship to end users. This page is for a secret you are willing to type into your own browser while debugging.
Is my input uploaded or saved?
This tool processes input in your browser and does not save its input or output to local storage. Copy and download are user actions. The site uses analytics for page and action metadata and may use ads and production error reporting; see the Privacy Policy for those services.
About this tool
Maintained by Toolbench. For large or deeply nested files, split the input into smaller batches and verify representative results before using them in an import or application. Report reproducible problems through Contact.
Next steps
Related tools
Continue the workflow with nearby tools for formatting, publishing, debugging, or follow-up checks.
SHA-256 Hash Generator
Hash text with SHA-256 and copy the lowercase hex digest.
Open SHA-256 Hash GeneratorSHA-512 Hash Generator
Create a SHA-512 digest from text or a file and compare an expected value.
Open SHA-512 Hash GeneratorSHA-1 Hash Generator
Generate a SHA-1 digest from text or a file, in hex or Base64.
Open SHA-1 Hash GeneratorHash Identifier
Guess a hash family from digest length and alphabet.
Open Hash IdentifierTrust
Policies, contact, and answers
Clear pages for how Toolbench works, what we collect, and how to reach us.