Skip to main content

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

FieldTypeDescription
kidstringKey ID (referenced in license artifacts)
algstringClassical signature algorithm
alg_pqcstringPost-quantum signature algorithm
usestringKey usage (sig for signatures)
statusstringKey status: active, rotated, expired
ktystringKey type (RSA)
nstringRSA modulus (base64)
estringRSA exponent (base64)
pqc_publicstringML-DSA public key (base64)

Key Status Values

StatusDescription
activeCurrently used for new signatures
rotatedNo longer used but still valid for verification
expiredNo 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:

  1. New key is marked active
  2. Old key is marked rotated (still valid for verification)
  3. After rotation period, old key is marked expired

Your application should:

  • Cache multiple keys (all active and rotated)
  • 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

  1. HTTPS Only: Always fetch keys over HTTPS
  2. Cache Securely: Store cached keys in a secure location
  3. Verify Chain: In production, keys should be signed by a root key
  4. Multiple Keys: Always handle multiple keys for rotation