Implementation of the client side of the IETF draft "Signing HTTP Messages"
Project description
http-signature-client

Utility function with an HTTP client agnostic Python implementation of the client side of the IETF draft "Signing HTTP Messages". No dependencies other than the standard library, but cryptography would typically be required in client code to load a private key.
See http-signature-server for a compatible server-side implementation.
Installation
pip install http-signature-client
Usage
from http_signature_client import sign_headers
def sign(data):
# Return a signature of `data`, for example using a private key
signed_headers = sign_headers(key_id, sign, method, path, headers_to_sign)
Recipe: HTTPX with PEM-encoded private key and SHA-512 body digest
from base64 import b64encode
import hashlib
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import httpx
from http_signature_client import sign_headers
class HttpSignature(httpx.Auth):
requires_request_body = True
def __init__(self, key_id, pem_private_key):
self.key_id = key_id
self.private_key = load_pem_private_key(
pem_private_key, password=None, backend=default_backend())
def auth_flow(self, request):
body_sha512 = b64encode(hashlib.sha512(r.content).digest()).decode('ascii')
headers_to_sign = tuple(request.headers.items()) + (('digest', f'SHA512={body_sha512}'),)
request.headers = httpx.Headers(sign_headers(
self.key_id, self.private_key.sign, request.method,
request.url.full_path, headers_to_sign))
yield r
# In real cases, take credentials from environment variables/secret store
response = httpx.post('https://postman-echo.com/post', data=b'The bytes', auth=HttpSignature(
key_id='my-key',
pem_private_key= \
b'-----BEGIN PRIVATE KEY-----\n' \
b'MC4CAQAwBQYDK2VwBCIEINQG5lNt1bE8TZa68mV/WZdpqsXaOXBHvgPQGm5CcjHp\n' \
b'-----END PRIVATE KEY-----\n',
)
)
Recipe: Python requests with PEM-encoded private key and SHA-512 body digest
from base64 import b64encode
import hashlib
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import requests
import urllib3
from http_signature_client import sign_headers
def HttpSignature(key_id, pem_private_key):
private_key = load_pem_private_key(
pem_private_key, password=None, backend=default_backend())
def sign(r):
body_sha512 = b64encode(hashlib.sha512(r.body).digest()).decode('ascii')
headers_to_sign = tuple(r.headers.items()) + (('digest', f'SHA512={body_sha512}'),)
parsed_url = urllib3.util.url.parse_url(r.path_url)
path = parsed_url.path + (f'?{parsed_url.query}' if parsed_url.query else '')
r.headers = dict(sign_headers(
key_id, private_key.sign, r.method, path, headers_to_sign))
return r
return sign
# In real cases, take credentials from environment variables/secret store
response = requests.post('https://postman-echo.com/post', data=b'The bytes', auth=HttpSignature(
key_id='my-key',
pem_private_key= \
b'-----BEGIN PRIVATE KEY-----\n' \
b'MC4CAQAwBQYDK2VwBCIEINQG5lNt1bE8TZa68mV/WZdpqsXaOXBHvgPQGm5CcjHp\n' \
b'-----END PRIVATE KEY-----\n',
)
)
Recipe: Create an Ed25519 public/private key pair
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption, PrivateFormat, PublicFormat
private_key = Ed25519PrivateKey.generate()
print(private_key.private_bytes(encoding=Encoding.PEM, format=PrivateFormat.PKCS8, encryption_algorithm=NoEncryption()))
print(private_key.public_key().public_bytes(encoding=Encoding.PEM, format=PublicFormat.SubjectPublicKeyInfo))
API
Parameters
-
key_id- The keyId parameter sent with the signature. Typically, the server treats this as the claimed identity of the client. -
sign- A function that signs the request bytes once canonicalised. Typically, this would be a function that uses a private key. -
method- The HTTP method of the request, such asGETorPOST. -
path- The full path of the request, including any query string. -
headers_to_sign- HTTP header names and values to sign. -
headers_to_ignore- HTTP header names to not be signed, even if passed inheaders_to_sign. These default to hop-by-hop-headers that are typically set by intermediaries.
Returns
The headers_to_sign argument concatanated with an signature header containing the HTTP signature.
What's implemented
A deliberate subset of the signature algorithm is implemented:
- the
(request-target)pseudo-header is sent and signed [to allow the server to verify the method and path]; - the
createdparameter is sent and signed as the(created)pseudo-header [to allow the server to decide to reject if the skew is too large]; - the
headersparameter is sent and signed [to allow the server to verify headers and pseudo-headers]; - the
expiresparameter is not sent [the server can decide this using the created parameter]; - the
algorithmparameter is not sent [it should not be used by the server to choose the algorithm].
The (request-target) and (created) pseudo-headers are always prepended to the list of real HTTP headers before canonicalisation.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file http-signature-client-0.0.22.tar.gz.
File metadata
- Download URL: http-signature-client-0.0.22.tar.gz
- Upload date:
- Size: 4.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.20.1 setuptools/47.1.1 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb71278152c4d37d8084e6554487a2456cadbebda5e9e34b44229fd2e93bf384
|
|
| MD5 |
bbb9b202dd27b2c2af979b6073039de6
|
|
| BLAKE2b-256 |
6c2d03462f80f350a1a9fb8c1660e0d10429526b3682da79a2f727e8bb904382
|
File details
Details for the file http_signature_client-0.0.22-py3-none-any.whl.
File metadata
- Download URL: http_signature_client-0.0.22-py3-none-any.whl
- Upload date:
- Size: 5.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.4.2 requests/2.20.1 setuptools/47.1.1 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
567ffefa37001f371ad76a7999306c241f3e0df5a53a6495bbe67739dca17033
|
|
| MD5 |
52af1718cd71366f5a5b374422ca7d16
|
|
| BLAKE2b-256 |
3bb24dc3eafcf37a13bf96831fa033859387f502f6dbdb3a2abb87e6308eda5f
|