Skip to main content

Key Management

The Ed25519 signing key is critical to the security of UAPK Gateway. This guide covers secure key generation, storage, rotation, and recovery.

Key Overview

Key TypePurposeAlgorithm
Gateway Signing KeySign audit recordsEd25519
Capability Issuer KeysSign capability tokensEd25519

Key Lifecycle

Gateway Signing Key

Generation

Generate a secure Ed25519 key pair:

# Generate key pair
python -c "
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization

private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()

# Save private key (keep secure!)
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
with open('gateway_private.pem', 'wb') as f:
f.write(private_pem)

# Save public key (can be distributed)
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open('gateway_public.pem', 'wb') as f:
f.write(public_pem)

print('Keys generated successfully')
"

# Set permissions
chmod 600 gateway_private.pem
chmod 644 gateway_public.pem

Storage Options

Option 1: Environment Variable (Development)

# Not recommended for production
export GATEWAY_SIGNING_KEY=$(cat gateway_private.pem | base64)

Option 2: File-based (Simple Production)

# Store in protected directory
mkdir -p /etc/uapk-gateway/keys
cp gateway_private.pem /etc/uapk-gateway/keys/
chmod 600 /etc/uapk-gateway/keys/gateway_private.pem
chown uapk:uapk /etc/uapk-gateway/keys/gateway_private.pem

# Configure gateway
export GATEWAY_SIGNING_KEY_FILE=/etc/uapk-gateway/keys/gateway_private.pem
# AWS Secrets Manager
aws secretsmanager create-secret \
--name uapk-gateway/signing-key \
--secret-string "$(cat gateway_private.pem)"

# Configure gateway
export GATEWAY_SIGNING_KEY_SECRET=aws:secretsmanager:uapk-gateway/signing-key

Option 4: HashiCorp Vault

# Store in Vault
vault kv put secret/uapk-gateway/signing-key \
private_key=@gateway_private.pem

# Configure gateway
export GATEWAY_SIGNING_KEY_SECRET=vault:secret/data/uapk-gateway/signing-key#private_key

Key Rotation

When to Rotate

TriggerAction
Scheduled (annual)Planned rotation
Key exposure suspectedEmergency rotation
Personnel changeConsider rotation
Security incidentEmergency rotation

Rotation Process

Step-by-Step Rotation

# 1. Generate new key
python -c "
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization

private_key = Ed25519PrivateKey.generate()
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
with open('gateway_private_new.pem', 'wb') as f:
f.write(private_pem)
"

# 2. Verify current chain before rotation
curl http://localhost:8000/api/v1/orgs/$ORG_ID/logs/verify/all \
-H "Authorization: Bearer $TOKEN"

# 3. Export current logs
curl -X POST http://localhost:8000/api/v1/orgs/$ORG_ID/logs/export/download \
-H "Authorization: Bearer $TOKEN" \
-d '{}' > pre-rotation-export.json

# 4. Update gateway configuration (during maintenance window)
# Replace GATEWAY_SIGNING_KEY_FILE with new key

# 5. Restart gateway
systemctl restart uapk-gateway

# 6. Verify new key is active
curl http://localhost:8000/api/v1/gateway/keys/status \
-H "Authorization: Bearer $TOKEN"

# 7. Archive old key securely
mv gateway_private.pem gateway_private_$(date +%Y%m%d).pem.archived

Maintaining Verification

After rotation, old records can still be verified using the archived public key:

# Include old public keys in verification
python verify_log_chain.py pre-rotation-export.json \
--public-keys gateway_public_old.pem,gateway_public_new.pem

Capability Issuer Keys

Creating an Issuer

curl -X POST http://localhost:8000/api/v1/orgs/$ORG_ID/capability-issuers \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "production-issuer",
"description": "Primary issuer for production tokens"
}'

Response includes the public key:

{
"issuer_id": "issuer-abc123",
"name": "production-issuer",
"public_key": "MCowBQYDK2VwAyEA...",
"created_at": "2024-12-14T10:00:00Z"
}

Rotating Issuer Keys

# Create new issuer
curl -X POST http://localhost:8000/api/v1/orgs/$ORG_ID/capability-issuers \
-H "Authorization: Bearer $TOKEN" \
-d '{"name": "production-issuer-v2"}'

# Update agents to use new issuer
# ...

# Revoke old issuer (after all tokens expire)
curl -X POST http://localhost:8000/api/v1/orgs/$ORG_ID/capability-issuers/issuer-abc123/revoke \
-H "Authorization: Bearer $TOKEN"

Key Backup

Backup Strategy

Backup TypeFrequencyRetention
Encrypted backupWeekly1 year
Offline copyMonthlyIndefinite
Geographic redundancyQuarterlyIndefinite

Backup Procedure

# Encrypt backup
gpg --symmetric --cipher-algo AES256 \
-o gateway_private.pem.gpg gateway_private.pem

# Store in multiple locations
aws s3 cp gateway_private.pem.gpg s3://backup-bucket/keys/
cp gateway_private.pem.gpg /mnt/backup-drive/keys/

# Document passphrase securely (separate from backup)

Key Recovery

Recovery Procedure

# 1. Retrieve encrypted backup
aws s3 cp s3://backup-bucket/keys/gateway_private.pem.gpg .

# 2. Decrypt
gpg --decrypt gateway_private.pem.gpg > gateway_private.pem

# 3. Verify key matches public key
python -c "
from cryptography.hazmat.primitives import serialization

with open('gateway_private.pem', 'rb') as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)

public_bytes = private_key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
print(public_bytes.decode())
"

# 4. Compare with known public key
# 5. Restore to gateway

Compromise Response

If you suspect key compromise:

# 1. Immediate: Rotate to new key
# See rotation procedure above

# 2. Revoke all capability tokens
curl -X POST http://localhost:8000/api/v1/orgs/$ORG_ID/capability-tokens/revoke-all \
-H "Authorization: Bearer $TOKEN" \
-d '{"reason": "Key compromise incident"}'

# 3. Export and verify logs signed with old key
curl -X POST http://localhost:8000/api/v1/orgs/$ORG_ID/logs/export/download \
-H "Authorization: Bearer $TOKEN" \
-d '{}' > incident-export.json

python verify_log_chain.py incident-export.json

# 4. Document incident
# 5. Notify affected parties if required

Security Controls

Access Control

ControlImplementation
File permissions600 (owner read/write only)
User isolationRun gateway as dedicated user
Network isolationKeys never transmitted over network
Audit loggingLog all key operations

Monitoring

EventAction
Key file accessLog and alert
Signature failuresAlert immediately
Key rotationLog and notify

Best Practices

Use Secrets Management

Use a secrets manager (Vault, AWS Secrets Manager) in production.

Regular Rotation

Rotate signing keys at least annually.

Backup Verification

Test backup recovery quarterly.

Never Share Private Keys

Private keys should never leave the production environment.

Separate Environments

Use different keys for dev, staging, and production.