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.