Python SDK to generate and validate HTTP message signatures per RFC 9421 and RFC 9530
Project description
eBay Digital Signature SDK for Python
HTTP message signatures provide a mechanism for end-to-end authenticity and integrity for components of an HTTP message.
This Python SDK is designed to simplify the process of generating digital signature headers and also provides a method to validate the digital signature headers.
Table of contents
- Digital Signatures for Public API Calls
- Features
- Installation
- Getting Started
- Usage
- Configuration
- Key Generation
- Logging
- License
Digital Signatures for Public API Calls
Due to regulatory requirements emanating from SCA for our European/UK sellers, we are requiring our developers to add a digital signature for every HTTP call that is made on behalf of a EU/UK seller to certain APIs.
This SDK is generic and the signature scheme is compliant with these finalized IETF standards:
Features
This SDK is intended to generate required message signature headers, as per the above IETF standards, and also provides a way to verify signature headers. There is also an example Python FastAPI service included with the SDK.
This SDK incorporates:
- Generation of the following HTTP message signature headers:
- Content-Digest: This header includes a SHA-256 or SHA-512 digest over the HTTP payload (as specified in RFC 9530 Digest Fields), if any. It is not required to be sent for APIs that do not include a request payload (e.g. GET requests).
- Signature-Input: This header indicates which headers and pseudo-headers are included, as well as the order in which they are used when calculating the signature. It is created as specified in RFC 9421 HTTP Message Signatures.
- Signature: The value of the Signature header is created as described in Section 3.1, Creating a Signature, of RFC 9421. It uses the Private Key value generated by the Key Management API.
- x-ebay-signature-key: This header includes the JWE that is created using the Key Management API.
sign_messagemethod to sign the incoming request objectvalidate_signed_messagemethod to validate the signature of the incoming request object- There are individual methods as well to generate and validate the headers:
generate_content_digestgenerate_signaturebuild_signature_inputbuild_signature_basevalidate_content_digestvalidate_signature
For more details on Digital Signatures for eBay APIs, please refer to the documentation.
Usage
Prerequisites
- Python: 3.9 or higher
- pip: Latest version recommended
Installation
pip install digital-signature-python-sdk
Quick Start
from digital_signature_sdk import sign_message, RequestToSign
# Load your private key
with open('path/to/privatekey.pem', 'rb') as f:
private_key = f.read()
# Create a request to sign
request = RequestToSign(
method="POST",
path="/v1/sell/finances/transaction",
authority="api.ebay.com",
body=b'{"hello": "world"}',
jwe="<JWE from Key Management API>"
)
# Generate signature headers
headers = sign_message(
request,
private_key,
digest_algo="sha-256",
algorithm="RSA" # or "ED25519"
)
# Add headers to your HTTP request
print(headers)
# {
# 'Content-Digest': 'sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:',
# 'x-ebay-signature-key': '<JWE>',
# 'Signature-Input': 'sig1=("content-digest" "x-ebay-signature-key" "@method" "@path" "@authority");created=1672531200',
# 'Signature': 'sig1=:MEUCIQDZm...aGFz:'
# }
Getting Started
Quick Setup & Run Example Server
The fastest way to get started with the SDK is to run the included example server:
# 1. Clone the repository
git clone https://github.com/eBay/digital-signature-python-sdk.git
cd digital-signature-python-sdk
# 2. Create virtual environment (optional but recommended)
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -e ".[dev,examples]"
# 4. Generate development keys (required - no keys included for security)
python scripts/setup_dev_keys.py
# 5. Set up environment variables
cp .env.example .env
# Edit .env file and set your EBAY_JWE_TOKEN
# 6. Start the example server
python examples/server.py
The server will start at http://localhost:8080
Testing the Server
Once the server is running, test it with these curl commands:
Generate Signature Headers
curl -X POST http://localhost:8080/sign \
-H "Content-Type: application/json" \
-d '{"orderId": "12345", "amount": 99.99}' \
-s | jq .
Example Response:
{
"content-digest": "sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:",
"x-ebay-signature-key": "your-jwe-token",
"signature-input": "sig1=(\"content-digest\" \"x-ebay-signature-key\" \"@method\" \"@path\" \"@authority\");created=1767602259",
"signature": "sig1=:MEUCIQDZm...aGFz:"
}
Sign Complete Request
curl -X POST http://localhost:8080/sign-request \
-H "Content-Type: application/json" \
-d '{"message": "Hello eBay API"}' \
-i
Validate Signatures
With jq (recommended):
# First generate a signature
RESPONSE=$(curl -s -X POST http://localhost:8080/sign \
-H "Content-Type: application/json" \
-d '{"test": "validation"}')
# Then validate it (requires jq)
echo $RESPONSE | jq -r '{
"method": "POST",
"path": "/ws/api.dll",
"authority": "api.sandbox.ebay.com",
"body": "{\"test\": \"validation\"}",
"headers": .
}' | curl -X POST http://localhost:8080/validate \
-H "Content-Type: application/json" \
-d @- \
-w "Status: %{http_code}\n"
Without jq (manual process):
# 1. Generate signature and copy the output
curl -X POST http://localhost:8080/sign \
-H "Content-Type: application/json" \
-d '{"test": "validation"}'
# 2. Use the signature components in validation (replace with actual values)
curl -X POST http://localhost:8080/validate \
-H "Content-Type: application/json" \
-d '{
"method": "POST",
"path": "/ws/api.dll",
"authority": "api.sandbox.ebay.com",
"body": "{\"test\": \"validation\"}",
"headers": {
"content-digest": "PASTE_CONTENT_DIGEST_HERE",
"x-ebay-signature-key": "PASTE_JWE_TOKEN_HERE",
"signature-input": "PASTE_SIGNATURE_INPUT_HERE",
"signature": "PASTE_SIGNATURE_HERE"
}
}' \
-w "Status: %{http_code}\n"
Success Response: HTTP 200 (empty body) Failure Response: HTTP 400 with error message
Available Endpoints
| Endpoint | Method | Description |
|---|---|---|
/sign |
POST | Generate signature headers for request body |
/sign-request |
POST | Sign complete HTTP request using high-level API |
/validate |
POST | Validate signature using individual validation methods |
/validate-request |
POST | Validate signature using high-level validation API |
Troubleshooting
"ModuleNotFoundError: No module named 'fastapi'"
pip install -e ".[dev,examples]"
"FileNotFoundError: Private and public keys must be provided"
python scripts/setup_dev_keys.py
"zsh: command not found: python"
# Activate virtual environment first
source .venv/bin/activate
# Or use python3
python3 examples/server.py
"zsh: command not found: jq"
# Install jq for JSON processing (optional)
brew install jq # macOS
sudo apt-get install jq # Ubuntu
Signature Validation
from digital_signature_sdk import validate_signed_message, RequestToSign
# Load public key
with open('path/to/publickey.pem', 'rb') as f:
public_key = f.read()
# Recreate the request
request = RequestToSign(
method="POST",
path="/v1/sell/finances/transaction",
authority="api.ebay.com",
body=b'{"hello": "world"}'
)
# Validate
is_valid = validate_signed_message(
request,
headers, # Headers dict with Signature, Signature-Input, Content-Digest
public_key,
algorithm="RSA"
)
print(f"Signature valid: {is_valid}")
Configuration
Environment Variables (Recommended for Security)
For production applications, use environment variables to keep secrets secure. Copy the .env.example file to .env and fill in your values:
cp .env.example .env
Edit the .env file with your actual values:
# Your JWE token from eBay's Key Management API
EBAY_JWE_TOKEN=your_actual_jwe_token_here
# Your key file paths
EBAY_PRIVATE_KEY_PATH=path/to/your/privatekey.pem
EBAY_PUBLIC_KEY_PATH=path/to/your/publickey.pem
# API endpoint configuration
EBAY_SIGNATURE_AUTHORITY=api.ebay.com
EBAY_SIGNATURE_PATH=/v1/sell/finances/transaction
The SDK automatically loads environment variables with the EBAY_ prefix and prioritizes them over configuration files.
Using Configuration with Environment Variables
from digital_signature_sdk import Config, sign_message, RequestToSign
# Load configuration that prioritizes environment variables
config = Config.load_secure_config("path/to/config.json")
# Load keys using the secure configuration
private_key = config.load_key_file(config.get("privateKey"))
# Get JWE token from environment (falls back to config file)
jwe_token = config.get("jwe")
# Create request
request = RequestToSign(
method="POST",
path=config.get("signatureComponents.path"),
authority=config.get("signatureComponents.authority"),
body=b'{"hello": "world"}',
jwe=jwe_token
)
# Sign the request
headers = sign_message(request, private_key)
JSON Configuration Files (Legacy)
For backward compatibility, you can still use JSON configuration files. However, avoid storing secrets in these files for security reasons.
Example configuration for signing-only (example-config.json):
{
"digestAlgorithm": "sha-256",
"_jwe_comment": "JWE token should be provided via EBAY_JWE_TOKEN environment variable",
"privateKey": "examples/keys/ed25519/privatekey.pem",
"signatureComponents": {
"method": "POST",
"authority": "api.ebay.com",
"path": "/v1/sell/finances/transaction"
},
"signatureParams": [
"content-digest",
"x-ebay-signature-key",
"@method",
"@path",
"@authority"
]
}
For signing and validation (example-config-full.json):
{
"digestAlgorithm": "sha-256",
"algorithm": "Ed25519",
"privateKey": "examples/keys/ed25519/privatekey.pem",
"publicKey": "examples/keys/ed25519/publickey.pem",
"jwtPayload": {
"_pkey_comment": "Public key should be provided via EBAY_JWT_PAYLOAD_PKEY environment variable"
},
"signatureComponents": {
"method": "POST",
"authority": "api.ebay.com",
"path": "/v1/sell/finances/transaction"
},
"signatureParams": [
"content-digest",
"x-ebay-signature-key",
"@method",
"@path",
"@authority"
]
}
Environment Variable Reference
| Environment Variable | Description | Example |
|---|---|---|
EBAY_JWE_TOKEN |
JWE token from Key Management API | <your-jwe-token> |
EBAY_PRIVATE_KEY_PATH |
Path to private key file | keys/ed25519/privatekey.pem |
EBAY_PUBLIC_KEY_PATH |
Path to public key file | keys/ed25519/publickey.pem |
EBAY_SIGNATURE_ALGORITHM |
Signature algorithm | Ed25519 or RSA |
EBAY_DIGEST_ALGORITHM |
Digest algorithm | sha-256 or sha-512 |
EBAY_SIGNATURE_AUTHORITY |
API hostname | api.ebay.com |
EBAY_SIGNATURE_PATH |
API path | /v1/sell/finances/transaction |
EBAY_SIGNATURE_PARAMS |
Signature parameters (comma-separated) | content-digest,x-ebay-signature-key,@method |
Running the Example
The SDK includes an example FastAPI server. To run it:
# 1. Generate development keys (first time only)
python scripts/setup_dev_keys.py
# 2. Set up environment variables
cp .env.example .env
# Edit .env with your actual EBAY_JWE_TOKEN
# 3. Install example dependencies
pip install fastapi uvicorn authlib
# 4. Run the example server
python -m examples.server
Key Generation
For security, no cryptographic keys are included in the repository. Generate your own:
# Quick setup - generates all development keys
python scripts/setup_dev_keys.py
# Advanced options
python -m digital_signature_sdk.key_generator --help
Note for Production Deployment
For Production, please host with HTTPS enabled.
Logging
Uses standard Python logging. Configure as needed in your application:
import logging
logging.basicConfig(level=logging.DEBUG)
License
Copyright 2025 eBay Inc. Developer: Qingyuan Liu
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
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 digital_signature_python_sdk-1.0.0.tar.gz.
File metadata
- Download URL: digital_signature_python_sdk-1.0.0.tar.gz
- Upload date:
- Size: 24.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
099e8705a1dd913b1b76cd1ffddbc1f443251b163977096f941942c852026125
|
|
| MD5 |
7e5e107fb7c973456d39564bf7289fc2
|
|
| BLAKE2b-256 |
b4351d79bce7fa009cf59696c8f9854003a33961dba087f8b76e316c433c8386
|
File details
Details for the file digital_signature_python_sdk-1.0.0-py3-none-any.whl.
File metadata
- Download URL: digital_signature_python_sdk-1.0.0-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f06e21941fb6cadb922d0e909b7ad241d573fb846f183ee53a4ba25acb39899f
|
|
| MD5 |
c844e6bf28c5c8a694692c71c634173e
|
|
| BLAKE2b-256 |
0a3b17c6ebfacb1dcb81463000a3eeb9f0de9ad379b8706b72b6655b87825863
|