How to Generate HMAC
What Is HMAC?
HMAC (Hash-based Message Authentication Code) is an authentication method that combines cryptographic hash functions with a secret key. Unlike plain hashing, HMAC requires a shared secret key โ only those who know the key can generate or verify the HMAC. HMAC simultaneously provides two guarantees: message integrity (data hasn't been modified) and message authenticity (data genuinely came from the key holder).
/* HMAC formula */
HMAC(key, message) = Hash(
(key XOR opad) ||
Hash((key XOR ipad) || message)
)
/* Where:
opad = 0x5c repeated
ipad = 0x36 repeated
|| = concatenation */
Key Difference Between HMAC and Plain Hash
Plain hashing (like SHA256) is publicly computable โ anyone can calculate the hash of any data. This means an attacker can modify data and recalculate the hash to forge an integrity proof. HMAC solves this: without the key, a correct HMAC cannot be computed. Even if an attacker knows the data and the HMAC algorithm, they cannot forge the HMAC without the key.
Implementing HMAC-SHA256 in Various Languages
// JavaScript (Node.js)
const crypto = require('crypto');
function generateHmac(secret, message) {
return crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
}
const signature = generateHmac('my-secret-key', 'Hello World');
// Output: 'f59c9c0b1234567...' (64-char hex)
// Verify
function verifyHmac(secret, message, receivedHmac) {
const expected = generateHmac(secret, message);
// Use timingSafeEqual to prevent timing attacks!
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(receivedHmac, 'hex')
);
}
# Python
import hmac, hashlib
def generate_hmac(secret: str, message: str) -> str:
key = secret.encode('utf-8')
msg = message.encode('utf-8')
return hmac.new(key, msg, hashlib.sha256).hexdigest()
def verify_hmac(secret: str, message: str, received: str) -> bool:
expected = generate_hmac(secret, message)
# Use compare_digest to prevent timing attacks!
return hmac.compare_digest(expected, received)
signature = generate_hmac('my-secret-key', 'Hello World')
HMAC in API Request Signing
API signing is one of the most common HMAC use cases. Here's a typical HMAC-based API signing workflow (simplified reference to AWS Signature Version 4):
// API signing example
function signRequest(secretKey, method, path, body, timestamp) {
// 1. Create string to sign
const bodyHash = crypto.createHash('sha256')
.update(body).digest('hex');
const stringToSign = [
method.toUpperCase(),
path,
timestamp,
bodyHash
].join('\n');
// 2. Generate HMAC signature
const signature = crypto
.createHmac('sha256', secretKey)
.update(stringToSign)
.digest('hex');
return signature;
}
// Server verification
const expectedSig = signRequest(serverSecret, method, path, body, timestamp);
if (crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSig))) {
// Request is authentic
}
HMAC-SHA256 in JWT (HS256)
JWT's HS256 algorithm is HMAC-SHA256. JWT's structure is header.payload.signature, where signature is the Base64URL-encoded HMAC-SHA256 result of header.payload using the secret key. The server holding the key can verify whether the JWT has been tampered with; clients cannot forge valid JWTs (unless they know the key).
// JWT HS256 signature (simplified)
const header = Base64URL(JSON.stringify({ alg: "HS256", typ: "JWT" }));
const payload = Base64URL(JSON.stringify({ sub: "user123", exp: 1720000000 }));
const sigInput = `${header}.${payload}`;
const signature = Base64URL(
crypto.createHmac('sha256', jwtSecret).update(sigInput).digest()
);
const jwt = `${header}.${payload}.${signature}`;
HMAC vs Digital Signatures (RSA/ECDSA)
HMAC and digital signatures (like RSA-SHA256, ECDSA) both ensure message authenticity, but with a key difference: HMAC uses a symmetric key (same key for generating and verifying, shared by both parties) โ suitable only for mutually trusted scenarios. Digital signatures use asymmetric keys (private key signs, public key verifies) โ anyone with the public key can verify the signature, but only the private key holder can generate it โ suitable for publicly verifiable scenarios (like software package signing, certificates).
HMAC Security Best Practices
- Use HMAC-SHA256 or HMAC-SHA512; avoid HMAC-MD5 and HMAC-SHA1
- Key length should equal or exceed hash output length (HMAC-SHA256 uses 32 bytes or longer keys)
- Always use timing-safe comparison when verifying (like
crypto.timingSafeEqual) to prevent timing attacks - Include timestamp and nonce in API signatures to prevent replay attacks
- Keys should be stored securely (environment variables, key management services) and never hardcoded in source code
Try the free tool now
Use Free Tool โ