AWS KMS & Encryption
A comprehensive deep dive into AWS Key Management Service — key types, envelope encryption, key policies, grants, rotation, multi-region keys, service integrations, CloudHSM, and encryption patterns for the DVA-C02 exam.
What is AWS KMS?
AWS Key Management Service (KMS) is a managed encryption service that creates and controls cryptographic keys used to encrypt data across AWS services and your own applications. KMS runs on FIPS 140-2 validated Hardware Security Modules (HSMs) — the physical hardware that stores and never exports key material.
Core mental model: KMS never gives you the raw key — it performs cryptographic operations on your behalf inside its HSMs. You call KMS APIs; KMS does the math and returns ciphertext or plaintext. Your key material never leaves AWS KMS unencrypted.
Key Types
| Key Type | Alias | Cost | Rotation | Control | Cross-Account |
|---|---|---|---|---|---|
| AWS Owned Keys | None (internal) | Free | Automatic | None | No |
| AWS Managed Keys | aws/s3, aws/rds, etc. | Free | Automatic (annual) | View only | No |
| Customer Managed Keys (CMK) | Custom alias | $1/month | Optional (annual or on-demand) | Full | Yes |
| External Key Material (BYOK) | Custom alias | $1/month | Manual only | Full | Yes |
| CloudHSM-backed Keys | Custom alias | $1/month + HSM cost | Manual | Full | Yes |
Key rule for the exam: You can only control key policy, grants, and rotation for Customer Managed Keys. AWS Managed and Owned keys are not directly manageable.
Key Material Origin
When you create a CMK, you choose where the key material comes from:
| Origin | Key generated by | Deletion | Use case |
|---|---|---|---|
AWS_KMS | KMS HSM | Schedule deletion (7–30 days) | Default — most use cases |
EXTERNAL | You (imported) | Immediate delete allowed | Compliance: bring own key material |
AWS_CloudHSM | Your CloudHSM cluster | Schedule deletion | Dedicated hardware requirement |
KMS API Operations
| API | Input Size Limit | Returns | Use case |
|---|---|---|---|
Encrypt | 4 KB | Ciphertext blob | Encrypt small data (passwords, tokens) |
Decrypt | 6 KB ciphertext | Plaintext | Decrypt data encrypted by KMS |
ReEncrypt | 6 KB ciphertext | New ciphertext | Rotate encryption to new key without exposing plaintext |
GenerateDataKey | N/A | Plaintext DEK + encrypted DEK | Envelope encryption of large data |
GenerateDataKeyWithoutPlaintext | N/A | Encrypted DEK only | Generate and store DEK for later use |
GenerateRandom | Up to 1024 bytes | Random bytes | Cryptographically secure random data |
DescribeKey | N/A | Key metadata | Inspect key state and metadata |
ListKeys / ListAliases | N/A | List | Enumerate keys |
4 KB limit is critical for the exam. Direct
Encryptonly handles up to 4 KB. For anything larger, you must use envelope encryption withGenerateDataKey.
Envelope Encryption (Deep Dive)
Envelope encryption solves the 4 KB limit by using KMS to protect a Data Encryption Key (DEK), then using the DEK locally to encrypt arbitrarily large data.
Envelope Encryption in Code
1import { KMSClient, GenerateDataKeyCommand, DecryptCommand } from '@aws-sdk/client-kms';
2import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
3
4const kms = new KMSClient({ region: 'us-east-1' });
5const KEY_ID = 'arn:aws:kms:us-east-1:123456789012:key/mrk-abc123';
6
7// ─── ENCRYPT ─────────────────────────────────────────────────────────────────
8async function encryptLargeData(plaintext) {
9 // 1. Ask KMS for a data key (symmetric AES-256)
10 const { Plaintext: dek, CiphertextBlob: encryptedDek } = await kms.send(
11 new GenerateDataKeyCommand({ KeyId: KEY_ID, KeySpec: 'AES_256' })
12 );
13
14 // 2. Encrypt the data locally with the plaintext DEK
15 const iv = randomBytes(12); // 96-bit IV for GCM
16 const cipher = createCipheriv('aes-256-gcm', dek, iv);
17 const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
18 const authTag = cipher.getAuthTag();
19
20 // 3. Immediately discard the plaintext DEK
21 dek.fill(0);
22
23 // 4. Store: encrypted payload + encrypted DEK + IV + authTag
24 return {
25 encryptedData: encrypted.toString('base64'),
26 encryptedDek: Buffer.from(encryptedDek).toString('base64'),
27 iv: iv.toString('base64'),
28 authTag: authTag.toString('base64'),
29 };
30}
31
32// ─── DECRYPT ─────────────────────────────────────────────────────────────────
33async function decryptLargeData({ encryptedData, encryptedDek, iv, authTag }) {
34 // 1. Ask KMS to decrypt the encrypted DEK
35 const { Plaintext: dek } = await kms.send(
36 new DecryptCommand({
37 CiphertextBlob: Buffer.from(encryptedDek, 'base64'),
38 KeyId: KEY_ID, // optional but recommended for extra safety
39 })
40 );
41
42 // 2. Decrypt the data locally
43 const decipher = createDecipheriv(
44 'aes-256-gcm',
45 dek,
46 Buffer.from(iv, 'base64')
47 );
48 decipher.setAuthTag(Buffer.from(authTag, 'base64'));
49 const decrypted = Buffer.concat([
50 decipher.update(Buffer.from(encryptedData, 'base64')),
51 decipher.final(),
52 ]);
53
54 dek.fill(0); // zero out plaintext DEK
55 return decrypted.toString();
56}Data Key Caching
Calling GenerateDataKey for every operation is expensive (cost + latency). The AWS Encryption SDK supports data key caching to reuse a DEK across multiple operations.
1import {
2 KmsKeyringNode,
3 buildClient,
4 CommitmentPolicy,
5 LocalCryptographicMaterialsCache,
6 CachingMaterialsManager,
7} from '@aws-crypto/client-node';
8
9const { encrypt, decrypt } = buildClient(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT);
10
11const keyring = new KmsKeyringNode({ generatorKeyId: KEY_ID });
12
13// Cache: reuse DEK for up to 60 seconds or 100 messages, whichever comes first
14const cache = new LocalCryptographicMaterialsCache(100);
15const cachingCMM = new CachingMaterialsManager({
16 backingMaterialsManager: keyring,
17 cache,
18 maxAge: 60_000, // 60 seconds
19 maxMessagesEncrypted: 100,
20});
21
22const { result } = await encrypt(cachingCMM, 'Hello, cached world!');| Cache limit | Meaning |
|---|---|
maxAge | DEK expires after this many milliseconds |
maxMessagesEncrypted | DEK is retired after encrypting N messages |
maxBytesEncrypted | DEK is retired after encrypting N bytes |
KMS Key Policies
Key policies are resource-based policies attached to the KMS key. Unlike most AWS resources, IAM policies alone are not sufficient — the key policy must explicitly allow access.
1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Sid": "Enable IAM policies",
6 "Effect": "Allow",
7 "Principal": { "AWS": "arn:aws:iam::123456789012:root" },
8 "Action": "kms:*",
9 "Resource": "*"
10 },
11 {
12 "Sid": "Allow app role to use the key",
13 "Effect": "Allow",
14 "Principal": { "AWS": "arn:aws:iam::123456789012:role/AppRole" },
15 "Action": ["kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey"],
16 "Resource": "*"
17 },
18 {
19 "Sid": "Allow key admin",
20 "Effect": "Allow",
21 "Principal": { "AWS": "arn:aws:iam::123456789012:role/KeyAdminRole" },
22 "Action": [
23 "kms:Create*", "kms:Describe*", "kms:Enable*", "kms:List*",
24 "kms:Put*", "kms:Update*", "kms:Revoke*", "kms:Disable*",
25 "kms:Get*", "kms:Delete*", "kms:TagResource", "kms:UntagResource",
26 "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion"
27 ],
28 "Resource": "*"
29 }
30 ]
31}Critical rule: The first statement (Enable IAM policies) delegates control to IAM in the account. Without this, no IAM policy in the account can grant KMS access for this key. If you accidentally remove it, you must contact AWS Support to regain access.
Cross-Account Key Access
Both the key policy in Account A (must allow Account B) AND an IAM policy in Account B (must explicitly allow the KMS action) are required. Neither alone is sufficient.
KMS Grants
Grants are an alternative to key policies for programmatic, temporary, delegated access. Useful when a service needs to use a key on behalf of a user without modifying the key policy.
1# Create a grant — allow a Lambda execution role to decrypt
2aws kms create-grant --key-id mrk-abc123 --grantee-principal arn:aws:iam::123456789012:role/LambdaRole --operations Decrypt GenerateDataKey --name "lambda-access-grant"
3
4# List grants on a key
5aws kms list-grants --key-id mrk-abc123
6
7# Retire a grant (by the grantee)
8aws kms retire-grant --grant-token <token>
9
10# Revoke a grant (by the key admin)
11aws kms revoke-grant --key-id mrk-abc123 --grant-id <id>| Key Policy | Grant | |
|---|---|---|
| Who modifies | Key admin | Any principal with kms:CreateGrant |
| Persistence | Permanent until changed | Revocable / retirable |
| Cross-account | Yes | Yes |
| Use case | Static access | Service delegation, temporary access |
Key Rotation
| Key Type | Rotation Type | Frequency | Old key material |
|---|---|---|---|
| AWS Managed | Automatic | Every year | Retained to decrypt old ciphertext |
| CMK (AWS_KMS origin) | Automatic (if enabled) | Every year | Retained |
| CMK (AWS_KMS origin) | On-demand | Anytime | Retained |
| External (BYOK) | Manual | You decide | You manage old versions |
| CloudHSM-backed | Manual | You decide | You manage |
1# Enable automatic annual rotation for a CMK
2aws kms enable-key-rotation --key-id mrk-abc123
3
4# Rotate immediately (on-demand) — no downtime, old versions retained
5aws kms rotate-key-on-demand --key-id mrk-abc123
6
7# Check rotation status
8aws kms get-key-rotation-status --key-id mrk-abc123Key insight: Rotation creates a new backing key (new cryptographic material) but the Key ARN stays the same. Ciphertext encrypted with old backing keys is automatically decrypted using the retained old key material. No re-encryption of existing data needed.
Multi-Region Keys (MRK)
Multi-Region Keys are interoperable KMS keys that can be replicated across AWS regions. Ciphertext encrypted in one region can be decrypted in another without re-encryption via KMS.
1# Create a primary MRK
2aws kms create-key --multi-region --description "Primary MRK for global app" --region us-east-1
3
4# Replicate to another region
5aws kms replicate-key --key-id arn:aws:kms:us-east-1:123456789012:key/mrk-abc123 --replica-region eu-west-1When to use MRKs:
- Disaster recovery: decrypt data in a backup region without additional KMS calls
- Global applications: low-latency encryption/decryption in the user's region
- Active-active multi-region architectures with DynamoDB Global Tables
S3 Encryption Options
| Method | Key managed by | KMS API call per object | Header |
|---|---|---|---|
| SSE-S3 | S3 (AES-256) | No | x-amz-server-side-encryption: AES256 |
| SSE-KMS | KMS CMK | Yes (GenerateDataKey) | x-amz-server-side-encryption: aws:kms |
| DSSE-KMS | KMS CMK | Yes (dual-layer) | x-amz-server-side-encryption: aws:kms:dsse |
| SSE-C | You (per request) | No | x-amz-server-side-encryption-customer-key |
| Client-side | You (before upload) | Optional | None |
1# Upload object with SSE-KMS
2aws s3 cp secret.txt s3://my-bucket/secret.txt --sse aws:kms --sse-kms-key-id alias/my-key
3
4# Set default bucket encryption to SSE-KMS
5aws s3api put-bucket-encryption --bucket my-bucket --server-side-encryption-configuration '{
6 "Rules": [{
7 "ApplyServerSideEncryptionByDefault": {
8 "SSEAlgorithm": "aws:kms",
9 "KMSMasterKeyID": "alias/my-key"
10 },
11 "BucketKeyEnabled": true
12 }]
13 }'S3 Bucket Key: Reduces KMS API calls by generating a short-lived bucket-level DEK. A single GenerateDataKey call serves thousands of S3 objects instead of one call per object. Reduces KMS costs by ~99% for high-volume buckets.
KMS with AWS Services
| Service | How KMS integrates | Key option |
|---|---|---|
| S3 | SSE-KMS per object or bucket default | CMK or AWS Managed (aws/s3) |
| EBS | Volume encryption at rest | CMK or AWS Managed (aws/ebs) |
| RDS | Encryption at rest on instance creation | CMK or AWS Managed (aws/rds) |
| Lambda | Encrypt environment variables | CMK |
| Secrets Manager | Encrypt secret value | CMK (default: aws/secretsmanager) |
| CloudTrail | Encrypt log files in S3 | CMK |
| SSM Parameter Store | SecureString parameters | CMK or AWS Managed (aws/ssm) |
| SNS | Encrypt messages at rest | CMK |
| SQS | Encrypt messages at rest (SSE-SQS or SSE-KMS) | CMK or AWS Managed (aws/sqs) |
1// Lambda environment variable encryption with CMK
2import { KMSClient, DecryptCommand } from '@aws-sdk/client-kms';
3
4const kms = new KMSClient({});
5
6// Lambda auto-decrypts env vars with the key at startup.
7// For additional client-side decryption (when using your own CMK):
8async function getDecryptedSecret() {
9 const { Plaintext } = await kms.send(new DecryptCommand({
10 CiphertextBlob: Buffer.from(process.env.ENCRYPTED_API_KEY, 'base64'),
11 }));
12 return Buffer.from(Plaintext).toString('utf-8');
13}Asymmetric Keys
KMS supports asymmetric key pairs for signing/verification and public-key encryption.
| Key Spec | Algorithm | Use case |
|---|---|---|
RSA_2048 / RSA_4096 | RSA-OAEP | Asymmetric encryption |
ECC_NIST_P256 / ECC_NIST_P384 | ECDSA | Digital signing |
RSA_2048 (signing) | RSASSA-PSS / PKCS1v15 | Document/JWT signing |
SM2 | SM2 | China regions only |
1import { KMSClient, SignCommand, VerifyCommand } from '@aws-sdk/client-kms';
2import { createHash } from 'crypto';
3
4const kms = new KMSClient({});
5const SIGNING_KEY_ID = 'alias/my-signing-key';
6
7// Sign a message
8async function signPayload(message) {
9 const messageHash = createHash('sha256').update(message).digest();
10
11 const { Signature } = await kms.send(new SignCommand({
12 KeyId: SIGNING_KEY_ID,
13 Message: messageHash,
14 MessageType: 'DIGEST',
15 SigningAlgorithm: 'ECDSA_SHA_256',
16 }));
17 return Buffer.from(Signature).toString('base64');
18}
19
20// Verify signature (can use public key locally — no KMS call needed)
21async function verifySignature(message, signature) {
22 const messageHash = createHash('sha256').update(message).digest();
23
24 const { SignatureValid } = await kms.send(new VerifyCommand({
25 KeyId: SIGNING_KEY_ID,
26 Message: messageHash,
27 MessageType: 'DIGEST',
28 Signature: Buffer.from(signature, 'base64'),
29 SigningAlgorithm: 'ECDSA_SHA_256',
30 }));
31 return SignatureValid;
32}The public key can be downloaded from KMS (
GetPublicKey) and distributed freely. Signature verification can happen locally without any KMS API call — only signing requires KMS.
Key States
| State | Encrypt | Decrypt | Key policy changes |
|---|---|---|---|
| Enabled | ✅ | ✅ | ✅ |
| Disabled | ❌ | ❌ | ✅ |
| Pending Deletion | ❌ | ❌ | ❌ |
| Pending Import | ❌ | ❌ | ✅ |
Scheduled deletion window: 7 days minimum, 30 days maximum. During this window you can cancel deletion. After the window passes, the key is permanently and irrecoverably deleted.
AWS CloudHSM
CloudHSM provides dedicated, single-tenant Hardware Security Modules in your VPC. Unlike KMS (shared, multi-tenant HSMs), CloudHSM gives you exclusive access to the hardware.
| KMS | CloudHSM | |
|---|---|---|
| Hardware | Shared (multi-tenant) | Dedicated (single-tenant) |
| Compliance | FIPS 140-2 Level 2 | FIPS 140-2 Level 3 |
| Key management | AWS shares responsibility | You have full control |
| Cost | Per-request | Per HSM per hour (~$1.60/hr) |
| Integration | Native AWS service integration | Manual SDK integration (PKCS#11, JCE) |
| High availability | Built-in | You must create a cluster (min 2 HSMs) |
| Root of Trust for KMS | Optional | Not applicable |
When to use CloudHSM: Regulatory requirement for dedicated hardware, FIPS 140-2 Level 3, custom cryptographic operations, or when you need to be the sole owner of the HSM.
Key Policy + Grant + IAM Evaluation Logic
Rule: For KMS, both the key policy AND (IAM policy OR grant) must allow. An explicit deny anywhere always wins.
DVA-C02 Quick Reference
| Topic | Key Fact |
|---|---|
| Direct KMS Encrypt limit | 4 KB maximum |
| Envelope encryption why | KMS 4 KB limit → use DEK for large data |
| GenerateDataKey returns | Plaintext DEK + Encrypted DEK |
| GenerateDataKeyWithoutPlaintext | Returns encrypted DEK only (no plaintext) |
| CMK cost | $1/month per key + $0.03 per 10,000 API calls |
| AWS Managed Key cost | Free |
| Automatic rotation frequency | Annually (CMK optional, AWS Managed automatic) |
| Rotation effect on ARN | ARN unchanged — new backing key created |
| Key policy root statement | Required — enables IAM delegation; without it, no IAM policy works |
| Cross-account access requires | Key policy in owner account AND IAM policy in user account |
| Grants vs key policies | Grants are programmatic, revocable — policies are static |
| S3 Bucket Key benefit | Reduces KMS API calls by up to 99% |
| SSE-KMS API call | GenerateDataKey per S3 object (without Bucket Key) |
| SSE-S3 vs SSE-KMS | SSE-S3: no audit trail; SSE-KMS: CloudTrail logs every decryption |
| Multi-Region Key prefix | mrk- prefix on Key ID |
| MRK benefit | Encrypt in one region, decrypt in another without re-encryption |
| Key deletion minimum window | 7 days (max 30 days) |
| Key permanently deleted | Cannot be recovered — all encrypted data permanently inaccessible |
| CloudHSM FIPS level | FIPS 140-2 Level 3 (vs KMS Level 2) |
| CloudHSM vs KMS | CloudHSM: dedicated hardware, you manage; KMS: managed service |
| KMS + CloudTrail | Every KMS API call logged — who used what key, when |
| Asymmetric: verify without KMS | Download public key with GetPublicKey — verify locally |
Practice Questions5
Q1. A developer needs to encrypt a 5 MB file using AWS KMS. The KMS Encrypt API rejects the request. What is the correct approach for encrypting large data?
Select one answer before revealing.
Q2. A company requires that all KMS key operations be logged and that only specific IAM roles can perform kms:Decrypt. The developer needs to ensure no other principal can use the key, even AWS account root. Which KMS feature enforces this?
Select one answer before revealing.
Q3. A Lambda function calls kms:GenerateDataKey on every invocation to encrypt user data. This is causing KMS API throttling (429 errors). How should the developer reduce KMS API calls without sacrificing security?
Select one answer before revealing.
Q4. A developer stores a Lambda function's database password in an environment variable encrypted with KMS. The function needs to decrypt the password at startup. Which KMS API should be called?
Select one answer before revealing.
Q5. A company has a KMS key used to encrypt S3 objects. They want to allow a Lambda in account B to decrypt these objects, but the KMS key is in account A. What configuration is required?
Select one answer before revealing.