HTTP Message Signatures
An implementation of the IETF HTTP Message Signatures draft standard, providing tools to sign and verify HTTP messages using various cryptographic algorithms. The current stable version is 2.0.1, with a moderate release cadence driven by specification updates and bug fixes.
Warnings
- breaking Python 3.8 and 3.9 are no longer supported. Version 2.0.0 dropped support for Python 3.9, and 2.0.1 explicitly removed metadata for Python 3.8/3.9. Projects must use Python 3.10 or newer.
- breaking Version 2.0.0 introduced support for multiple signatures on a single message. While this enhances functionality, it may involve changes to how signatures are added and retrieved from messages, potentially breaking existing code that assumed a single 'Signature' header.
- gotcha Versions prior to 1.0.1 had a bug in the `get_request_target` method, which could lead to an incorrect 'request-target' component in the signature (e.g., an extra '?'). This can cause signature mismatches between signers and verifiers on different versions.
- gotcha The `max_clock_skew` parameter was added to `HTTPMessageVerifier` in version 0.5.0. Without this parameter, the verifier does not automatically account for clock differences between the signer and verifier, potentially making systems vulnerable to replay attacks if not handled externally.
- gotcha Mismanagement of cryptographic keys (e.g., using weak algorithms, exposing private keys, or reusing keys across different contexts) is a common security vulnerability. While the library provides the signing mechanisms, key management is left to the user.
Install
-
pip install http-message-signatures
Imports
- HTTPSignatureKeySigner
from http_message_signatures import HTTPSignatureKeySigner
- HTTPSignatureKeyVerifier
from http_message_signatures import HTTPSignatureKeyVerifier
- HTTPMessageSignatures
from http_message_signatures import HTTPMessageSignatures
- algorithms
from http_message_signatures import algorithms
Quickstart
import requests
from http_message_signatures import HTTPSignatureKeySigner, HTTPSignatureKeyVerifier, algorithms
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
# 1. Generate a dummy RSA key pair (for demonstration)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
# Serialize keys for signer/verifier
private_jwk = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
).decode()
public_jwk = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode()
key_id = "test-key"
# 2. Prepare an HTTP request
request = requests.Request(
method="POST",
url="https://example.com/data",
headers={
"Host": "example.com",
"Content-Type": "application/json",
"Date": "Tue, 20 Apr 2021 02:07:55 GMT"
},
data='{"message": "hello world"}'
)
# 3. Sign the request
signer = HTTPSignatureKeySigner(
key_id=key_id,
private_key=private_jwk,
algorithm=algorithms.RSA_V1_5_SHA256
)
signed_request = signer.sign(request)
print("--- Signed Request Headers ---")
for k, v in signed_request.headers.items():
print(f"{k}: {v}")
# 4. Verify the signed request (simulate receiving the request)
verifier = HTTPSignatureKeyVerifier(
key_id=key_id,
public_key=public_jwk,
algorithm=algorithms.RSA_V1_5_SHA256
)
# Create a dummy requests.Response object for verification
# In a real scenario, this would be the actual received request
dummy_received_request = requests.Request(
method=signed_request.method,
url=signed_request.url,
headers=signed_request.headers,
data=signed_request.data
)
try:
is_valid = verifier.verify(dummy_received_request)
print(f"\nSignature is valid: {is_valid}")
except Exception as e:
print(f"\nSignature verification failed: {e}")