Skip to main content

Implementation of the client side of the IETF draft "Signing HTTP Messages"

Project description

http-signature-client CircleCI Test Coverage

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.

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 as GET or POST.

  • 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 in headers_to_sign. These default to hop-by-hop-headers that are typically set by intermediaries.

Returns

The headers_to_sign argument concatanated with an authorization 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 created parameter is sent and signed [to allow the server to decide to reject if the skew is too large];
  • the headers parameter is sent and signed [to allow the server to verify headers and pseudo-headers];
  • the expired parameter is not sent [the server can decide this using the created parameter];
  • the algorithm parameter is not sent [it should not be used by the server to choose the algorithm].

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

http-signature-client-0.0.18.tar.gz (4.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

http_signature_client-0.0.18-py3-none-any.whl (4.9 kB view details)

Uploaded Python 3

File details

Details for the file http-signature-client-0.0.18.tar.gz.

File metadata

  • Download URL: http-signature-client-0.0.18.tar.gz
  • Upload date:
  • Size: 4.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/40.7.1 requests-toolbelt/0.8.0 tqdm/4.31.1 CPython/3.6.7

File hashes

Hashes for http-signature-client-0.0.18.tar.gz
Algorithm Hash digest
SHA256 9337b54389f36e4e5ed78afd42d03092c35f0a4887d0bb03a05016490780de88
MD5 468b44565565d753f895c02ecedcd830
BLAKE2b-256 1557f5cd56b2956a0d27db2fe1261e614d0bf30b5fd3d5e5b525e01fdb33e4a6

See more details on using hashes here.

File details

Details for the file http_signature_client-0.0.18-py3-none-any.whl.

File metadata

  • Download URL: http_signature_client-0.0.18-py3-none-any.whl
  • Upload date:
  • Size: 4.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/40.7.1 requests-toolbelt/0.8.0 tqdm/4.31.1 CPython/3.6.7

File hashes

Hashes for http_signature_client-0.0.18-py3-none-any.whl
Algorithm Hash digest
SHA256 2416fb10f07b55f9f6be39f1abeddebcebcc16ec7e8546efbd7d375386cba82e
MD5 567d6da8ca2af0e3a3c4f1d046617830
BLAKE2b-256 531a336a3f9891e9dce28e36750c0bda2fdb99e275940ae354236edf88c9e63a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page