Key Management Policy
Key Management Policy
Project / Organization:
{{ORG_NAME}}ALAI Holding AS — Drop Payment App Policy Number: POL-SEC-KM-{{NUMBER}}001 Version:{{VERSION}}1.0 Date:{{DATE}}2026-02-23 Author:{{AUTHOR}}Security Architect Status: Draft| In Review | ApprovedReviewers:{{REVIEWERS}}CISO, CTO, DPO Next Review:{{REVIEW_DATE}}2027-02-23 Classification: Confidential — Restricted Distribution
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | Initial draft — Drop key management for AWS KMS + Secrets Manager |
1. Purpose & Scope
Purpose: This policy defines the lifecycle management requirements for all cryptographic keys and secrets used by {{ORG_NAME}},ALAI Holding AS for the Drop payment app, including generation, distribution, storage, usage, rotation, revocation, and destruction.
Regulatory basis:
- GDPR Art. 32 — appropriate technical measures for personal data
- Personopplysningsloven (LOV-2018-06-15-38) § 28
- IKT-forskriften (FOR-2003-05-21-630) §§ 5-6
- DORA (EU) 2022/2554 Art. 9(4)(d) — encryption key management
- Hvitvaskingsloven (LOV-2018-06-01-23) § 30 — KYC data protection at rest
Scope: All cryptographic keys and secrets in use across:
All production,Production, staging, and development environments for DropAll cloudAWS infrastructureand(KMS,on-premisesSecretssystemsManager, S3, App Runner)- All employee
devicesand contractor workstations handling Confidential or Restricted data - All third-party integrations where
{{ORG_NAME}}ALAI Holding AS holds keys
Policy Owner: {{CISO_NAME}}CISO ({{CISO_EMAIL}})[email protected])
Operational Owner: {{SECURITY_TEAM}}Security team
2. Key Types & PurposesInventory
2.1 Complete Key Taxonomy
| Key ID | Type | Algorithm | Purpose | Data Classification |
Owner | Storage |
|---|---|---|---|---|---|---|
drop-national-id-key |
AES- |
Restricted (L4) | Security team | AWS KMS (eu-north-1) | ||
drop-db-master-key |
AES-256 XTS | PostgreSQL TDE — Phase 2 | Restricted (L4) | Security team | AWS KMS (eu-north-1) | |
drop-kyc-key |
KEK (Object) | AES-256-GCM | KYC document encryption (S3 SSE-KMS) | Restricted (L4) | Security team | AWS KMS (eu-north-1) |
drop-backup-key |
KEK (Backup) | AES-256 | Database |
Restricted (L4) | Security team | AWS |
JWT_SECRET |
Signing secret | HMAC-SHA-256 | JWT session token signing | Confidential (L3) | Security team | AWS Secrets Manager |
TLS-EXT |
TLS Certificate |
ECDSA P-256 | External HTTPS (getdrop.no) | — | Cloudflare managed | |
BankID-cert |
TLS Certificate |
— | BankID managed | |||
SUMSUB-API- |
API Key | HMAC-SHA-256 | Confidential (L3) | AWS Secrets Manager | ||
DEK-* |
Restricted (L4) | Application | Generated by AWS KMS GenerateDataKey |
2.2 Secrets Inventory (Non-KMS)
| Secret Name | Purpose | Storage | Rotation |
|---|---|---|---|
JWT_SECRET |
JWT token signing | AWS Secrets Manager | Quarterly |
SUMSUB_SECRET_KEY |
Sumsub KYC integration | AWS Secrets Manager | Annual or on compromise |
BANKID_CLIENT_SECRET |
BankID OIDC client secret | AWS Secrets Manager | Per BankID schedule |
DATABASE_URL |
PostgreSQL connection string | AWS Secrets Manager | On DB credential rotation |
SENTRY_DSN |
Sentry error monitoring | AWS Secrets Manager | Annual |
3. Key Hierarchy
AWS KMS Root of Trust (eu-north-1 primary)
│
├── drop-national-id-key (AES-256-GCM — annual rotation)
│ └── DEK-{user_id}-{timestamp}: Per-record envelope DEKs
│ └── Encrypts: fødselsnummer field in users table
│ └── Stored: base64(encryptedDEK || iv || tag || ciphertext)
│
├── drop-db-master-key (AES-256 XTS — annual rotation)
│ └── Encrypts: PostgreSQL database at rest (Phase 2 — AWS RDS)
│
├── drop-kyc-key (AES-256-GCM — annual rotation)
│ └── Encrypts: KYC document objects in AWS S3 (SSE-KMS)
│
└── AWS KMS Root of Trust (eu-west-1 — SEPARATE REGION for backups)
└── drop-backup-key (AES-256 — annual rotation)
└── Encrypts: All database backup files
AWS Secrets Manager
├── JWT_SECRET (HMAC-SHA-256 — quarterly rotation)
│ └── Signs: Drop JWT session tokens (HS256 via jose ^6.1.3)
├── SUMSUB_SECRET_KEY (API key — annual rotation)
├── BANKID_CLIENT_SECRET (OIDC client secret — BankID schedule)
└── DATABASE_URL (connection string — on rotation)
Cloudflare / Let's Encrypt
└── TLS Certificate (ECDSA P-256 — 90-day automated rotation)
└── External HTTPS: getdrop.no
BankID Norge AS CA
└── BankID Certificate (RSA-2048 — per BankID renewal schedule)
└── Used for: BankID JWT signature verification
4. Key Lifecycle
3.4.1 Lifecycle Overview
flowchart LR
GEN[Generation] -->|"Secure,AWS random"KMS CSPRNG"| DIST[Distribution]
DIST -->|"EncryptedRuntime channel"API call\nnever in env files"| STORE[Storage]
STORE -->|"AccessKMS / Secrets Manager\naccess controlled"| USE[Usage]
USE -->|"Scheduled"| ROT[Rotation]
ROT -->|"New key active"active\noverlap period"| USE
ROT -->|"Old key retired"| ARCH[Archive / Revoke]
ARCH -->|"End of life"| DEST[Destruction]
REVOKE[Emergency Revocation] -->|"Compromise detected"| DEST
USE --> REVOKE
3.4.2 Generation
Entropy requirements:
- All keys MUST be generated using AWS KMS or a cryptographically secure random number generator (CSPRNG)
Minimum entropy: 256 bits for symmetric keys, as specified for asymmetric- NEVER
use:use user-supplied passphrases, timestamps, UUIDs, or predictable values as keys - NEVER generate keys in
userspaceapplicationwithoutcodeanforestablishedRestrictedcryptodatalibrary— use AWS KMSGenerateKeyorGenerateDataKey
Approved key generation methods:
| Key Type | Generation Method | Tool |
|---|---|---|
| Symmetric (AES-256) KMS keys | KMS CreateKey API |
AWS KMS (FIPS 140-2 Level 3 HSMs) |
| Envelope DEKs | KMS GenerateDataKey API |
AWS KMS |
| JWT signing secret | crypto.randomBytes(32).toString('hex') |
|
protocol |
||
CA |
||
| ||
Generation environment:
KeysAll KMS-managed keys for Restricted data:MUST begenerated within AWS KMSor HSMHSMs — key material never leaves AWS- JWT_SECRET: generated using
crypto.randomBytes(32)then stored inapplicationAWScodeSecrets Keys for Confidential data: MAY be generated in application code using OS CSPRNGManager- All key generation events
MUSTloggedbeinloggedAWS CloudTrail
3.4.3 Distribution
Principles:
- Never transmit keys in plaintext over any channel
- Never include keys in source code,
configuration.envfiles committed to Git, configuration files in VCS, orlogslog output - Never send keys via email, Slack, or any messaging platform
- Always
usefetchencryptedsecretschannelsatwithapplicationmutualruntimeauthenticationfromforAWSkeySecretsdistributionManager or KMS API
Distribution methods:
| Scenario | Method | Notes | |
|---|---|---|---|
| Application runtime |
|||
| Envelope DEK generation | AWS KMS GenerateDataKey per encryption operation |
Key material decrypted in |
|
| Developer access to | Least privilege | ||
| CI/CD pipeline secrets | |||
3.4.4 Storage
Storage hierarchy:hierarchy for Drop:
Level 1 — HSMAWS KMS HSMs (HardwareFIPS Security140-2 Module)Level 3)
├── Master keysdrop-national-id-key (KEKs)fødselsnummer forencryption)
Restricted├── datadrop-db-master-key (database TDE — Phase 2)
├── drop-kyc-key (KYC document S3 encryption)
└── Rootdrop-backup-key CA(backup privateencryption keys— eu-west-1)
Level 2 — Cloud KMS (AWS KMSSecrets / Azure Key Vault / GCP Cloud KMS)Manager
├── KeyJWT_SECRET Encryption(HMAC Keyssigning for Confidential datakey)
├── ServiceSUMSUB_SECRET_KEY signing(API keyskey)
├── BANKID_CLIENT_SECRET (OIDC secret)
└── EncryptedDATABASE_URL DEK(connection storagestring)
Level 3 — Secrets Manager (HashiCorp Vault / AWS Secrets Manager)
├── Application secrets (DB passwords, API keys)
├── TLS certificate private keys
└── Derived symmetric keys (encrypted by Level 2 keys)
Level 4 — Application memory (ephemeral only)
├── DEKsDecrypted DEK during active useencryption/decryption operation
└── Decrypted secretsJWT_SECRET during operationtoken Note:signing/verification
NOTE: NEVER persist Level 43 to disk
Prohibited storage locations:
- Source code or config files in
VCSGit (.env,config.ts,secrets.json) - Application logs or error messages (Sentry, BetterStack)
- Unencrypted database columns
(for Restricted data) - Email attachments or
messagingSlackplatforms Shared drives or unencrypted file sharesmessages- Browser localStorage or sessionStorage
EnvironmentContainervariablesimageinlayerscontainer(Dockerfile,images.dockerignoreexclusions not sufficient)
3.4.5 Usage
Access control principles:
| Principle | Implementation |
|---|---|
| Least privilege | |
| Separation of duties | |
| Key purpose binding | drop-national-id-key used |
| Audit all access | Every |
| Time-bound access |
KeyKMS usagekey auditpolicy requirements:roles:
AllIAM Role Permissions MFA Required drop-key-adminCreateKey, ScheduleKeyDeletion, RotateKey — NO Decrypt YES drop-app-encryptkms:Encrypt,kms:GenerateDataKeyNo (service account) drop-compliance-decryptkms:Decryptfordrop-national-id-keyoperationsonlylogged:YES actor,operation,keydrop-key-auditorID,kms:ListKeys,timestamp,kms:DescribeKey,sourceCloudTrailIPreadLogsYES shippedtoSIEMdrop-app-runnerwithinkms:GenerateDataKey,1kms:Decryptminute(scopedUnusualperaccesskey)patternsNo alert(IAMwithinrole,5VPCminutesconstraint)- Generate
remainsnewvalidJWT_SECRETvalue - Deploy new secret to AWS Secrets Manager
- Rolling deploy of Drop app (picks up new secret)
- Wait for
{{OVERLAP_PERIOD}}allafterexisting sessions to expire (max 24h — JWT expiry) - All sessions issued with old secret are now invalid (users re-authenticate)
- Delete old secret version from Secrets Manager
- Known or suspected key compromise (e.g., key material logged, unauthorized access)
- Employee departure
(forwithkeysaccessintopersonalkeycustody)management IAM roles - System compromise where key was in use
- Legal hold or regulatory requirement
CustomerDetectionrequestof(forunauthorizedcustomerdecryptionAPIactivitykeys)in CloudTrail logs
3.4.6 Rotation Schedule
| Key |
Rotation Period | Method | Owner | Alert if Overdue |
|---|---|---|---|---|
drop-national-id-key |
Annual | Security team | Yes — 7 days overdue | |
drop-db-master-key |
Annual | AWS KMS automatic key rotation | Security team | Yes — 7 days overdue |
drop-kyc-key |
Annual | AWS KMS automatic key rotation | Security team | Yes — 7 days overdue |
drop-backup-key |
Annual | Manual + AWS KMS rotation | Security team | Yes — 7 days overdue |
JWT_SECRET |
Quarterly | Yes — 3 days overdue | ||
| TLS |
90 days | Automated ( |
Platform | Yes — 14 days |
| Calendar reminder | ||||
SUMSUB_SECRET_KEY |
Annual or on compromise | Manual via Sumsub dashboard | Security team | Yes — 7 days overdue |
RotationJWT_SECRET rotation overlap period:procedure:
AWS KMS automatic rotation: When enabled, AWS KMS generates new key activationmaterial toannually allowwhile in-flightretaining operationsall toprior complete.
Keyfor rotationdecryption monitoringof dashboard:existing {{DASHBOARD_URL}}ciphertext. Re-encryption of existing data is required when performing cryptographic erasure (GDPR Art. 17).
3.4.7 Revocation Procedures
Trigger conditions for immediate revocation:
Emergency revocation procedure (< 1 hour):
Step 1: Alert Security Lead immediately via #security-incident Slack channel
Step 2: Identify allscope systems— usingwhich thedata was accessible with compromised key?
Step 3: AWS KMS: Disable compromised key (prevents new encrypt/decrypt)
Step 3:4: Generate replacement key (seevia §3.2)KMS CreateKey
Step 4:5: KMS:Re-encrypt Disable/revokeall data protected by compromised key Step(priority: 5:Restricted Deploy new key to all systems (use zero-downtime rotation)first)
Step 6: Update KMS key references in application configuration
Step 7: Deploy updated application configuration
Step 8: Verify all systems using new key
Step 7:9: Audit forCloudTrail: unauthorizedWhat usagedecrypt ofoperations used the compromised key in last 30 days?
Step 8:10: Assess breach notification requirement (see data-breach-response-plan.md §5)
Step 11: Document in incident log
Step 9:12: Post-mortem within 48 hours
CertificateJWT_SECRET revocation:emergency rotation (session compromise):
Step Immediately1: uponGenerate compromise:new RevokeJWT_SECRET viaimmediately
CAStep and2: pushDeploy CRLto update AWS OCSPSecrets response:Manager
MustStep reflect3: revocationRolling withindeploy {{OCSP_UPDATE_TIME}} Drop Notifyapplication — all certificatein-flight consumers sessions invalidated
Step 4: All users must re-authenticate (acceptable security impact)
Step 5: Document in incident log
3.4.8 Destruction
When destruction is required:
- Key has been rotated out and overlap period expired
System(KMS:decommissionedschedule deletion with 30-day waiting period)- Crypto-
shreddingshredding:(deletedeletingencrypteduser data by destroyingitsthe envelope DEK encrypted underdrop-national-id-key—(GDPRerasure)Art. 17 erasure mechanism) - System decommissioned
Destruction methods:
| Key Location | Destruction Method | Verification |
|---|---|---|
ScheduleKeyDeletion ( |
KMS + CloudTrail audit log | |
| AWS Secrets Manager | DeleteSecret |
|
| Application memory (DEK) | |
|
|
||
Verification:Crypto-shredding for GDPR erasure (Art. 17):
KeyWhen destructiona mustuser berequests verifiedaccount —deletion:
- The envelope DEK for that
decryptionuser'swithfødselsnummerdestroyediskeydeletedfails.fromDestructionthelog:database - The
key destruction event recorded in {{KEY_DESTRUCTION_LOG_LOCATION}}4. Key HierarchyRoot of Trust: HSM / Cloud KMS Root │ ├── KEK-01: Database Encryption Master Keyciphertext (annualfødselsnummer)rotation)becomes│permanently├──unreadable - AML
Per-tableretention:encryptiontransactionkeyrecords(quarterlyretainedrotation)5│years│but└──user'sEncrypts:fødselsnummerDatabaseisfield-levelunrecoverable
5. Key Management System
Primary KMS: {{KMS_TOOL}}AWS KMS (e.g.,eu-north-1 — Stockholm region)
Backup KMS for backups: AWS KMS,KMS Azure(eu-west-1 Key— Vault,Ireland) HashiCorpfor Vault)drop-backup-key only
Secondary/BackupSecrets KMS:management: {{SECONDARY_KMS}}AWS Secrets Manager (separate region/provider)
HSM: {{HSM_PROVIDER}} (for highest-classification keys)eu-north-1)
Multi-region note: KMS accesskeys control:
| ||
| ||
| ||
| ||
6. Access Controls for Key Operations
Separation of duties requirements:
| Operation | Required Roles | Approval Process |
|---|---|---|
| Create new |
drop-key-admin + CISO approval |
|
| Rotate |
drop-key-admin (proposer) + CISO (approver) |
Dual approval via IAM policy |
| Emergency |
Security Lead (any |
Single |
drop-key-admin + CISO |
||
| Grant new service access to key | Security Lead + system owner | |
| Rotate JWT_SECRET | Security Lead | Single — with deployment coordination |
7. Key Escrow & Recovery
Escrow policy: KEKs and backup encryption keys must be escrowed.
Escrow process:
Key generated in KMS/HSMKey material exported (encrypted under escrow master key) — only where KMS allows exportExported material split into {{N}} shares using Shamir's Secret Sharing (requires {{K}} of {{N}} shares to reconstruct)Shares distributed to: {{SHARE_HOLDER_1}}, {{SHARE_HOLDER_2}}, {{SHARE_HOLDER_3}}Share holders store in secure location (safe deposit box or equivalent)Quarterly: Verify shares exist and holders still accessible
Recovery procedure (requires {{K}} share holders):
Incident Commander opens emergency key recovery ticket{{K}} share holders convene (in person or secure video call)Shares combined to reconstruct key materialKey imported into KMSFull event logged and post-mortem required
Note: For cloud KMS-only keys that cannot be exported: Rely on KMS redundancy (multi-region replication) — no separate escrow needed.
8. Audit Logging for Key Operations
All key operations logged,logged in AWS CloudTrail, including:
- Key
generationcreation (actor, key ID, key type, timestamp) Key encryption/Encryption/decryption (actor, key ID,datacontext, timestamp)- Key rotation (actor, old key
ID,version, new keyID,version, timestamp) - Key
revocationdisabling / deletion scheduling (actor, key ID, reason,timestamp) Key destruction (actor, key ID, verification hash,timestamp)- Access grant/revoke (actor, grantee, key ID, permissions, timestamp)
- Failed access attempts (actor, key ID, reason, timestamp)
Log destination: {{SIEM_TOOL}}AWS —CloudTrail → S3 (immutable, append-onlyonly) + BetterStack aggregation
Log retention: 25 years (PCI-DSS)AML /compliance 3 years (SOC 2)minimum — whicheverHvitvaskingsloven is§ longer30)
Alert on:
- Decrypt
volume,operationsoff-hoursbyaccessunexpectedtoIAMKEKs,role - Failed KMS access attempts > 3 in 5 minutes
- Off-hours access to
drop-national-id-keyordrop-kyc-key - Any
ScheduleKeyDeletionevent (immediate alert)
8. Fødselsnummer Field Encryption — Implementation Pattern
Key: drop-national-id-key (AWS KMS — separate from database master key)
Stored: Only AES-256-GCM ciphertext — never plaintext fødselsnummer in database
// Envelope encryption for fødselsnummer — src/drop-app/src/lib/encrypt.ts (Phase 2)
// Key: drop-national-id-key (AWS KMS — separate from db master)
// Stored: base64(encryptedDEK || iv || tag || ciphertext)
async function encryptNationalId(fodselsnummer: string): Promise<string> {
const iv = crypto.randomBytes(12); // 96-bit random IV — never reused
const dek = await kmsClient.generateDataKey({
KeyId: process.env.NATIONAL_ID_KEY_ARN,
KeySpec: 'AES_256'
});
const cipher = crypto.createCipheriv('aes-256-gcm', dek.Plaintext, iv);
const ciphertext = Buffer.concat([cipher.update(fodselsnummer, 'utf8'), cipher.final()]);
const tag = cipher.getAuthTag();
// Securely zero DEK plaintext from memory after use
dek.Plaintext.fill(0);
// Return: base64(encryptedDEK || iv || tag || ciphertext)
return Buffer.concat([dek.CiphertextBlob, iv, tag, ciphertext]).toString('base64');
}
async function decryptNationalId(encrypted: string): Promise<string> {
const buf = Buffer.from(encrypted, 'base64');
// AWS KMS CiphertextBlob is always 184 bytes for AES_256 DEK
const encryptedDek = buf.subarray(0, 184);
const iv = buf.subarray(184, 196); // 12 bytes
const tag = buf.subarray(196, 212); // 16 bytes
const ciphertext = buf.subarray(212);
const dek = await kmsClient.decrypt({ CiphertextBlob: encryptedDek });
const decipher = crypto.createDecipheriv('aes-256-gcm', dek.Plaintext, iv);
decipher.setAuthTag(tag);
const plaintext = decipher.update(ciphertext) + decipher.final('utf8');
dek.Plaintext.fill(0);
return plaintext;
}
Access restriction: Only the Compliance function (KYC verification, AML reporting) has IAM access to drop-compliance-decrypt role. Application code cannot decrypt fødselsnummer except during explicit compliance workflows.
9. ComplianceException MappingProcess
Exceptions are not permitted for: Restricted (L4) data (fødselsnummer, KYC documents) — no exceptions to field-level encryption requirement.
Exception request process:
- Submit request to: [email protected]
- Required: system, data classification, key being excepted, business justification, risk assessment, compensating controls, proposed exception duration (max 12 months)
- Approval required from: CISO
- Exceptions logged in: compliance register
- Review: Quarterly — exceptions exceeding 12 months require board-level approval
Active exceptions:
| Compensating Controls | |||
|---|---|---|---|
| JWT_SECRET |
|||
10. Emergency Procedures — Key Compromise
Immediate response checklist (execute within 1 hour of suspected compromise):
□ Activate incident response:response #incident-key-compromise-{{DATE}}— □ Notify:notify: Security Lead + CISO + Engineering LeadCTO
□ Identify scope: Which data was accessible with compromised key?
□ RevokeAWS KMS: Disable compromised key immediately
□ For JWT_SECRET compromise: rotate immediately (seeall §3.7)sessions invalidated)
□ Generate new replacement key
□ Re-encrypt all data that was protected by compromised key □(Restricted Rotatedata all derived keys that used compromised key as sourcefirst)
□ Audit logs:CloudTrail: What was decrypted using the compromised key in the pastlast 30 days?
□ Assess if breach notification requiredrequirement (see data-breach-response-plan.md §5)
→ GDPR Art. 33 / Personopplysningsloven § 32: 72h to Datatilsynet if personal data affected
→ Finanstilsynet notification if payment data affected
□ Document everything in incident log
□ Post-mortem within 48 hours
Re-encryption priority order:priority:
Most sensitive data firstFødselsnummer (drop-national-id-key) — Restricted→(L4),Confidentialhighest→ Internal)priorityMostKYCrecently accessed data firstdocuments (likelydrop-kyc-key)to—haveRestrictedbeen targeted)(L4)OldestDatabasedatabackuplastfiles (historicaldrop-backup-key)data,—lowerRestrictedimmediate(L4)- Database master (
drop-db-master-key) — Phase 2
Estimated re-encryption time for {{N}} records: {{ESTIMATE}} — plan for {{ESTIMATE × SAFETY_FACTOR}}
Approval
| Role | Name | Date | Signature |
|---|---|---|---|
| Author | Security Architect | 2026-02-23 | |
| CISO | |||
| CTO | |||
| DPO ( |
|||
| Management |