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.
- Digital Signatures for Public API Calls
- Features
- Installation
- Getting Started
- Usage
- Configuration
- Key Generation
- Logging
- License
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:
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.
- Python: 3.9 or higher
- pip: Latest version recommended
pip install digital-signature-python-sdkfrom 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:'
# }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.pyThe server will start at http://localhost:8080
Once the server is running, test it with these curl commands:
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:"
}curl -X POST http://localhost:8080/sign-request \
-H "Content-Type: application/json" \
-d '{"message": "Hello eBay API"}' \
-iWith 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
| 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 |
"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 # Ubuntufrom 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}")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 .envEdit 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/transactionThe SDK automatically loads environment variables with the EBAY_ prefix and prioritizes them over configuration files.
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)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 | 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 |
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.serverFor 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 --helpFor Production, please host with HTTPS enabled.
Uses standard Python logging. Configure as needed in your application:
import logging
logging.basicConfig(level=logging.DEBUG)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.