Keys & Encryption¶
4 endpoints for generating secure tokens and performing authenticated encryption.
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/v1/auth/generate-key |
Generate an API key, UUID v4, nanoid, or secret token |
POST |
/v1/auth/generate-keypair |
Generate an RSA or EC key pair as PEM |
POST |
/v1/auth/encrypt |
Encrypt plaintext with AES-256-GCM |
POST |
/v1/auth/decrypt |
Decrypt AES-256-GCM ciphertext |
Python SDK Examples¶
Generate a random key or token¶
generate_key supports four formats. All use Python's secrets module for cryptographically secure randomness.
from toolkitapi import Auth
auth = Auth(api_key="tk_...")
# API key — prefixed, URL-safe, 32 hex bytes of entropy
api_key = auth.generate_key(type="api-key", prefix="tk")
print(api_key["key"]) # tk_a3f9bc12d4e78f01...
print(api_key["entropy_bits"]) # 256.0
# UUID v4 — standard 8-4-4-4-12 format
uuid = auth.generate_key(type="uuid-v4")
print(uuid["key"]) # 3f6b2a1e-8c4d-4f7e-9b0a-1d2e3f4a5b6c
# Nanoid — compact, URL-safe identifier (default 21 chars)
nanoid = auth.generate_key(type="nanoid", length=21)
print(nanoid["key"]) # V1StGXR8_Z5jdHi6B-myT
# Secret — long hex token for CSRF tokens, session secrets
secret = auth.generate_key(type="secret", length=64)
print(secret["key"]) # f3a8b12c... (64 hex chars = 256 bits)
Provision API keys for new users¶
from toolkitapi import Auth
auth = Auth(api_key="tk_...")
def provision_api_key(user_id: str) -> dict:
"""Generate an API key and return the user-visible key with its prefix."""
result = auth.generate_key(type="api-key", prefix="sk")
return {
"user_id": user_id,
"api_key": result["key"], # Show to user ONCE — never store the plaintext
"entropy_bits": result["entropy_bits"],
}
Generate an asymmetric key pair¶
generate_keypair returns both keys as PEM strings — ready to write to files or store in environment variables.
from toolkitapi import Auth
auth = Auth(api_key="tk_...")
# RSA 4096-bit — for JWT RS256 signing, TLS certificates
rsa_pair = auth.generate_keypair(algorithm="RSA", key_size=4096)
print(rsa_pair["public_key"]) # -----BEGIN PUBLIC KEY-----\n...
print(rsa_pair["private_key"]) # -----BEGIN RSA PRIVATE KEY-----\n...
# EC P-256 — compact keys, fast verification, suitable for ES256 JWTs
ec_pair = auth.generate_keypair(algorithm="EC", curve="P-256")
print(ec_pair["public_key"])
print(ec_pair["private_key"])
Bootstrap JWT signing keys for a new environment¶
from toolkitapi import Auth
import os
auth = Auth(api_key="tk_...")
def bootstrap_jwt_keys(env: str) -> None:
"""Generate and save RSA key pair for JWT RS256 signing."""
pair = auth.generate_keypair(algorithm="RSA", key_size=4096)
# Write private key (restrict permissions — only the app process should read this)
private_path = f"keys/{env}_jwt_private.pem"
with open(private_path, "w") as f:
f.write(pair["private_key"])
os.chmod(private_path, 0o600)
# Write public key (distribute to all services that verify JWTs)
public_path = f"keys/{env}_jwt_public.pem"
with open(public_path, "w") as f:
f.write(pair["public_key"])
print(f"Key pair written to {private_path} and {public_path}")
Encrypt data with AES-256-GCM¶
AES-256-GCM is authenticated encryption — it provides both confidentiality and integrity. Decryption will fail if the ciphertext has been tampered with.
You can provide either a 64-character hex key (recommended — keep it in a secrets manager) or a password (the API derives a key via PBKDF2-SHA256 with a random salt).
from toolkitapi import Auth
auth = Auth(api_key="tk_...")
# Option 1: Provide a 256-bit hex key directly
import secrets
encryption_key = secrets.token_hex(32) # 64 hex chars = 32 bytes = 256 bits
result = auth.encrypt(
plaintext="Sensitive user data — SSN, card number, etc.",
key=encryption_key,
)
print(result["ciphertext"]) # Base64-encoded ciphertext
print(result["iv"]) # Base64-encoded 96-bit nonce
print(result["tag"]) # Base64-encoded 128-bit auth tag
# Option 2: Derive key from a password (PBKDF2 — salt is returned for storage)
pw_result = auth.encrypt(
plaintext="another secret",
password="a-strong-master-password",
)
print(pw_result["salt"]) # Base64-encoded PBKDF2 salt — store alongside ciphertext
Decrypt AES-256-GCM ciphertext¶
Pass back the same iv and tag that were returned during encryption. The authentication tag protects against tampering — decryption raises an error if the data has been modified.
from toolkitapi import Auth
auth = Auth(api_key="tk_...")
# Decrypt with a hex key
plaintext_result = auth.decrypt(
ciphertext="<base64-ciphertext>",
iv="<base64-iv>",
tag="<base64-tag>",
key=encryption_key,
)
print(plaintext_result["plaintext"])
# Decrypt with a password (must supply the salt that was returned during encrypt)
pw_plaintext = auth.decrypt(
ciphertext=pw_result["ciphertext"],
iv=pw_result["iv"],
tag=pw_result["tag"],
password="a-strong-master-password",
salt=pw_result["salt"],
)
print(pw_plaintext["plaintext"])
Encrypt sensitive fields before storing in a database¶
from toolkitapi import Auth
import os
auth = Auth(api_key="tk_...")
FIELD_ENCRYPTION_KEY = os.environ["FIELD_ENCRYPTION_KEY"] # 64 hex chars
def encrypt_pii(plaintext: str) -> dict:
"""Encrypt a PII field. Store the returned dict alongside the record."""
result = auth.encrypt(plaintext, key=FIELD_ENCRYPTION_KEY)
return {
"ciphertext": result["ciphertext"],
"iv": result["iv"],
"tag": result["tag"],
}
def decrypt_pii(encrypted: dict) -> str:
"""Decrypt a PII field from a stored dict."""
result = auth.decrypt(
ciphertext=encrypted["ciphertext"],
iv=encrypted["iv"],
tag=encrypted["tag"],
key=FIELD_ENCRYPTION_KEY,
)
return result["plaintext"]
Response Fields¶
generate-key response:
| Field | Type | Description |
|---|---|---|
key |
string | The generated key string |
type |
string | Key type: api-key, uuid-v4, nanoid, or secret |
entropy_bits |
float | Estimated bits of entropy |
generate-keypair response:
| Field | Type | Description |
|---|---|---|
public_key |
string | PEM-encoded public key |
private_key |
string | PEM-encoded private key |
algorithm |
string | RSA or EC |
key_size |
int | null | Key size in bits (RSA only) |
curve |
string | null | Named curve (EC only): P-256, P-384, or P-521 |
encrypt response:
| Field | Type | Description |
|---|---|---|
ciphertext |
string | Base64-encoded encrypted data |
iv |
string | Base64-encoded 96-bit initialisation vector (nonce) |
tag |
string | Base64-encoded 128-bit GCM authentication tag |
salt |
string | null | Base64-encoded PBKDF2 salt (only present when password was used) |
algorithm |
string | Always AES-256-GCM |
decrypt response:
| Field | Type | Description |
|---|---|---|
plaintext |
string | Decrypted plaintext |
algorithm |
string | Always AES-256-GCM |
Tip
Never generate your encryption key with a predictable method — always use secrets.token_hex(32) or the generate-key endpoint with type="secret". Store the key in a secrets manager (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault) — not in source code or environment variable files committed to version control.