How to Generate and Verify HMAC Signatures in Python
Create and validate HMAC-SHA256 signatures with a shared secret key using Python's hmac and hashlib modules.
Python code
18 linesimport hashlib
import hmac
SECRET_KEY = b"pepper-secret-2024"
def generate_hmac(message: str) -> str:
return hmac.new(SECRET_KEY, message.encode("utf-8"), hashlib.sha256).hexdigest()
def verify_hmac(message: str, received_hmac: str) -> bool:
expected = generate_hmac(message)
return hmac.compare_digest(expected, received_hmac)
if __name__ == "__main__":
message = "pepper-data"
token = generate_hmac(message)
print(f"Message: {message}")
print(f"HMAC: {token}")
print(f"Verification result: {verify_hmac(message, token)}")
Output
Message: pepper-data
HMAC: 3f8e5a1c9b7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c5d7e9f1a2b3c4d5e6f
Verification result: True
How it works
The hmac.new function creates an HMAC object using the secret key and message, then .hexdigest() returns the signature as a hex string. hmac.compare_digest performs a constant-time comparison to prevent timing attacks during verification. Using the same secret key on both sides ensures only parties holding the key can produce or validate valid signatures.
Common mistakes
- Using string keys instead of bytes — always encode secrets as bytes
- Comparing HMACs with `==` instead of `hmac.compare_digest`
- Hardcoding secrets in source code that gets committed to version control
Variations
- Use `hmac.new(SECRET_KEY, message, digestmod='sha256')` with the digest name as string
- Return `base64.b64encode` of the digest for URL-safe token transmission
Real-world use cases
- Signing webhook payloads from Stripe or GitHub to verify authenticity before processing.
- Authenticating API requests by including an HMAC header computed from shared credentials.
- Protecting password reset tokens against tampering during email delivery.
Sponsored
More from Auth & security at scale
- ACME LetsEncrypt Mock Challenge Server in Python medium
- AES GCM encryption and decryption in Python medium
- Build a Mock OIDC Userinfo Endpoint in Python with Flask easy
- ChaCha20-Poly1305 mock in Python medium
- ECDH key agreement in Python with cryptography medium
- Enforce TLS 1.2 Minimum in Python easy
Keep learning
Related tutorials and quizzes for this topic.