Public Keys
Get public keys for signature verification.
Endpoint
GET /api/v1/v2/keys
Description
Returns the public keys used by QuantumLock™ to sign licenses and proofs. Use these keys to verify signatures offline.
Response
Success (200 OK)
{
"keys": [
{
"kid": "ql-api-v2-default",
"alg": "RSA-PSS-SHA256",
"alg_pqc": "ML-DSA-65",
"use": "sig",
"status": "active",
"kty": "RSA",
"n": "base64-encoded-modulus...",
"e": "AQAB",
"pqc_public": "base64-encoded-mldsa-public-key..."
}
],
"generated_at": "2025-12-30T15:30:00Z"
}
Key Fields
| Field | Type | Description |
|---|---|---|
kid | string | Key ID (referenced in license artifacts) |
alg | string | Classical signature algorithm |
alg_pqc | string | Post-quantum signature algorithm |
use | string | Key usage (sig for signatures) |
status | string | Key status: active, rotated, expired |
kty | string | Key type (RSA) |
n | string | RSA modulus (base64) |
e | string | RSA exponent (base64) |
pqc_public | string | ML-DSA public key (base64) |
Key Status Values
| Status | Description |
|---|---|
active | Currently used for new signatures |
rotated | No longer used but still valid for verification |
expired | No longer valid for any purpose |
Example
cURL
curl "https://quantumlock.softquantus.com/api/v1/v2/keys" \
-H "Authorization: Bearer ql_your_api_key"
Python SDK
from quantumlock_sdk import QuantumLockClient
client = QuantumLockClient(api_key="ql_your_api_key")
keys = client.get_public_keys()
for key in keys:
print(f"Key ID: {key.kid}")
print(f" Algorithm: {key.alg} + {key.alg_pqc}")
print(f" Status: {key.status}")
Caching Keys
For offline validation, cache the public keys:
import json
from datetime import datetime, timedelta
def refresh_keys_if_needed():
"""Refresh keys daily or on first run."""
try:
with open("keys_cache.json", "r") as f:
cache = json.load(f)
cached_at = datetime.fromisoformat(cache["cached_at"])
if datetime.utcnow() - cached_at < timedelta(days=1):
return cache["keys"]
except FileNotFoundError:
pass
# Fetch fresh keys
keys = client.get_public_keys()
# Cache them
with open("keys_cache.json", "w") as f:
json.dump({
"keys": [k.to_dict() for k in keys],
"cached_at": datetime.utcnow().isoformat()
}, f)
return keys
Key Rotation
QuantumLock™ rotates signing keys periodically for security. When keys are rotated:
- New key is marked
active - Old key is marked
rotated(still valid for verification) - After rotation period, old key is marked
expired
Your application should:
- Cache multiple keys (all
activeandrotated) - Try verification with each key by matching
kid - Refresh keys periodically (daily recommended)
def verify_signature(artifact, keys):
"""Verify signature using the correct key."""
kid = artifact.header.kid
for key in keys:
if key.kid == kid and key.status in ("active", "rotated"):
return key.verify(artifact)
raise KeyNotFoundError(f"No valid key found for kid: {kid}")
Security Notes
- HTTPS Only: Always fetch keys over HTTPS
- Cache Securely: Store cached keys in a secure location
- Verify Chain: In production, keys should be signed by a root key
- Multiple Keys: Always handle multiple keys for rotation