http-message-signatures: An implementation of RFC 9421, the IETF HTTP Message Signatures standard

http-message-signatures is an implementation of the IETF RFC 9421 HTTP Message Signatures standard in Python.

Security considerations

It is recommended that you read and understand section 7 of the RFC, Security Considerations before using this library.

Installation

pip install http-message-signatures

Synopsis

import hashlib

import requests
from http_sf.compat import Dictionary
from http_message_signatures import HTTPMessageSigner, HTTPMessageVerifier, HTTPSignatureKeyResolver, algorithms

class MyHTTPSignatureKeyResolver(HTTPSignatureKeyResolver):
    keys = {"my-key": b"top-secret-key"}

    def resolve_public_key(self, key_id: str):
        return self.keys[key_id]

    def resolve_private_key(self, key_id: str):
        return self.keys[key_id]

request = requests.Request('POST', 'https://example.com/foo?param=Value&Pet=dog', json={"hello": "world"})
request = request.prepare()
request.headers["Content-Digest"] = str(Dictionary({"sha-256": hashlib.sha256(request.body).digest()}))

signer = HTTPMessageSigner(signature_algorithm=algorithms.HMAC_SHA256, key_resolver=MyHTTPSignatureKeyResolver())
signer.sign(request, key_id="my-key", covered_component_ids=("@method", "@authority", "@target-uri", "content-digest"))

verifier = HTTPMessageVerifier(signature_algorithm=algorithms.HMAC_SHA256, key_resolver=MyHTTPSignatureKeyResolver())
verifier.verify(request)

Note that verifying the body content-digest is outside the scope of this package’s functionality, so it remains the caller’s responsibility. The requests-http-signature library builds upon this package to provide integrated signing and validation of the request body.

See what is signed

It is important to understand and follow the best practice rule of “See what is signed” when verifying HTTP message signatures. The gist of this rule is: if your application neglects to verify that the information it trusts is what was actually signed, the attacker can supply a valid signature but point you to malicious data that wasn’t signed by that signature. Failure to follow this rule can lead to vulnerability against signature wrapping and substitution attacks.

In http-message-signatures, you can ensure that the information signed is what you expect to be signed by only trusting the data returned by the verify() method:

verify_results = verifier.verify(request)

This returns a list of VerifyResult s, which are namedtuple s with the following attributes:

  • label (str): The label for the signature

  • algorithm: (same as signature_algorithm above)

  • covered_components: A mapping of component names to their values, as covered by the signature

  • parameters: A mapping of signature parameters to their values, as covered by the signature

  • body: Always None (the requests-http-signature package implements returning the body upon successful digest validation).

Remote and asynchronous signing

Use prepare() and apply() when a KMS, HSM, SSH agent, or asynchronous client holds the signing key. prepare() builds the RFC 9421 signature base without reading a private key or modifying the message. It accepts the signing options used by sign(), including covered components, creation and expiration times, nonce, tag, label, and include_alg. The returned PreparedSignature contains immutable bytes and strings. apply() writes the Signature-Input and Signature headers after the external signer returns.

This example expects an asynchronous sign_base function that signs the supplied bytes with Ed25519 and returns the raw 64-byte signature. The client must use the private key identified by key_id:

from collections.abc import Awaitable, Callable

from http_message_signatures import HTTPMessageSigner, algorithms

async def sign_request(request, key_id: str, sign_base: Callable[[bytes], Awaitable[bytes]]):
    signer = HTTPMessageSigner(signature_algorithm=algorithms.ED25519)
    prepared = signer.prepare(
        request,
        key_id=key_id,
        label="sig1",
        covered_component_ids=("@method", "@authority", "@target-uri", "content-digest"),
    )
    signature = await sign_base(prepared.signature_base)
    signer.apply(request, prepared, signature)

Populate Content-Digest from the request body before this example runs, as in the synopsis above. The same two calls work with a synchronous signer or a signing job completed in another process. No local key_resolver is needed. The existing sign() method still resolves a local key and performs all three steps synchronously.

Pass prepared.signature_base to the signing algorithm as bytes, without base64 encoding or a trailing newline. prepared.signature_params is the serialized @signature-params value; it is useful for inspecting the covered component list and signed metadata. Do not rebuild either value yourself. The selected algorithm applies to the remote signer even when include_alg=False; omitting the parameter does not negotiate an algorithm.

Remote clients differ in their accepted input and output formats. Configure the algorithm as specified in RFC 9421 section 3.3. Ed25519 signs the base without prehashing. For RSA and ECDSA, a client that accepts raw messages must perform the specified hashing; a client that accepts a digest must receive that digest and be configured to avoid hashing it a second time. The RSA-PSS profile uses SHA-512, MGF1 with SHA-512, and a 64-byte salt.

apply() expects raw signature bytes and performs the Structured Field base64 encoding. Decode base64 responses from a service before calling it. ECDSA P-256 clients often return DER, while RFC 9421 requires a 64-byte concatenation of r and s, each padded to 32 bytes. Convert DER output using the algorithm’s helper:

signer = HTTPMessageSigner(signature_algorithm=algorithms.ECDSA_P256_SHA256)
prepared = signer.prepare(request, key_id="p256-key")
# der_signature is the client's ECDSA P-256/SHA-256 signature of prepared.signature_base.
signature = algorithms.ECDSA_P256_SHA256.signature_from_der(der_signature)
signer.apply(request, prepared, signature)

Retain the prepared object and associate each remote result with its input. Use a trusted signing service and protect queued signing input against modification and disclosure: the base contains covered header and URL values. apply() compares the message’s current signature base with the prepared bytes and rejects changes before writing headers. For example, changing a covered method from POST to DELETE while awaiting the signature is rejected. Changes to uncovered values or values with the same RFC normalization are permitted. This check does not authenticate the prepared object, verify the returned signature, or check a body against its digest. Applications must handle those responsibilities and keep the message stable during apply() and until it is sent.

Creation and expiration times are set during preparation and are not refreshed by apply(). Allow for signing delays when choosing expiration times; an expired result will still be rejected by a verifier. To add the result alongside existing signatures, pass append_if_signature_exists=True to apply(). Duplicate labels or malformed existing signature headers are rejected. By default it replaces existing signature headers, just like sign().

Structured Field serialization

Use the sf component parameter to cover a whole header field using Structured Fields serialization:

signer.sign(request, key_id="my-key", covered_component_ids=('"priority";sf',))

For example, Priority: u=03,   i=?1 produces this signature-base line:

"priority";sf: u=3, i

The field’s Item, List, or Dictionary type is selected by its name using http_sf’s registry. Both endpoints must use matching type definitions. The registry may omit otherwise standardized fields; an unknown type or a value invalid for its registered type raises HTTPMessageSignaturesException during signing, or InvalidSignature during verification. The library does not infer types from field values. Headers covered without sf use the ordinary HTTP field normalization rules and do not require a registry entry.

The registry also contains retrofit mappings for legacy headers whose parsing rules can differ from Structured Fields. For example, this header:

Cache-Control: max-age=3600

can be changed to:

Cache-Control: max-age=0, max-age=3600

Both produce the same signed component because Structured Fields Dictionary parsing keeps the last duplicate:

"cache-control";sf: max-age=3600

However, RFC 9111 section 4.2.1 says duplicate freshness directives should use the first occurrence or make the response stale. The change can therefore alter caching behavior without invalidating a signature that covers "cache-control";sf. The retrofit draft acknowledges this difference in duplicate handling.

Omit ;sf when the application’s or other HTTP consumers’ interpretation differs from Structured Fields semantics, or compatibility is unknown. For example, use covered_component_ids=("cache-control",) to keep both duplicate directives in the signed value; adding one then changes the signature base. Use ;sf for fields defined as Structured Fields, or legacy fields only when compatible interpretation is enforced. Applications must still validate header values: successful signature verification does not establish their semantic validity.

Repeated field lines are combined before parsing. In a response signature, '"priority";req;sf' uses the associated request’s field. Dictionary-member selection with key continues to work without a registry entry and may include the redundant sf flag. The bs and tr parameters remain unsupported.

URL components

The request’s url supplies the target URL. URL components preserve percent-encoded octets and distinguish an absent query from an empty query. @authority omits credentials and default ports; @target-uri excludes credentials and fragments. Empty paths normally become /. For OPTIONS, @target-uri preserves an empty path so server-wide OPTIONS * remains distinct from OPTIONS /. The separate @path component still represents an empty path as /.

Supply an encoded absolute URL with a nonempty host. Parsing preserves the supplied spelling and rejects invalid syntax, including malformed percent escapes (%, %2, or %ZZ), spaces, control characters, and unencoded non-ASCII characters. It never repairs input. Each signature component applies its own RFC 9421 formatting rules. This applies during signing, verification, applying a prepared signature, and resolving request components covered by a response signature. A response’s own url is not used; components marked req use its associated request.

For @request-target, clients can supply message.request_target containing the HTTP/1.1 request target. This is necessary for absolute-form proxy requests and asterisk-form OPTIONS * requests, since those forms cannot be inferred from an absolute URL. Without this attribute, the resolver derives origin form from the URL, or authority form for CONNECT. The supplied target and URL must describe the same request.

Multiple signatures

The message’s headers.items() must expose every field line, or values already combined in wire order. Repeated field names are compared case-insensitively, normalized individually, and joined with a comma followed by a space before signing or parsing Structured Fields. Containers that discard repeated field lines before passing them to this library cannot be used to verify those fields reliably. Signing also requires header assignment and deletion; existing Signature and Signature-Input field lines are replaced together, including when appending a signature.

An HTTP request can potentially have multiple signatures. By default, http-message-signatures overwrites any existing signature when signing, and requires exactly one signature to be present when verifying. Before enabling multi-signature support, please read section 7.2.6 of the RFC, Multiple Signature Confusion.

To append a signature to a message that might already carry an existing signature without overwriting it, use:

signer.sign(request, append_if_signature_exists=True, ...)

To identify a signature using a tag parameter and verify it on a message possibly carrying multiple signatures, use:

verifier.verify(request, expect_tag="my_app_tag")

This will filter all signatures down to only those that set the tag to the expected value, verify each of them, and return a list of VerifyResult s. Verifying multiple signatures without prior knowledge of the application tag is not supported.

Error handling

Input request methods must be uppercase strings, including the request associated with a response. Methods such as get or Fetch are rejected before signature construction or verification; the library does not change their case. This applies even when @method is not covered.

Individual query parameters use the component name @query-param with a name parameter, for example '"@query-param";name="Pet"'. The previous spelling @query-params is rejected.

Signatures require a created timestamp by default. To accept signatures without one, set verifier.require_created = False and pass max_age=None to verify(); an age limit cannot be enforced without a creation timestamp. Expiration checks still apply when expires is present.

If multiple signatures are found in the request but expect_tag=... was not passed, then InvalidSignature is raised. Also, the verify() method raises HTTPMessageSignaturesException or an exception derived from this class in case an error occurs (unable to load PEM key, unsupported algorithm specified in signature input, signature doesn’t match digest etc.)

Authors

  • Andrey Kislyuk <https://kislyuk.com>

License

Copyright 2022-2026, Andrey Kislyuk and http-message-signatures contributors. Licensed under the terms of the Apache License, Version 2.0. Distribution of attribution information, LICENSE and NOTICE files with source copies of this package and derivative works is REQUIRED as specified by the Apache License.

API documentation

class http_message_signatures.HTTPSignatureAlgorithm(public_key=None, private_key=None)[source]

Interface for explicitly configured signature algorithm implementations.

Subclasses define algorithm_id and implement initialization, sign(), and verify(). Pass the subclass as signature_algorithm when creating a signer or verifier; no registration is required. The configured class is used regardless of whether the signature includes an alg parameter. When present, alg must match the class’s algorithm_id.

algorithm_id: str
sign(message)[source]

Return signature bytes for the supplied message bytes.

Parameters:

message (bytes)

verify(signature, message)[source]

Return None for a valid signature or raise if verification fails.

Parameters:
  • signature (bytes)

  • message (bytes)

exception http_message_signatures.HTTPMessageSignaturesException[source]

Base class for exceptions raised by http_message_signatures

args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception http_message_signatures.InvalidSignature[source]

Class for exceptions raised in the course of verifying an HTTP message signature

args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

class http_message_signatures.HTTPSignatureComponentResolver(message)[source]

Resolve covered components from a request or response object.

The constructor reads headers. Requests also supply an uppercase method and an encoded absolute url with a nonempty host. Invalid URL syntax is rejected, including malformed percent escapes and unencoded spaces, controls, or non-ASCII characters. The presence of status_code identifies a response; its request supplies components qualified with req. An associated request must also have an uppercase method. The response’s own url is not used.

headers.items() must expose string names and values for all field lines in order, or values already combined in that order. Signing additionally requires iteration over names, membership testing, assignment, and deletion.

A request may provide request_target for an HTTP/1.1 target that cannot be inferred from url, such as an absolute-form proxy target or *. The supplied target and URL must describe the same request.

Whole-field sf serialization uses the field type registered in http_sf. Unknown types and malformed values raise HTTPMessageSignaturesException.

field_component_parameters = frozenset({'bs', 'key', 'sf', 'tr'})
derived_component_resolvers: ClassVar[dict[str, Callable[[...], str]]] = {'@authority': <function HTTPSignatureComponentResolver.<lambda>>, '@method': <function HTTPSignatureComponentResolver.<lambda>>, '@path': <function HTTPSignatureComponentResolver.<lambda>>, '@query': <function HTTPSignatureComponentResolver.<lambda>>, '@query-param': <function HTTPSignatureComponentResolver.<lambda>>, '@request-target': <function HTTPSignatureComponentResolver.<lambda>>, '@scheme': <function HTTPSignatureComponentResolver.<lambda>>, '@status': <function HTTPSignatureComponentResolver.<lambda>>, '@target-uri': <function HTTPSignatureComponentResolver.<lambda>>}

Callables receive the resolver instance followed by component parameters. Subclasses can copy and extend this mapping to support additional components.

resolve(component_node)[source]

Return the string value of a parsed Structured Field component item.

Parameters:

component_node (Item)

get_dictionary_member(component_id, field_value, params)[source]
Parameters:
  • component_id (str)

  • field_value (str)

  • params (dict)

get_method()[source]
get_target_uri()[source]
get_authority()[source]
get_scheme()[source]
get_request_target()[source]
get_path()[source]
get_query()[source]
get_query_params(*, name)[source]
Parameters:

name (str)

get_status()[source]
class http_message_signatures.HTTPSignatureKeyResolver[source]

Subclass this interface to look up keys by their signed key IDs.

Return PEM bytes or the appropriate cryptography key object for asymmetric algorithms. For HMAC, return the same secret bytes from both methods.

resolve_public_key(key_id)[source]

Return the verification key for key_id; None means unavailable.

Parameters:

key_id (str)

resolve_private_key(key_id)[source]

Return the signing key for key_id or raise if it is unavailable.

Parameters:

key_id (str)

class http_message_signatures.HTTPMessageSigner(*, signature_algorithm, key_resolver=None, component_resolver_class=<class 'http_message_signatures.resolvers.HTTPSignatureComponentResolver'>)[source]

Construct HTTP message signatures and write their headers to a message.

Configure the algorithm and component resolver used for signing.

Parameters:
  • signature_algorithm (type[HTTPSignatureAlgorithm]) – Built-in or custom subclass of HTTPSignatureAlgorithm.

  • key_resolver (HTTPSignatureKeyResolver | None) – Required by sign() for local signing. May be omitted when using prepare() and apply() with an external signer.

  • component_resolver_class (type) – Resolver class instantiated for each message.

DEFAULT_SIGNATURE_LABEL = 'pyhms'
sign(message, *, key_id, created=None, expires=None, nonce=None, label=None, tag=None, include_alg=True, covered_component_ids=('@method', '@authority', '@target-uri'), append_if_signature_exists=False)[source]

Sign a message in place; return None.

Parameters:
  • message – Request or response implementing the message interface documented in HTTPSignatureComponentResolver.

  • key_id (str) – ID passed to key_resolver.resolve_private_key and included in the signed metadata.

  • created (datetime | None) – Creation time; defaults to the current time. A naive datetime is interpreted in the local timezone.

  • expires (datetime | None) – Optional expiration time, with the same timezone rules.

  • nonce (str | None) – Optional signed nonce. Replay detection is the caller’s responsibility.

  • label (str | None) – Signature dictionary key; defaults to pyhms.

  • tag (str | None) – Optional signed application identifier.

  • include_alg (bool) – Include the configured algorithm ID in signed metadata.

  • covered_component_ids (Sequence[str]) – Sequence of component names or serialized Structured Field String items, for example ("date",) or ('"@query-param";name="Pet"',). A scalar string is rejected. Defaults cover request method, authority, and target URL. Responses require an explicit list such as ("@status", "content-type").

  • append_if_signature_exists (bool) – Preserve existing signatures and add a new label. Otherwise replace existing signature headers.

Raises:

HTTPMessageSignaturesException – A component or signature parameter is invalid or an existing signature cannot be appended to.

Key resolver and algorithm errors can also propagate. Signing does not calculate a body digest; supply and cover a digest header when needed.

prepare(message, *, key_id, created=None, expires=None, nonce=None, label=None, tag=None, include_alg=True, covered_component_ids=('@method', '@authority', '@target-uri'))[source]

Build the signing input without resolving a key or changing the message.

Arguments have the same meaning and defaults as in sign(). Creation time is captured here, before any remote signing delay. Header append or replacement policy is selected later in apply().

Returns:

Immutable signing input for a synchronous or asynchronous signer. Sign signature_base with the configured algorithm and the key identified by key_id, then pass the resulting bytes to apply().

Raises:

HTTPMessageSignaturesException – A component, parameter, or label is invalid.

Parameters:
  • key_id (str)

  • created (datetime | None)

  • expires (datetime | None)

  • nonce (str | None)

  • label (str | None)

  • tag (str | None)

  • include_alg (bool)

  • covered_component_ids (Sequence[str])

Return type:

PreparedSignature

The component resolver must return stable values for unchanged messages; apply() resolves them again to check for changes while signing.

apply(message, prepared, signature, *, append_if_signature_exists=False)[source]

Attach an externally generated signature to a message in place.

Parameters:
  • message – Message whose covered values still match prepared.

  • prepared (PreparedSignature) – Input returned by prepare() with the same algorithm and component resolver configuration.

  • signature (bytes) – Raw algorithm output, before base64 encoding. ECDSA P-256 requires 64 bytes containing r || s; convert DER output with ECDSA_P256_SHA256.signature_from_der() first.

  • append_if_signature_exists (bool) – Preserve existing signatures and add the prepared label. Otherwise replace existing signature headers.

Raises:

HTTPMessageSignaturesException – Covered values changed, prepared input is inconsistent, signature bytes are empty, or headers cannot be appended to. Validation completes before changing headers.

Return type:

None

This method neither resolves keys nor verifies the signature. The caller must associate the result with the prepared input and use a trusted signer with the intended key and algorithm. The comparison covers signature-base values only; it does not validate the body against a digest header, check expiration, or prevent concurrent message mutation. Keep the message stable during this call and until it is sent.

signature_metadata_parameters: ClassVar[set[str]] = {'alg', 'created', 'expires', 'keyid', 'nonce', 'tag'}
class http_message_signatures.HTTPMessageVerifier(*, signature_algorithm, key_resolver, component_resolver_class=<class 'http_message_signatures.resolvers.HTTPSignatureComponentResolver'>)[source]

Verify signatures using a configured algorithm and key resolver.

Configure signing or verification.

Parameters:
max_clock_skew: timedelta = datetime.timedelta(seconds=5)

Tolerance for creation, expiration, and maximum-age checks.

require_created: bool = True

Require a creation timestamp. Set False and use max_age=None to allow its omission.

allow_label_only_selection: bool = False

Permit label selection without a signed tag, with a warning.

validate_created_and_expires(sig_input, max_age=None)[source]
verify(message, *, max_age=datetime.timedelta(days=1), expect_tag=None, expect_label=None)[source]

Return verified signatures matching the requested selection.

Parameters:
  • message – Request or response implementing the interface documented in HTTPSignatureComponentResolver.

  • max_age (timedelta | None) – Maximum age since creation, with clock-skew tolerance. None disables the age limit; expiration checks still apply.

  • expect_tag (str | None) – Require this signed application tag. Required when selecting among multiple signatures unless label-only selection is enabled.

  • expect_label (str | None) – Also select this signature dictionary label. Labels are not signed; normally expect_tag must be supplied with this option.

Returns:

A list of VerifyResult objects. Every selected signature must verify; no partial result is returned.

Raises:
Return type:

list[VerifyResult]

Check the returned coverage before trusting message data. Body digest validation, authorization, and nonce replay detection remain the caller’s responsibility. A valid signature alone does not establish those properties.

signature_metadata_parameters: ClassVar[set[str]] = {'alg', 'created', 'expires', 'keyid', 'nonce', 'tag'}
class http_message_signatures.PreparedSignature(signature_base, signature_params, label, algorithm_id)[source]

Immutable signing input returned by HTTPMessageSigner.prepare().

Variables:
  • signature_base – ASCII bytes to pass to the signing algorithm, without additional encoding or an added newline.

  • signature_params – Serialized @signature-params value, including the covered component list and signed metadata.

  • label – Signature dictionary label (not covered by the signature).

  • algorithm_id – Configured algorithm ID, retained even when alg is omitted.

Parameters:
  • signature_base (bytes)

  • signature_params (str)

  • label (str)

  • algorithm_id (str)

Retain this object until applying the corresponding signature. It contains covered message values, which may be sensitive. Immutability prevents accidental edits; it does not authenticate data sent through a queue or service.

signature_base: bytes
signature_params: str
label: str
algorithm_id: str
class http_message_signatures.VerifyResult(label, algorithm, covered_components, parameters, body, nonce)[source]

A verified signature and the values it covers.

Variables:
  • label – Signature dictionary label (not covered by the signature).

  • algorithm – Algorithm class used for verification.

  • covered_components – Serialized component identifiers mapped to signed values, including the generated "@signature-params" entry.

  • parameters – Signed signature metadata, such as keyid and created.

  • body – None in this package; reserved for integrations validating body digests.

  • nonce – Signed nonce, or None when omitted.

Parameters:
  • label (str)

  • algorithm (type[HTTPSignatureAlgorithm])

  • covered_components (dict[str, str])

  • parameters (dict[str, int | str])

  • body (bytes | None)

  • nonce (str | None)

Create new instance of VerifyResult(label, algorithm, covered_components, parameters, body, nonce)

label: str

Alias for field number 0

algorithm: type[HTTPSignatureAlgorithm]

Alias for field number 1

covered_components: dict[str, str]

Alias for field number 2

parameters: dict[str, int | str]

Alias for field number 3

body: bytes | None

Alias for field number 4

nonce: str | None

Alias for field number 5

count(value, /)

Return number of occurrences of value.

index(value, start=0, stop=9223372036854775807, /)

Return first index of value.

Raises ValueError if the value is not present.

Algorithms

class http_message_signatures.algorithms.ECDSA_P256_SHA256(public_key=None, private_key=None, password=None)[source]

ECDSA P-256 with SHA-256, using PEM bytes or cryptography EC key objects.

password decrypts an encrypted PEM private key when provided.

algorithm_id: str = 'ecdsa-p256-sha256'
sign(message)[source]

Return signature bytes for the supplied message bytes.

Parameters:

message (bytes)

static signature_from_der(signature)[source]

Convert a DER-encoded ECDSA P-256 signature to RFC 9421 signature bytes.

Returns the 64-byte concatenation of unsigned, big-endian r and s, each padded to 32 bytes (RFC 9421 section 3.3.4). Use this for a remote signer’s DER output before calling HTTPMessageSigner.apply(). Malformed DER or integers that do not fit raise HTTPMessageSignaturesException. Conversion does not verify the signature or the signer’s choice of curve.

Parameters:

signature (bytes)

Return type:

bytes

verify(signature, message)[source]

Return None for a valid signature or raise if verification fails.

Parameters:
  • signature (bytes)

  • message (bytes)

load_pem_keys(public_key=None, private_key=None, password=None)
class http_message_signatures.algorithms.ED25519(public_key=None, private_key=None, password=None)[source]

Ed25519 using PEM bytes or cryptography Ed25519 key objects.

password decrypts an encrypted PEM private key when provided.

algorithm_id: str = 'ed25519'
sign(message)[source]

Return signature bytes for the supplied message bytes.

Parameters:

message (bytes)

verify(signature, message)[source]

Return None for a valid signature or raise if verification fails.

Parameters:
  • signature (bytes)

  • message (bytes)

load_pem_keys(public_key=None, private_key=None, password=None)
class http_message_signatures.algorithms.HMAC_SHA256(public_key=None, private_key=None, shared_secret=None)[source]

HMAC with SHA-256 using secret bytes.

Supply shared_secret, public_key, or private_key. At least one is required, and all supplied values must agree. The key resolver integration uses private_key for signing and public_key for verification.

algorithm_id: str = 'hmac-sha256'
sign(message)[source]

Return signature bytes for the supplied message bytes.

Parameters:

message (bytes)

verify(signature, message)[source]

Return None for a valid signature or raise if verification fails.

Parameters:
  • signature (bytes)

  • message (bytes)

class http_message_signatures.algorithms.RSA_PSS_SHA512(public_key=None, private_key=None, password=None)[source]

RSA-PSS with SHA-512. Keys may be PEM bytes or cryptography RSA key objects.

password decrypts an encrypted PEM private key when provided.

algorithm_id: str = 'rsa-pss-sha512'
sign(message)[source]

Return signature bytes for the supplied message bytes.

Parameters:

message (bytes)

verify(signature, message)[source]

Return None for a valid signature or raise if verification fails.

Parameters:
  • signature (bytes)

  • message (bytes)

load_pem_keys(public_key=None, private_key=None, password=None)
class http_message_signatures.algorithms.RSA_V1_5_SHA256(public_key=None, private_key=None, password=None)[source]

RSA PKCS#1 v1.5 with SHA-256; accepts the same key arguments as RSA-PSS.

algorithm_id: str = 'rsa-v1_5-sha256'
padding: AsymmetricPadding
hash_algorithm: HashAlgorithm
load_pem_keys(public_key=None, private_key=None, password=None)
sign(message)

Return signature bytes for the supplied message bytes.

Parameters:

message (bytes)

verify(signature, message)

Return None for a valid signature or raise if verification fails.

Parameters:
  • signature (bytes)

  • message (bytes)

class http_message_signatures.algorithms.HTTPSignatureAlgorithm(public_key=None, private_key=None)[source]

Interface for explicitly configured signature algorithm implementations.

Subclasses define algorithm_id and implement initialization, sign(), and verify(). Pass the subclass as signature_algorithm when creating a signer or verifier; no registration is required. The configured class is used regardless of whether the signature includes an alg parameter. When present, alg must match the class’s algorithm_id.

algorithm_id: str
sign(message)[source]

Return signature bytes for the supplied message bytes.

Parameters:

message (bytes)

verify(signature, message)[source]

Return None for a valid signature or raise if verification fails.

Parameters:
  • signature (bytes)

  • message (bytes)

Release Notes

Changes for v2.0.1 (2026-01-18)

Remove Python 3.8 and 3.9 from PyPI metadata

Changes for v2.0.0 (2026-01-02)

  • Multiple signature support (#22)

  • Drop Python 3.9 support

Changes for v1.0.1 (2026-01-01)

  • Remove extra ? in get_request_target (#19)

Changes for v1.0.0 (2025-07-26)

  • Vendor http_sfv

  • Package is stable

Changes for v0.7.0 (2025-06-29)

  • fix: convert url to string in HTTPSignatureComponentResolver constructor (#18)

Changes for v0.6.1 (2025-05-29)

  • Switch to trusted publishing

Changes for v0.6.0 (2025-05-29)

  • [http-message-signatures-add] Add support for ‘tag’ signature parameter (#17)

Changes for v0.5.0 (2024-02-21)

  • Add max_clock_skew to HTTPMessageVerifier (#11)

  • Documentation and test infrastructure improvements

Changes for v0.4.4 (2022-08-31)

  • Fix call signature in RSA_V1_5_SHA256 algorithm

  • Test improvements

Changes for v0.4.3 (2022-04-19)

  • Relocate VerifyResult

Changes for v0.4.2 (2022-04-19)

  • Fix casting bug

Changes for v0.4.1 (2022-04-19)

  • Add type information

  • Clean up algorithms namespace

  • Use max-age=24h by default

Changes for v0.4.0 (2022-04-14)

  • Validate created and expires params

Changes for v0.3.0 (2022-04-12)

  • Support query parameter addressing

Changes for v0.2.3 (2022-04-12)

  • Raise InvalidSignature in more code paths

  • Return related request method

  • Clarify that sign and verify work on both requests and responses

Changes for v0.2.2 (2022-04-10)

  • Add body placeholder

Changes for v0.2.1 (2022-04-10)

  • Return VerifyResult in verifier

Changes for v0.2.0 (2022-04-10)

  • Organize exceptions

Changes for v0.1.0 (2022-04-09)

  • Initial release.

Changes for v0.0.3 (2022-04-04)

  • Begin http-message-signatures

Table of Contents