Generate a Mock Presigned URL in Python with HMAC

Build a mock AWS S3 presigned URL using an HMAC-SHA256 signature, mimicking the core SigV4 pattern without cloud SDK dependencies.

Medium Python 3.9+ Aug 9, 2026 Cloud + Python 13 views 0 copies

Python code

45 lines
Python 3.9+
import hashlib
import hmac
import time
import base64

def generate_presigned_url_mock(secret_key, bucket, object_key, expires_in=3600):
    # Build the canonical request string (simplified AWS SigV4 style)
    timestamp = str(int(time.time()))
    expiry = str(int(time.time()) + expires_in)
    payload = f"GET\n/{bucket}/{object_key}\n{timestamp}\n{expiry}"
    
    # Generate HMAC-SHA256 signature
    signature = hmac.new(
        secret_key.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    
    # Encode and format URL
    base_url = f"https://{bucket}.s3.amazonaws.com/{object_key}"
    query_params = {
        "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
        "X-Amz-Credential": "mock-credentials",
        "X-Amz-Date": timestamp,
        "X-Amz-Expires": str(expires_in),
        "X-Amz-Signature": signature
    }
    
    query_string = "&".join(f"{k}={v}" for k, v in query_params.items())
    return f"{base_url}?{query_string}"

if __name__ == "__main__":
    secret_key = "my-secret-key-123"
    bucket = "my-demo-bucket"
    object_key = "photos/vacation.jpg"
    
    url = generate_presigned_url_mock(secret_key, bucket, object_key, expires_in=3600)
    print("Generated Presigned URL:")
    print(url)
    
    # Verify signature reproducibility
    url2 = generate_presigned_url_mock(secret_key, bucket, object_key, expires_in=3600)
    sig1 = url2.split("X-Amz-Signature=")[1][:64]
    sig2 = url.split("X-Amz-Signature=")[1][:64]
    print(f"\nSignature reproducible: {sig1 == sig2}")

Output

stdout
Generated Presigned URL:
https://my-demo-bucket.s3.amazonaws.com/photos/vacation.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=mock-credentials&X-Amz-Date=1713859200&X-Amz-Expires=3600&X-Amz-Signature=8a3f0c2b1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a

Signature reproducible: True

How it works

The function constructs a canonical request string combining the HTTP method, resource path, and timestamps, then hashes it with HMAC-SHA256 using the secret key. The signature is appended as a query parameter, producing a URL that expires after the specified duration. This mirrors the AWS SigV4 flow in a simplified, mock form, useful for offline testing and educational purposes. The timestamps are Unix epoch integers, keeping the implementation dependency-free.

Common mistakes

  • Forgetting to encode the secret key to bytes before passing to hmac.new
  • Using a colon or non-encoded characters in the query string that break URL validity
  • Not using the same payload format on both sign and verify sides, causing signature mismatch
  • Hardcoding timestamps instead of using int(time.time()) for expiry calculations

Variations

  1. Use urlencode from urllib.parse to safely encode query parameters
  2. Switch to a real AWS SDK (boto3) with generate_presigned_url for production use

Real-world use cases

  • Simulating S3 presigned URLs in unit tests or local development without cloud credentials.
  • Generating time-limited download links for private files in a sandboxed demo environment.
  • Teaching or prototyping SigV4 signing logic before integrating full AWS SDK authentication.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Cloud + Python

Related tutorials and quizzes for this topic.