Skip to main content

C3-06: Cloud KMS Components - Cryptographic Key Management

Document Type: C4 Level 3 (Component) Diagram Container: Google Cloud Key Management Service (Cloud KMS) Technology: Cloud KMS, RSA-4096, PKCS#1 v1.5, Asymmetric Signing Status: Specification Complete - Ready for Implementation Last Updated: November 30, 2025


Table of Contents

  1. Overview
  2. Component Diagram
  3. Cloud KMS Architecture
  4. Key Ring and Crypto Key Configuration
  5. License Signing Workflow
  6. Client-Side Signature Verification
  7. Key Rotation and Versioning
  8. IAM and Access Control
  9. Integration with Django
  10. Security Best Practices
  11. Monitoring and Audit
  12. Production Deployment

Overview

Purpose

This document specifies the component-level architecture of Google Cloud Key Management Service for the CODITECT License Management Platform. It provides:

  • Complete Cloud KMS configuration (key rings, asymmetric signing keys)
  • RSA-4096 key pair generation for license signing
  • License signing workflow (server-side)
  • Signature verification workflow (client-side offline)
  • Key rotation and versioning strategies
  • Production-ready security and monitoring

Cloud KMS Role

Cloud KMS serves as:

  • License Signing Authority: Signs license data with RSA-4096 private key
  • Tamper-Proof Mechanism: Ensures licenses cannot be forged or modified
  • Offline Verification: Clients verify signatures locally without network
  • Key Rotation: Automatic key versioning and rotation
  • HSM-Backed Security: Hardware security module protection

Key Features:

  • Asymmetric Signing: RSA-4096 with SHA-256 digest
  • Hardware-Backed: Keys stored in FIPS 140-2 Level 3 HSMs
  • Automatic Rotation: Key versions rotated on schedule
  • Audit Logging: All signing operations logged
  • Global Availability: Low-latency access from any region

License Signing Pattern

Django API → Cloud KMS: Sign license data

Cloud KMS: RSA-4096 private key signature

Django API → CODITECT Client: Signed license token

CODITECT Client: Verify signature with public key (offline)

CODITECT Client: ✅ License valid, run framework

Why Asymmetric Signing?

  • Private key never leaves Cloud KMS - Cannot be stolen or leaked
  • Public key distributed to clients - No secrets in client code
  • Offline verification - Works without network connectivity
  • Tamper-proof - Any modification invalidates signature

Component Diagram

Cloud KMS Internal Components


Cloud KMS Architecture

Key Hierarchy

Cloud KMS Organization:

Project: coditect-cloud-infra

Location: us-central1 (regional)

Key Ring: license-signing (logical grouping)

Crypto Key: license-key (signing key)

Key Versions: v1, v2, v3... (rotation)

Resource Names:

Key Ring: projects/coditect-cloud-infra/locations/us-central1/keyRings/license-signing
Crypto Key: projects/coditect-cloud-infra/locations/us-central1/keyRings/license-signing/cryptoKeys/license-key
Key Version: projects/coditect-cloud-infra/locations/us-central1/keyRings/license-signing/cryptoKeys/license-key/cryptoKeyVersions/1

OpenTofu Configuration

File: opentofu/modules/kms/main.tf

/**
* Cloud KMS Configuration for License Signing
*
* Creates:
* - Key ring for license signing keys
* - RSA-4096 asymmetric signing key
* - IAM bindings for Django service account
*/

# Enable Cloud KMS API
resource "google_project_service" "cloudkms" {
project = var.project_id
service = "cloudkms.googleapis.com"

disable_on_destroy = false
}

# Key Ring (regional, cannot be deleted)
resource "google_kms_key_ring" "license_signing" {
project = var.project_id
name = "license-signing"
location = var.region # us-central1

depends_on = [google_project_service.cloudkms]
}

# Crypto Key for License Signing
resource "google_kms_crypto_key" "license_key" {
name = "license-key"
key_ring = google_kms_key_ring.license_signing.id

# Purpose: Asymmetric signing
purpose = "ASYMMETRIC_SIGN"

# Algorithm: RSA 4096-bit with SHA-256 digest
# PKCS#1 v1.5 padding (widely supported)
version_template {
algorithm = "RSA_SIGN_PKCS1_4096_SHA256"
protection_level = "HSM" # Hardware Security Module
}

# Key rotation (automatic)
rotation_period = "7776000s" # 90 days
next_rotation_time = timeadd(timestamp(), "7776000s")

# Lifecycle
lifecycle {
prevent_destroy = true # Prevent accidental deletion
}

labels = {
environment = var.environment
purpose = "license-signing"
managed_by = "opentofu"
}
}

# Import service account (runs Django API)
data "google_service_account" "django_api" {
account_id = "license-api-sa"
project = var.project_id
}

# Grant Django service account permission to sign
resource "google_kms_crypto_key_iam_member" "signer" {
crypto_key_id = google_kms_crypto_key.license_key.id
role = "roles/cloudkms.signerVerifier"
member = "serviceAccount:${data.google_service_account.django_api.email}"
}

# Grant permission to get public key (anyone)
resource "google_kms_crypto_key_iam_member" "public_key_viewer" {
crypto_key_id = google_kms_crypto_key.license_key.id
role = "roles/cloudkms.publicKeyViewer"
member = "allUsers" # Public key is public by definition
}

# Output key name for Django configuration
output "crypto_key_name" {
description = "Full resource name of crypto key"
value = google_kms_crypto_key.license_key.id
}

output "crypto_key_version" {
description = "Primary key version"
value = google_kms_crypto_key.license_key.primary[0].name
}

Key Algorithm Details

RSA_SIGN_PKCS1_4096_SHA256:

  • Key Size: 4096 bits (extremely secure, 617 years to crack with current tech)
  • Padding: PKCS#1 v1.5 (RFC 3447)
  • Digest: SHA-256 (256-bit cryptographic hash)
  • Signature Size: 512 bytes (4096 bits / 8)
  • Protection: HSM-backed (FIPS 140-2 Level 3)

Why RSA-4096?

  • Future-Proof: Resistant to quantum computing threats
  • Widely Supported: Works with all major crypto libraries
  • Standards Compliant: NIST recommended for long-term security

License Signing Workflow

Server-Side Signing

Django Integration

File: app/services/cloud_kms_service.py

"""
Cloud KMS Service for License Signing
"""
from google.cloud import kms
from google.cloud.kms_v1 import KeyManagementServiceClient
from google.cloud.kms_v1.types import Digest
from django.conf import settings
from typing import Dict
import hashlib
import base64
import json
import logging

logger = logging.getLogger(__name__)


class CloudKMSService:
"""
Service for signing license data with Cloud KMS

Uses RSA-4096 asymmetric signing for tamper-proof licenses
"""

# Cloud KMS client (singleton)
_client: KeyManagementServiceClient = None

# Crypto key resource name
_key_name: str = None

@classmethod
def initialize(cls):
"""
Initialize Cloud KMS client

Should be called once during Django startup (apps.py ready())
"""
if cls._client is None:
cls._client = KeyManagementServiceClient()

# Build key name from settings
cls._key_name = (
f"projects/{settings.GCP_PROJECT_ID}/"
f"locations/{settings.KMS_LOCATION}/"
f"keyRings/{settings.KMS_KEYRING}/"
f"cryptoKeys/{settings.KMS_KEY}/"
f"cryptoKeyVersions/{settings.KMS_KEY_VERSION}"
)

logger.info(f"Cloud KMS initialized: {cls._key_name}")

@classmethod
def sign_license_data(cls, payload: Dict) -> str:
"""
Sign license payload with Cloud KMS

Args:
payload: License data dictionary

Returns:
Base64-encoded signature (512 bytes for RSA-4096)

Raises:
Exception: If signing fails

Example:
payload = {
'license_id': 'uuid',
'user_id': 'uuid',
'hardware_id': 'hash',
'issued_at': 1701360000,
'expires_at': 1732896000,
'seats_total': 10,
'features': ['ai', 'cloud']
}
signature = CloudKMSService.sign_license_data(payload)
"""
cls.initialize()

try:
# Step 1: Serialize payload to canonical JSON
payload_json = json.dumps(payload, sort_keys=True, separators=(',', ':'))
payload_bytes = payload_json.encode('utf-8')

# Step 2: Hash payload (SHA-256)
digest_sha256 = hashlib.sha256(payload_bytes).digest()

# Step 3: Create digest object for KMS
digest = Digest()
digest.sha256 = digest_sha256

# Step 4: Call Cloud KMS to sign
sign_request = {
'name': cls._key_name,
'digest': digest,
}

response = cls._client.asymmetric_sign(request=sign_request)

# Step 5: Base64-encode signature for transport
signature_b64 = base64.b64encode(response.signature).decode('utf-8')

logger.info(f"Signed license data (payload size: {len(payload_bytes)} bytes)")

return signature_b64

except Exception as e:
logger.error(f"Failed to sign license data: {e}")
raise

@classmethod
def get_public_key(cls) -> str:
"""
Get public key for signature verification (client-side)

Returns:
PEM-formatted public key

Example:
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA...
-----END PUBLIC KEY-----
"""
cls.initialize()

try:
public_key_request = {
'name': cls._key_name,
}

response = cls._client.get_public_key(request=public_key_request)

# Response contains PEM-formatted public key
return response.pem

except Exception as e:
logger.error(f"Failed to get public key: {e}")
raise

@classmethod
def verify_signature_server_side(cls, payload: Dict, signature_b64: str) -> bool:
"""
Verify signature server-side (for testing, not production)

Args:
payload: License data dictionary
signature_b64: Base64-encoded signature

Returns:
True if signature valid

Note:
Production clients verify offline with embedded public key
This method is for server-side testing only
"""
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend

# Get public key
public_key_pem = cls.get_public_key()
public_key = serialization.load_pem_public_key(
public_key_pem.encode('utf-8'),
backend=default_backend()
)

# Serialize and hash payload
payload_json = json.dumps(payload, sort_keys=True, separators=(',', ':'))
payload_bytes = payload_json.encode('utf-8')
digest = hashlib.sha256(payload_bytes).digest()

# Decode signature
signature = base64.b64decode(signature_b64)

# Verify signature
try:
public_key.verify(
signature,
digest,
padding.PKCS1v15(),
hashes.SHA256()
)
return True
except Exception as e:
logger.warning(f"Signature verification failed: {e}")
return False

Client-Side Signature Verification

CODITECT Client Verification (Offline)

File: coditect-core/license/verifier.py (Python client)

"""
License signature verification (offline)

This module runs on the client machine WITHOUT network access
Public key is embedded in CODITECT framework during build
"""
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidSignature
import hashlib
import base64
import json
import logging

logger = logging.getLogger(__name__)


# Public key embedded during build (from Cloud KMS)
PUBLIC_KEY_PEM = """
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7xK9v2Z...
(FULL 4096-BIT RSA PUBLIC KEY)
...oVQ8IkXrN5wIDAQAB
-----END PUBLIC KEY-----
"""


class LicenseVerifier:
"""
Verify license signatures offline using embedded public key
"""

_public_key = None

@classmethod
def initialize(cls):
"""
Load public key from embedded PEM
"""
if cls._public_key is None:
cls._public_key = serialization.load_pem_public_key(
PUBLIC_KEY_PEM.encode('utf-8'),
backend=default_backend()
)

@classmethod
def verify_license(cls, license_data: dict, signature_b64: str) -> bool:
"""
Verify license signature

Args:
license_data: License payload dictionary
signature_b64: Base64-encoded signature from API

Returns:
True if signature valid, False otherwise

Example:
license_data = {
'license_id': 'uuid',
'user_id': 'uuid',
'hardware_id': 'hash',
'issued_at': 1701360000,
'expires_at': 1732896000,
'seats_total': 10,
'features': ['ai', 'cloud']
}
signature = "base64-encoded-signature"

if LicenseVerifier.verify_license(license_data, signature):
print("✅ License valid - Run CODITECT")
else:
print("❌ Invalid license - Exit")
"""
cls.initialize()

try:
# Step 1: Serialize payload (MUST match server-side serialization)
payload_json = json.dumps(license_data, sort_keys=True, separators=(',', ':'))
payload_bytes = payload_json.encode('utf-8')

# Step 2: Hash payload (SHA-256)
digest = hashlib.sha256(payload_bytes).digest()

# Step 3: Decode signature
signature = base64.b64decode(signature_b64)

# Step 4: Verify signature with public key
cls._public_key.verify(
signature,
digest,
padding.PKCS1v15(),
hashes.SHA256()
)

logger.info("✅ License signature valid")
return True

except InvalidSignature:
logger.error("❌ License signature invalid - License tampered!")
return False

except Exception as e:
logger.error(f"License verification error: {e}")
return False

Verification Flow

# CODITECT startup sequence
def startup_license_check():
"""
Verify license before starting CODITECT framework
"""
# Step 1: Load license from local cache
license_file = Path.home() / '.coditect' / 'license.json'

with open(license_file, 'r') as f:
license_doc = json.load(f)

license_data = license_doc['license_data']
signature = license_doc['signature']

# Step 2: Verify signature (offline, no network)
if not LicenseVerifier.verify_license(license_data, signature):
print("❌ Invalid license signature!")
print(" License may have been tampered with.")
print(" Please re-acquire license from API.")
sys.exit(1)

# Step 3: Check expiration
expires_at = license_data['expires_at']
if time.time() > expires_at:
print("❌ License expired!")
sys.exit(1)

# Step 4: Check hardware ID
current_hardware_id = get_hardware_id()
if license_data['hardware_id'] != current_hardware_id:
print("❌ License not valid for this machine!")
sys.exit(1)

# Step 5: All checks passed - Start CODITECT
print("✅ License valid - Starting CODITECT framework")
return True

Key Rotation and Versioning

Automatic Key Rotation

Configuration: 90-day rotation (set in OpenTofu)

Rotation Process:

  1. Day 0: Create new key version (v2)
  2. Day 0-90: Both v1 and v2 active, v2 becomes primary
  3. New signatures: Use v2 (primary)
  4. Old signatures: Still verify with v1 public key
  5. Day 90+: Optionally disable v1

Key Version States:

  • ENABLED - Active, can sign/verify
  • DISABLED - Cannot sign, can still verify (for old licenses)
  • DESTROYED - Irreversibly deleted (7-day grace period)

Managing Key Versions

# List key versions
gcloud kms keys versions list \
--keyring=license-signing \
--key=license-key \
--location=us-central1

# Create new version (manual rotation)
gcloud kms keys versions create \
--keyring=license-signing \
--key=license-key \
--location=us-central1 \
--primary

# Disable old version
gcloud kms keys versions disable 1 \
--keyring=license-signing \
--key=license-key \
--location=us-central1

Client Public Key Updates

Problem: Clients have old public key embedded, need new public key after rotation

Solution 1: Multi-Key Verification (Recommended)

# Embed multiple public keys in client
PUBLIC_KEYS = [
PUBLIC_KEY_V1_PEM,
PUBLIC_KEY_V2_PEM,
PUBLIC_KEY_V3_PEM,
]

def verify_with_any_key(license_data, signature):
"""Try verification with all known public keys"""
for pub_key_pem in PUBLIC_KEYS:
if verify_with_key(license_data, signature, pub_key_pem):
return True
return False

Solution 2: Auto-Update (Network Required)

def get_latest_public_key():
"""Fetch latest public key from API"""
response = requests.get('https://api.coditect.com/api/v1/public-key')
return response.json()['public_key']

IAM and Access Control

Service Account Permissions

Django API Service Account:

  • Role: roles/cloudkms.signerVerifier
  • Permissions:
    • cloudkms.cryptoKeyVersions.useToSign
    • cloudkms.cryptoKeyVersions.viewPublicKey

Public Access:

  • Role: roles/cloudkms.publicKeyViewer
  • Member: allUsers
  • Permissions:
    • cloudkms.cryptoKeyVersions.viewPublicKey

Admin Access:

  • Role: roles/cloudkms.admin
  • Member: user:admin@coditect.com
  • Permissions: Full key management

Least Privilege Principle

Django API CANNOT:

  • Delete keys
  • Disable keys
  • Modify key rotation policy
  • Grant IAM permissions

Django API CAN:

  • Sign data with primary key version
  • Get public key

Security Best Practices

Key Security

  1. HSM-Backed Keys

    • Private keys stored in FIPS 140-2 Level 3 HSMs
    • Keys never leave HSM (signing happens inside HSM)
    • Tamper-resistant hardware
  2. No Key Export

    • Private keys cannot be exported from Cloud KMS
    • No risk of key leakage or theft
  3. Automatic Key Rotation

    • 90-day rotation reduces risk of compromise
    • Old versions kept for verifying existing licenses
  4. Audit Logging

    • All signing operations logged
    • Monitor for unusual signing patterns

Signature Security

  1. Canonical Serialization

    • Use json.dumps(sort_keys=True, separators=(',', ':'))
    • Ensures identical payloads produce identical hashes
  2. Cryptographic Hash

    • SHA-256 digest before signing
    • Prevents signature of arbitrary data
  3. Large Key Size

    • RSA-4096 provides 617-year security against brute force
    • Resistant to quantum computing attacks (for now)

Monitoring and Audit

Cloud KMS Metrics

Key Metrics:

  • cloudkms.googleapis.com/crypto_key/encryption_request_count - Sign operations
  • cloudkms.googleapis.com/crypto_key/encryption_request_latencies - Signing latency
  • cloudkms.googleapis.com/crypto_key/decryption_request_count - Verify operations (if server-side)

Alerting Policy:

displayName: "High KMS Signing Rate"
conditions:
- displayName: "Signing operations > 1000/min for 5 minutes"
conditionThreshold:
filter: 'metric.type="cloudkms.googleapis.com/crypto_key/encryption_request_count"'
comparison: COMPARISON_GT
thresholdValue: 1000
duration: 300s
notifications:
- email: ops@coditect.com
- pagerduty: kms-alerts

Audit Logs

Access audit logs:

gcloud logging read \
'resource.type="cloudkms_cryptokey"
AND protoPayload.methodName="AsymmetricSign"' \
--limit 50 \
--format json

Sample audit log entry:

{
"protoPayload": {
"methodName": "AsymmetricSign",
"resourceName": "projects/coditect-cloud-infra/locations/us-central1/keyRings/license-signing/cryptoKeys/license-key/cryptoKeyVersions/1",
"authenticationInfo": {
"principalEmail": "license-api-sa@coditect-cloud-infra.iam.gserviceaccount.com"
},
"requestMetadata": {
"callerIp": "10.128.0.5"
}
},
"timestamp": "2025-11-30T12:34:56.789Z"
}

Production Deployment

Django Settings

File: config/settings/production.py

# Cloud KMS Configuration
KMS_CONFIG = {
'PROJECT_ID': 'coditect-cloud-infra',
'LOCATION': 'us-central1',
'KEYRING': 'license-signing',
'KEY': 'license-key',
'KEY_VERSION': '1', # Primary version
}

# Build full key name
KMS_KEY_NAME = (
f"projects/{KMS_CONFIG['PROJECT_ID']}/"
f"locations/{KMS_CONFIG['LOCATION']}/"
f"keyRings/{KMS_CONFIG['KEYRING']}/"
f"cryptoKeys/{KMS_CONFIG['KEY']}/"
f"cryptoKeyVersions/{KMS_CONFIG['KEY_VERSION']}"
)

Cost Estimation

Cloud KMS Pricing (as of 2025):

  • Key Version: $0.06/month per active version
  • Sign Operation: $0.03 per 10,000 operations
  • Get Public Key: Free

Expected Costs:

  • Key Versions: 2 active × $0.06 = $0.12/month
  • Sign Operations: 10,000 licenses/month × $0.03/10K = $0.03/month
  • Total: $0.15/month ($1.80/year)

Extremely cost-effective for tamper-proof licenses.


Summary

This C3-06 Cloud KMS Components specification provides:

Complete Cloud KMS configuration

  • RSA-4096 asymmetric signing key
  • HSM-backed key storage (FIPS 140-2 Level 3)
  • Automatic 90-day key rotation
  • Key ring and crypto key structure

License signing workflow

  • Server-side signing with Cloud KMS
  • SHA-256 digest + RSA-4096 signature
  • Base64-encoded signature transport
  • Complete Django integration

Client-side signature verification

  • Offline verification with embedded public key
  • Tamper detection
  • Hardware ID validation
  • Multi-key support for rotation

Key rotation and versioning

  • Automatic rotation configuration
  • Key version management
  • Client public key updates
  • Backward compatibility

IAM and access control

  • Least privilege service account
  • Public key viewer access
  • Admin key management
  • Audit logging

Security best practices

  • HSM-backed keys (no export)
  • Canonical JSON serialization
  • Cryptographic hash before signing
  • Large key size (4096-bit)

Monitoring and audit

  • Cloud Monitoring metrics
  • Alerting policies
  • Audit log analysis
  • Anomaly detection

Implementation Status: Specification Complete Next Steps:

  1. Enable Cloud KMS API (Phase 1)
  2. Create key ring and crypto key (Phase 1)
  3. Grant IAM permissions (Phase 1)
  4. Implement CloudKMSService (Phase 2)
  5. Test signing workflow (Phase 2)
  6. Embed public key in CODITECT client (Phase 4)

Current Status:

  • Cloud KMS API: ⏸️ Not enabled
  • Key Ring: ⏸️ Not created
  • Crypto Key: ⏸️ Not created

Dependencies:

  • google-cloud-kms >= 2.16.0
  • cryptography >= 41.0.0

Cost: $0.15/month ($1.80/year)

Total Lines: 800+ (complete production-ready Cloud KMS configuration)


Author: CODITECT Infrastructure Team Date: November 30, 2025 Version: 1.0 Status: Ready for Implementation