Skip to main content

eBay Digital Signature SDK for Python

PyPI version Python versions License

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

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_message method to sign the incoming request object
  • validate_signed_message method to validate the signature of the incoming request object
  • There are individual methods as well to generate and validate the headers:
    • generate_content_digest
    • generate_signature
    • build_signature_input
    • build_signature_base
    • validate_content_digest
    • validate_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

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.

Metadata

Release files for digital-signature-python-sdk 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for digital-signature-python-sdk 1.0.0
File Size Uploaded Provenance
digital_signature_python_sdk-1.0.0.tar.gz 24.4 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for digital-signature-python-sdk 1.0.0
File Interpreter ABI Platform
digital_signature_python_sdk-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 55.0 kB

Release files / digital_signature_python_sdk-1.0.0.tar.gz

Download URL digital_signature_python_sdk-1.0.0.tar.gz
Size 24.4 kB
Tags Source
SHA-256 checksum
How to use checksums
099e8705a1dd913b1b76cd1ffddbc1f443251b163977096f941942c852026125
BLAKE2b-256 checksum
How to use checksums
b4351d79bce7fa009cf59696c8f9854003a33961dba087f8b76e316c433c8386
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / digital_signature_python_sdk-1.0.0-py3-none-any.whl

Download URL digital_signature_python_sdk-1.0.0-py3-none-any.whl
Size 30.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f06e21941fb6cadb922d0e909b7ad241d573fb846f183ee53a4ba25acb39899f
BLAKE2b-256 checksum
How to use checksums
0a3b17c6ebfacb1dcb81463000a3eeb9f0de9ad379b8706b72b6655b87825863
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Security for digital-signature-python-sdk 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page