596 lines
28 KiB
Markdown
596 lines
28 KiB
Markdown
# 07c. KEY VAULT Y CRIPTOGRAFÍA
|
|
**Manual de Arquitectura Técnica — Documento 07c**
|
|
**Versión:** 1.2
|
|
**Dependencia:** `S-CONTRACT.md`
|
|
**Estado:** Enterprise Standard
|
|
|
|
---
|
|
|
|
# C.1 Introducción
|
|
|
|
El Key Vault es el **tercer espacio de almacenamiento** del ecosistema, separado de:
|
|
- **Libro Mayor (SFE)**: Datos de negocio
|
|
- **Almacenamiento de archivos**: Hostinger/S3
|
|
- **Key Vault**: Llaves, credenciales y secretos
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ PRINCIPIO DE SEPARACIÓN │
|
|
│ │
|
|
│ "Las llaves nunca viajan con los datos que protegen" │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
# C.2 Arquitectura del Key Vault
|
|
|
|
## C.2.1 Componentes
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ KEY VAULT │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ SECRETS │ │ KEYS │ │ CERTS │ │
|
|
│ │ STORE │ │ STORE │ │ STORE │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
│ │ │ │ │
|
|
│ └──────────────────┼──────────────────┘ │
|
|
│ │ │
|
|
│ ┌──────┴──────┐ │
|
|
│ │ ACCESS │ │
|
|
│ │ CONTROL │ │
|
|
│ └──────┬──────┘ │
|
|
│ │ │
|
|
│ ┌──────┴──────┐ │
|
|
│ │ AUDIT LOG │ │
|
|
│ └─────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## C.2.2 Tipos de Secretos
|
|
|
|
| Tipo | Descripción | Ejemplo | Rotación |
|
|
|------|-------------|---------|----------|
|
|
| **API_KEY** | Llaves de APIs externas | OpenRouter, Groq | 90 días |
|
|
| **ENCRYPTION_KEY** | Llaves de cifrado de datos | AES-256 keys | 365 días |
|
|
| **DB_CREDENTIAL** | Credenciales de bases de datos | NocoDB, PostgreSQL | 30 días |
|
|
| **SERVICE_TOKEN** | Tokens de servicios internos | n8n, Nextcloud | 7 días |
|
|
| **USER_SECRET** | Secretos específicos de usuario | OAuth tokens | Variable |
|
|
| **CERTIFICATE** | Certificados TLS/mTLS | SSL certs | 90 días |
|
|
| **SIGNING_KEY** | Llaves de firma digital | JWT signing | 180 días |
|
|
|
|
---
|
|
|
|
# C.3 Modelo de Datos
|
|
|
|
## C.3.1 Tabla VAULT_SECRETS
|
|
|
|
```sql
|
|
CREATE TABLE VAULT_SECRETS (
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- IDENTIFICACIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
secret_id VARCHAR(100) UNIQUE NOT NULL,
|
|
version INTEGER DEFAULT 1,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CLASIFICACIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
secret_type VARCHAR(50) NOT NULL,
|
|
category VARCHAR(50),
|
|
tags VARCHAR(50)[],
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- VALOR (CIFRADO)
|
|
-- ═══════════════════════════════════════════════════════════
|
|
encrypted_value BYTEA NOT NULL,
|
|
encryption_algorithm VARCHAR(50) DEFAULT 'AES-256-GCM',
|
|
key_encryption_key_id VARCHAR(100),
|
|
nonce BYTEA,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- METADATA
|
|
-- ═══════════════════════════════════════════════════════════
|
|
description TEXT,
|
|
owner_id UUID,
|
|
owner_type VARCHAR(50),
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CICLO DE VIDA
|
|
-- ═══════════════════════════════════════════════════════════
|
|
status VARCHAR(20) DEFAULT 'ACTIVE',
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ,
|
|
last_rotated_at TIMESTAMPTZ,
|
|
rotation_interval_days INTEGER,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- ACCESO
|
|
-- ═══════════════════════════════════════════════════════════
|
|
access_policy JSONB DEFAULT '{}',
|
|
allowed_modules VARCHAR(50)[],
|
|
allowed_players UUID[],
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- AUDITORÍA
|
|
-- ═══════════════════════════════════════════════════════════
|
|
access_count INTEGER DEFAULT 0,
|
|
last_accessed_at TIMESTAMPTZ,
|
|
last_accessed_by VARCHAR(100),
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CONSTRAINTS
|
|
-- ═══════════════════════════════════════════════════════════
|
|
CONSTRAINT valid_status CHECK (
|
|
status IN ('ACTIVE', 'DISABLED', 'EXPIRED', 'ROTATING', 'DELETED')
|
|
),
|
|
CONSTRAINT valid_type CHECK (
|
|
secret_type IN ('API_KEY', 'ENCRYPTION_KEY', 'DB_CREDENTIAL',
|
|
'SERVICE_TOKEN', 'USER_SECRET', 'CERTIFICATE', 'SIGNING_KEY')
|
|
)
|
|
);
|
|
|
|
-- Índices
|
|
CREATE INDEX idx_vault_secret_id ON VAULT_SECRETS(secret_id);
|
|
CREATE INDEX idx_vault_type ON VAULT_SECRETS(secret_type);
|
|
CREATE INDEX idx_vault_status ON VAULT_SECRETS(status);
|
|
CREATE INDEX idx_vault_expires ON VAULT_SECRETS(expires_at) WHERE expires_at IS NOT NULL;
|
|
CREATE INDEX idx_vault_owner ON VAULT_SECRETS(owner_id);
|
|
```
|
|
|
|
## C.3.2 Tabla VAULT_ACCESS_LOG
|
|
|
|
```sql
|
|
CREATE TABLE VAULT_ACCESS_LOG (
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- IDENTIFICACIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- SECRETO ACCEDIDO
|
|
-- ═══════════════════════════════════════════════════════════
|
|
secret_id VARCHAR(100) NOT NULL,
|
|
secret_version INTEGER,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CONTEXTO DE ACCESO
|
|
-- ═══════════════════════════════════════════════════════════
|
|
trace_id UUID,
|
|
step_id UUID,
|
|
module_name VARCHAR(50),
|
|
operation VARCHAR(50) NOT NULL,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- ACTOR
|
|
-- ═══════════════════════════════════════════════════════════
|
|
accessor_type VARCHAR(50) NOT NULL,
|
|
accessor_id VARCHAR(100) NOT NULL,
|
|
accessor_ip INET,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- RESULTADO
|
|
-- ═══════════════════════════════════════════════════════════
|
|
status VARCHAR(20) NOT NULL,
|
|
error_code VARCHAR(50),
|
|
error_message TEXT,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- TEMPORALIDAD
|
|
-- ═══════════════════════════════════════════════════════════
|
|
timestamp TIMESTAMPTZ DEFAULT NOW(),
|
|
duration_ms INTEGER,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- SEGURIDAD
|
|
-- ═══════════════════════════════════════════════════════════
|
|
encryption_profile VARCHAR(20),
|
|
purpose TEXT,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CONSTRAINTS
|
|
-- ═══════════════════════════════════════════════════════════
|
|
CONSTRAINT valid_operation CHECK (
|
|
operation IN ('READ', 'CREATE', 'UPDATE', 'DELETE', 'ROTATE',
|
|
'ENABLE', 'DISABLE', 'LIST', 'DECRYPT', 'ENCRYPT')
|
|
),
|
|
CONSTRAINT valid_access_status CHECK (
|
|
status IN ('SUCCESS', 'DENIED', 'ERROR', 'EXPIRED', 'NOT_FOUND')
|
|
),
|
|
CONSTRAINT valid_accessor CHECK (
|
|
accessor_type IN ('MODULE', 'PLAYER', 'SYSTEM', 'ADMIN', 'SERVICE')
|
|
)
|
|
);
|
|
|
|
-- Índices para auditoría eficiente
|
|
CREATE INDEX idx_vault_log_secret ON VAULT_ACCESS_LOG(secret_id);
|
|
CREATE INDEX idx_vault_log_trace ON VAULT_ACCESS_LOG(trace_id) WHERE trace_id IS NOT NULL;
|
|
CREATE INDEX idx_vault_log_timestamp ON VAULT_ACCESS_LOG(timestamp);
|
|
CREATE INDEX idx_vault_log_accessor ON VAULT_ACCESS_LOG(accessor_type, accessor_id);
|
|
CREATE INDEX idx_vault_log_denied ON VAULT_ACCESS_LOG(status, timestamp)
|
|
WHERE status = 'DENIED';
|
|
```
|
|
|
|
## C.3.3 Tabla VAULT_ENCRYPTION_KEYS (KEK)
|
|
|
|
```sql
|
|
CREATE TABLE VAULT_ENCRYPTION_KEYS (
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- IDENTIFICACIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
key_id VARCHAR(100) UNIQUE NOT NULL,
|
|
version INTEGER DEFAULT 1,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- PROPIEDADES
|
|
-- ═══════════════════════════════════════════════════════════
|
|
algorithm VARCHAR(50) NOT NULL DEFAULT 'AES-256-GCM',
|
|
key_size_bits INTEGER NOT NULL DEFAULT 256,
|
|
purpose VARCHAR(50) NOT NULL,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- VALOR (protegido por Master Key externa)
|
|
-- ═══════════════════════════════════════════════════════════
|
|
encrypted_key_material BYTEA NOT NULL,
|
|
master_key_ref VARCHAR(100),
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CICLO DE VIDA
|
|
-- ═══════════════════════════════════════════════════════════
|
|
status VARCHAR(20) DEFAULT 'ACTIVE',
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
activated_at TIMESTAMPTZ,
|
|
expires_at TIMESTAMPTZ,
|
|
deactivated_at TIMESTAMPTZ,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CONSTRAINTS
|
|
-- ═══════════════════════════════════════════════════════════
|
|
CONSTRAINT valid_kek_status CHECK (
|
|
status IN ('PENDING', 'ACTIVE', 'DEACTIVATED', 'DESTROYED')
|
|
),
|
|
CONSTRAINT valid_purpose CHECK (
|
|
purpose IN ('DATA_ENCRYPTION', 'SECRET_ENCRYPTION', 'SIGNING', 'KEY_WRAPPING')
|
|
)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
# C.4 Perfiles de Cifrado
|
|
|
|
## C.4.1 Perfil NONE
|
|
|
|
```yaml
|
|
profile: NONE
|
|
description: "Sin cifrado gestionado por el sistema"
|
|
transport: TLS 1.3 (obligatorio)
|
|
at_rest: Sin cifrar
|
|
use_cases:
|
|
- Datos públicos
|
|
- Logs técnicos no sensibles
|
|
- Métricas de rendimiento
|
|
key_vault: No requerido
|
|
```
|
|
|
|
## C.4.2 Perfil E2E_BASIC
|
|
|
|
```yaml
|
|
profile: E2E_BASIC
|
|
description: "Cifrado estándar para datos internos"
|
|
transport: TLS 1.3
|
|
at_rest:
|
|
algorithm: AES-256-GCM
|
|
key_derivation: PBKDF2-SHA256
|
|
key_rotation: Anual
|
|
use_cases:
|
|
- Documentos internos
|
|
- Emails corporativos
|
|
- Datos de configuración
|
|
key_vault:
|
|
required: true
|
|
key_type: ENCRYPTION_KEY
|
|
access_policy: module_based
|
|
```
|
|
|
|
## C.4.3 Perfil E2E_STRICT
|
|
|
|
```yaml
|
|
profile: E2E_STRICT
|
|
description: "Cifrado fuerte para datos sensibles"
|
|
transport: mTLS (mutual TLS)
|
|
at_rest:
|
|
algorithm: AES-256-GCM
|
|
key_derivation: Argon2id
|
|
key_rotation: Trimestral
|
|
envelope_encryption: true
|
|
additional:
|
|
- PII masking antes de procesar
|
|
- Audit logging obligatorio
|
|
- Zero-knowledge donde sea posible
|
|
use_cases:
|
|
- PII (datos personales)
|
|
- Datos financieros
|
|
- Información médica
|
|
- Biometría
|
|
key_vault:
|
|
required: true
|
|
key_type: ENCRYPTION_KEY
|
|
access_policy: per_record
|
|
key_isolation: true
|
|
```
|
|
|
|
---
|
|
|
|
# C.5 Operaciones del Key Vault
|
|
|
|
## C.5.1 URIs del Key Vault
|
|
|
|
El contrato común usa referencias URI al Key Vault:
|
|
|
|
```
|
|
kv://production/encryption/player_abc123
|
|
kv://production/api-keys/openrouter
|
|
kv://staging/certificates/mtls-client
|
|
```
|
|
|
|
Formato:
|
|
```
|
|
kv://{environment}/{category}/{secret_id}
|
|
```
|
|
|
|
## C.5.2 Integración con el Contrato
|
|
|
|
En el request:
|
|
```json
|
|
"security": {
|
|
"encryption_profile": "E2E_STRICT",
|
|
"data_sensitivity": "HIGH",
|
|
"key_vault_ref": "kv://production/encryption/player_abc123"
|
|
}
|
|
```
|
|
|
|
Alfred al procesar:
|
|
1. Extrae `key_vault_ref`
|
|
2. Solicita la llave al Key Vault (pasando `trace_id`)
|
|
3. Descifra el payload si viene cifrado
|
|
4. Procesa con GRACE
|
|
5. Cifra el resultado con la misma llave (o una derivada)
|
|
6. Almacena resultado cifrado
|
|
|
|
---
|
|
|
|
# C.6 Flujo de Cifrado End-to-End
|
|
|
|
## C.6.1 Envelope Encryption
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ ENVELOPE ENCRYPTION │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────┐ │
|
|
│ │ Plaintext │ │
|
|
│ │ Data │ │
|
|
│ └──────┬──────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ Data │◄───│ DEK │ (Data Encryption Key) │
|
|
│ │ Encrypted │ │ (efímera) │ │
|
|
│ └──────┬──────┘ └──────┬──────┘ │
|
|
│ │ │ │
|
|
│ │ ▼ │
|
|
│ │ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ │ DEK │◄───│ KEK │ │
|
|
│ │ │ Encrypted │ │ (Key Vault) │ │
|
|
│ │ └──────┬──────┘ └─────────────┘ │
|
|
│ │ │ │
|
|
│ ▼ ▼ │
|
|
│ ┌──────────────────────────────────────┐ │
|
|
│ │ Stored Package: │ │
|
|
│ │ - encrypted_data │ │
|
|
│ │ - encrypted_dek │ │
|
|
│ │ - kek_id │ │
|
|
│ │ - nonce │ │
|
|
│ │ - algorithm │ │
|
|
│ └──────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## C.6.2 Implementación Python
|
|
|
|
```python
|
|
import os
|
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
import base64
|
|
|
|
class EnvelopeEncryption:
|
|
"""Implementa envelope encryption para E2E_STRICT."""
|
|
|
|
def __init__(self, key_vault_client):
|
|
self.vault = key_vault_client
|
|
|
|
async def encrypt(self, plaintext: bytes, kek_id: str) -> dict:
|
|
"""Cifra datos usando envelope encryption."""
|
|
# 1. Generar DEK efímera
|
|
dek = os.urandom(32)
|
|
data_nonce = os.urandom(12)
|
|
|
|
# 2. Cifrar datos con DEK
|
|
aesgcm = AESGCM(dek)
|
|
encrypted_data = aesgcm.encrypt(data_nonce, plaintext, None)
|
|
|
|
# 3. Obtener KEK y cifrar DEK
|
|
kek = await self.vault.get_secret(kek_id)
|
|
dek_nonce = os.urandom(12)
|
|
kek_aesgcm = AESGCM(kek)
|
|
encrypted_dek = kek_aesgcm.encrypt(dek_nonce, dek, kek_id.encode())
|
|
|
|
return {
|
|
"encrypted_data": base64.b64encode(encrypted_data).decode(),
|
|
"encrypted_dek": base64.b64encode(encrypted_dek).decode(),
|
|
"data_nonce": base64.b64encode(data_nonce).decode(),
|
|
"dek_nonce": base64.b64encode(dek_nonce).decode(),
|
|
"kek_id": kek_id,
|
|
"algorithm": "AES-256-GCM"
|
|
}
|
|
|
|
async def decrypt(self, package: dict) -> bytes:
|
|
"""Descifra datos usando envelope encryption."""
|
|
# 1. Obtener KEK
|
|
kek = await self.vault.get_secret(package["kek_id"])
|
|
|
|
# 2. Descifrar DEK
|
|
kek_aesgcm = AESGCM(kek)
|
|
dek = kek_aesgcm.decrypt(
|
|
base64.b64decode(package["dek_nonce"]),
|
|
base64.b64decode(package["encrypted_dek"]),
|
|
package["kek_id"].encode()
|
|
)
|
|
|
|
# 3. Descifrar datos
|
|
aesgcm = AESGCM(dek)
|
|
return aesgcm.decrypt(
|
|
base64.b64decode(package["data_nonce"]),
|
|
base64.b64decode(package["encrypted_data"]),
|
|
None
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
# C.7 Gestión de Llaves
|
|
|
|
## C.7.1 Jerarquía de Llaves
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ JERARQUÍA DE LLAVES │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ MASTER KEY (MK) │ │
|
|
│ │ Almacenada fuera del sistema │ │
|
|
│ │ (HSM / KMS externo / Variable de entorno) │ │
|
|
│ └────────────────────────┬────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────┼───────────────┐ │
|
|
│ ▼ ▼ ▼ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ KEK-DATA │ │ KEK-SECRETS │ │ KEK-SIGNING │ │
|
|
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
|
│ │ │ │ │
|
|
│ ▼ ▼ ▼ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ DEK-1..n │ │ API Keys │ │ JWT Keys │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## C.7.2 Política de Rotación
|
|
|
|
| Tipo de Llave | Rotación | Período de Gracia | Automatizable |
|
|
|---------------|----------|-------------------|---------------|
|
|
| Master Key | Anual | 30 días | No (manual) |
|
|
| KEK | Semestral | 14 días | Sí |
|
|
| DEK | Por sesión | N/A | Automático |
|
|
| API Keys | 90 días | 7 días | Sí |
|
|
|
|
---
|
|
|
|
# C.8 Control de Acceso
|
|
|
|
## C.8.1 Políticas de Acceso
|
|
|
|
```json
|
|
{
|
|
"secret_id": "openrouter-api-key",
|
|
"access_policy": {
|
|
"type": "MODULE_BASED",
|
|
"rules": [
|
|
{
|
|
"principal_type": "MODULE",
|
|
"principals": ["CLASSIFIER", "SUMMARIZER", "OCR_CORE"],
|
|
"operations": ["READ"],
|
|
"conditions": {
|
|
"require_trace_id": true
|
|
}
|
|
},
|
|
{
|
|
"principal_type": "ADMIN",
|
|
"principals": ["admin@tzzr.pro"],
|
|
"operations": ["READ", "UPDATE", "ROTATE", "DELETE"],
|
|
"conditions": {
|
|
"require_mfa": true
|
|
}
|
|
}
|
|
],
|
|
"default_deny": true
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
# C.9 Integración con SENTINEL
|
|
|
|
## C.9.1 Reglas de Auditoría
|
|
|
|
```yaml
|
|
rules:
|
|
KV-001:
|
|
name: "Accesos denegados excesivos"
|
|
severity: HIGH
|
|
condition: "denied_count_1h > 5 per accessor"
|
|
action: ALERT_SECURITY
|
|
|
|
KV-002:
|
|
name: "Secreto próximo a expirar"
|
|
severity: MEDIUM
|
|
condition: "expires_at < NOW() + 7 days"
|
|
action: NOTIFY_ADMIN
|
|
|
|
KV-003:
|
|
name: "Rotación pendiente"
|
|
severity: MEDIUM
|
|
condition: "last_rotated_at < NOW() - rotation_interval"
|
|
action: SCHEDULE_ROTATION
|
|
```
|
|
|
|
---
|
|
|
|
# C.10 Checklist de Implementación
|
|
|
|
## Infraestructura:
|
|
- [ ] Crear tablas VAULT_SECRETS, VAULT_ACCESS_LOG, VAULT_ENCRYPTION_KEYS
|
|
- [ ] Configurar Master Key (variable de entorno o KMS)
|
|
- [ ] Generar KEKs iniciales
|
|
- [ ] Configurar backup automático
|
|
|
|
## Integración:
|
|
- [ ] Implementar cliente Key Vault
|
|
- [ ] Integrar con ContractBuilder
|
|
- [ ] Implementar envelope encryption
|
|
|
|
## Seguridad:
|
|
- [ ] Configurar políticas de acceso
|
|
- [ ] Habilitar logging de accesos
|
|
- [ ] Configurar alertas SENTINEL
|
|
- [ ] Establecer rotación automática
|
|
|
|
---
|
|
|
|
**Fin del Documento 07c — Key Vault y Criptografía**
|
|
|
|
*Referencia: `S-CONTRACT.md` v1.2*
|