About HMAC-SHA512
HMAC-SHA-512 proves two things at once: that a message has not been altered, and that whoever produced the code held the shared secret. It takes a key and a message and returns a 512-bit code (128 hex characters) that nobody can produce or verify without the key.
The construction is deliberately not a hash of the key and message concatenated. HMAC hashes the message twice using two different key-derived pads, defined in RFC 2104, and that is what makes it immune to the length-extension attack which breaks the naive hash(secret + message) approach. If you have ever written that shortcut, HMAC is the thing to replace it with.
HMAC-SHA512 shows up as HS512 in JWTs, in some financial and payment message formats that specify long MACs, and as the PRF inside PBKDF2-HMAC-SHA512 and HKDF-SHA512 - which is arguably its most common role, buried inside key derivation rather than used directly. On 64-bit hardware it is typically the fastest of the three.
Any byte string works as a key. There is no length requirement, but a key shorter than the digest gives away security for nothing, so generate 64 random bytes and store them as a secret rather than typing a passphrase.
Both the key and the message stay in your browser on this page. The code is computed with the Web Crypto API and never transmitted, which is the only safe way to paste a real signing secret into a web tool.
How to use the HMAC-SHA512
- Paste the message exactly as the signing side sees it, byte for byte, including any newlines - this is where most mismatches come from.
- Paste the shared secret into the key field.
- The HMAC-SHA-512 code is computed in your browser and shown as 128 hex characters.
- Compare it against the signature you received. In your own code, compare with a constant-time function, never with ==.
Examples
-
Message
payload
HMAC-SHA512 in code
The same operation this tool performs, in the languages you are most likely to need it.
const enc = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
enc.encode("my-shared-secret"),
{ name: "HMAC", hash: "SHA-512" },
false,
["sign"]
);
const sig = await crypto.subtle.sign("HMAC", key, enc.encode("message"));
const hex = [...new Uint8Array(sig)]
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
console.log(hex);
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, receivedHex, secret) {
const expected = createHmac("sha512", secret).update(rawBody).digest();
const received = Buffer.from(receivedHex, "hex");
// Lengths must match first - timingSafeEqual throws if they differ.
if (received.length !== expected.length) return false;
return timingSafeEqual(expected, received);
}
// rawBody MUST be the unparsed body. Re-serialising req.body with
// JSON.stringify changes key order and spacing, and verification fails.
import hmac, hashlib
def sign(secret: bytes, message: bytes) -> str:
return hmac.new(secret, message, hashlib.sha512).hexdigest()
def verify(secret: bytes, message: bytes, received: str) -> bool:
expected = sign(secret, message)
# compare_digest is constant-time; == is not
return hmac.compare_digest(expected, received)
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
Mac mac = Mac.getInstance("HmacSHA512");
mac.init(new SecretKeySpec(
"my-shared-secret".getBytes(StandardCharsets.UTF_8), "HmacSHA512"));
byte[] sig = mac.doFinal("message".getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : sig) sb.append(String.format("%02x", b));
System.out.println(sb);
package main
import (
"crypto/hmac"
"crypto/sha512"
"encoding/hex"
"fmt"
)
func main() {
mac := hmac.New(sha512.New, []byte("my-shared-secret"))
mac.Write([]byte("message"))
fmt.Println(hex.EncodeToString(mac.Sum(nil)))
// Verify with hmac.Equal, which is constant-time.
}
using System.Security.Cryptography;
using System.Text;
using var hmac = new HMACSHA512(Encoding.UTF8.GetBytes("my-shared-secret"));
byte[] sig = hmac.ComputeHash(Encoding.UTF8.GetBytes("message"));
Console.WriteLine(Convert.ToHexString(sig).ToLowerInvariant());
printf '%s' "message" \
| openssl dgst -sha512 -hmac "my-shared-secret" -hex
# printf '%s' rather than echo: echo appends a newline,
# which changes the signature.
When you need this
- Verifying an HS512 JWT.
- Matching a MAC required by a payment or financial message specification.
- Reproducing the PRF step of PBKDF2-HMAC-SHA512 or HKDF-SHA512.
- Signing large messages on 64-bit hardware, where SHA-512 has the best throughput.
- Producing a 512-bit tag where a specification mandates one.
Common problems and what causes them
- A 128-character tag truncated to fit a column
- 512 bits is 128 hex characters, which overflows fields sized for SHA-256. Truncating a MAC is legitimate but halves its strength per bit dropped and must be done identically on both sides - a silent truncation on one side only means verification always fails.
- Treating PBKDF2-HMAC-SHA512 as a single HMAC
- PBKDF2 runs HMAC many thousands of times with a salt and a counter. Computing one HMAC-SHA512 will never reproduce a PBKDF2 output, which is a common confusion when trying to verify a stored derived key by hand.
- Signature mismatch on a webhook the provider says is valid
- You are almost certainly signing a re-serialised body. Frameworks parse JSON and re-encode it, changing key order, whitespace and unicode escaping. Capture the raw request body before any body-parser middleware runs, and sign those exact bytes.
- Hex vs Base64 signature encoding
- The same 512-bit code prints as 128 hex characters or as Base64. GitHub sends hex with a "sha256=" prefix; several other providers send Base64. Decode both sides to bytes before comparing.
- Key treated as text when the provider means bytes
- If a secret is given to you as hex or Base64, decode it to bytes before using it as the key. Passing the printable string as UTF-8 produces a different key and therefore a different code.
- Comparing signatures with == or ===
- A normal string comparison returns as soon as it finds a difference, leaking how much of the signature was correct and enabling byte-at-a-time forgery over many attempts. Use timingSafeEqual, hmac.compare_digest, hmac.Equal or MessageDigest.isEqual.
- Using a plain hash where a MAC is required
- sha256(secret + message) is not a MAC. Because SHA-256 is a Merkle-Damgard construction, an attacker holding one valid digest can append data and compute a valid digest for the longer message without knowing the secret. HMAC exists to prevent exactly that.
- A trailing newline in the message
- A shell heredoc, a file read, or echo will usually add \n where the signing side did not. If your code is close but wrong, try again without the final newline.
FAQ
- Is HMAC-SHA512 slower than HMAC-SHA256?
- Usually faster on 64-bit hardware, for anything but very short messages, because SHA-512 uses 64-bit words and 1024-bit blocks. On 32-bit platforms the reverse is true. The longer output does not imply more work per byte.
- Where is HMAC-SHA512 actually used?
- As HS512 in JWTs, in some payment and financial message formats, and - most often - as the pseudorandom function inside PBKDF2-HMAC-SHA512 and HKDF-SHA512, where it is a building block rather than something you call directly.
- What is the difference between SHA-512 and HMAC-SHA-512?
- SHA-512 is a plain hash - anyone can compute it for any input, so it only tells you whether data changed by accident. HMAC-SHA-512 mixes in a secret key, so only someone holding the key can produce or check the code. That is what makes it proof of origin as well as integrity.
- How long should the secret key be?
- Any length is accepted, but use at least 64 random bytes so the key matches the digest size. Keys longer than the hash block get hashed down first, so extreme length adds nothing. Generate it randomly - a human-chosen passphrase is guessable.
- Why does my HMAC differ from the server's for the same message?
- In order of likelihood: the body was re-serialised instead of signed raw; there is a trailing newline; the signature is Base64 on one side and hex on the other; or the key was used as text when it should have been decoded from hex or Base64.
- Can an HMAC be reversed to recover the key or the message?
- No - it is built on a one-way hash and has no inverse. A weak, guessable key can be brute-forced offline by an attacker holding one message and its code, which is the reason to use random keys.
- Is HMAC-SHA-512 still considered secure?
- Yes. HMAC's security does not rest on the collision resistance of its hash, which is why even HMAC-SHA1 has no practical break. HMAC-SHA-512 is a current, recommended choice.
- Is my secret key safe to paste here?
- The computation runs in your browser through the Web Crypto API; the key and message are never transmitted or stored. You can confirm that in your network tab, or load the page once and then use it offline.
Related reading
- What is HMAC?
- API signature tester sign a full request
- JWT verifier verify an HS256 token