AWS KMS โ Key Management Service
Core concept: KMS manages encryption keys for AWS services (S3, EBS, RDS) and your applications. Understanding envelope encryption and key policies is critical for the exam.
๐ฐ What Is KMS?โ
KMS is a managed encryption key service. Think of it as a secure vault where AWS keeps your master keys. You never see the actual key material โ you just tell KMS to encrypt/decrypt data, and it does it inside FIPS 140-2 validated hardware.
Key Typesโ
| Type | Description | Rotation | Cost | Audit |
|---|---|---|---|---|
| AWS Managed Key | Auto-created per service (e.g., aws/s3) | Annual (automatic) | Free | CloudTrail |
| Customer Managed Key (CMK) | You create and manage | Configurable | $1/month + API calls | CloudTrail |
| AWS Owned Key | Shared across customers | AWS managed | Free | โ No |
Key Material Originsโ
| Source | Description | Use Case |
|---|---|---|
| KMS (default) | AWS generates and stores | Most common |
| External (BYOK) | You import your own key | Regulatory requirements |
| CloudHSM | Stored in your HSM cluster | Full control, FIPS 140-2 Level 3 |
| External Key Store | Key material in external HSM | Data sovereignty |
Envelope Encryption (Critical Concept!)โ
KMS can directly encrypt only 4 KB of data. For larger data, use envelope encryption:
ENCRYPTION:
1. Call KMS: GenerateDataKey(CMK-ID)
2. KMS returns:
- Plaintext Data Key (DEK)
- Encrypted Data Key (encrypted with CMK)
3. Use Plaintext DEK to encrypt your data locally
4. Store: Encrypted Data + Encrypted DEK
5. Delete Plaintext DEK from memory!
DECRYPTION:
1. Call KMS: Decrypt(Encrypted DEK)
2. KMS returns: Plaintext DEK
3. Use Plaintext DEK to decrypt your data locally
KmsClient kms = KmsClient.create();
// Generate data key
GenerateDataKeyResponse keyResponse = kms.generateDataKey(
GenerateDataKeyRequest.builder()
.keyId("arn:aws:kms:us-east-1:123:key/my-key-id")
.keySpec(DataKeySpec.AES_256)
.build());
byte[] plaintextKey = keyResponse.plaintext().asByteArray(); // Use to encrypt data
byte[] encryptedKey = keyResponse.ciphertextBlob().asByteArray(); // Store with ciphertext
// Encrypt data locally with plaintextKey using AES-256
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(plaintextKey, "AES"));
byte[] encryptedData = cipher.doFinal(plainData);
// IMPORTANT: Clear plaintext key from memory
Arrays.fill(plaintextKey, (byte) 0);
// Later: Decrypt the data key
DecryptResponse decryptResponse = kms.decrypt(
DecryptRequest.builder()
.ciphertextBlob(SdkBytes.fromByteArray(encryptedKey))
.build());
byte[] decryptedKey = decryptResponse.plaintext().asByteArray();
GenerateDataKey vs GenerateDataKeyWithoutPlaintextโ
| API | Returns | Use Case |
|---|---|---|
GenerateDataKey | Plaintext DEK + Encrypted DEK | Immediate encryption |
GenerateDataKeyWithoutPlaintext | Only Encrypted DEK | Pre-generate keys for later use |
KMS API Operationsโ
| API | Description | Max Data |
|---|---|---|
Encrypt | Encrypt data directly | 4 KB |
Decrypt | Decrypt ciphertext | N/A |
GenerateDataKey | Get plaintext + encrypted DEK | N/A |
GenerateDataKeyWithoutPlaintext | Get only encrypted DEK | N/A |
ReEncrypt | Re-encrypt under different key (plaintext never leaves KMS) | 4 KB |
CreateKey | Create a new CMK | N/A |
DescribeKey | Get key metadata | N/A |
EnableKeyRotation | Enable automatic annual rotation | N/A |
Key Policiesโ
Every CMK has a key policy (resource-based). Without a key policy granting access, even IAM admin policies won't work:
Default Key Policyโ
{
"Sid": "Enable IAM policies",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "kms:*",
"Resource": "*"
}
This default policy allows IAM policies to grant KMS access. Without it, NO ONE can use the key.
Custom Key Policyโ
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow Lambda to decrypt",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:role/LambdaRole" },
"Action": ["kms:Decrypt", "kms:GenerateDataKey"],
"Resource": "*"
},
{
"Sid": "Allow key admin",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:user/KeyAdmin" },
"Action": ["kms:Create*", "kms:Describe*", "kms:Enable*", "kms:List*",
"kms:Put*", "kms:Update*", "kms:Revoke*", "kms:Disable*",
"kms:Get*", "kms:Delete*", "kms:ScheduleKeyDeletion"],
"Resource": "*"
}
]
}
KMS Grantsโ
Grants provide temporary, programmatic access to a KMS key:
kms.createGrant(CreateGrantRequest.builder()
.keyId("arn:aws:kms:us-east-1:123:key/my-key")
.granteePrincipal("arn:aws:iam::123456789012:role/TempRole")
.operations(GrantOperation.ENCRYPT, GrantOperation.DECRYPT)
.build());
Use grants when you need to delegate KMS access dynamically (e.g., during EBS volume attachment).
Key Rotationโ
| Rotation Type | CMK | AWS Managed |
|---|---|---|
| Automatic | Annual (opt-in) | Annual (always on) |
| Manual | Create new key, update alias | N/A |
| Backing key | Old versions kept for decryption | Same |
# Enable automatic rotation
aws kms enable-key-rotation --key-id my-key-id
# Check rotation status
aws kms get-key-rotation-status --key-id my-key-id
KMS keeps ALL previous key versions (backing keys). Old ciphertext can still be decrypted. Only new encryptions use the new key version. You don't need to re-encrypt existing data.
Multi-Region Keysโ
Primary Key (us-east-1) โโ Replica Key (eu-west-1)
Same key ID, same key material
Encrypt in us-east-1 โ Decrypt in eu-west-1 (no cross-region API call!)
Use cases: Global DynamoDB tables, cross-region S3 replication, disaster recovery
KMS with AWS Servicesโ
| Service | Encryption | Key Type |
|---|---|---|
| S3 | SSE-KMS | AWS Managed or CMK |
| EBS | Volume encryption | AWS Managed or CMK |
| RDS | At-rest encryption | AWS Managed or CMK |
| DynamoDB | At-rest encryption | AWS Owned, AWS Managed, or CMK |
| Lambda | Env variable encryption | AWS Managed or CMK |
| Secrets Manager | Secret encryption | AWS Managed or CMK |
KMS API Throttlingโ
| Limit | Value |
|---|---|
| Encrypt/Decrypt | 5,500โ30,000 RPS (region-dependent) |
| GenerateDataKey | Same as above |
| Shared quota | All cryptographic operations share the quota |
If Lambda invoked 10,000 times/sec, each calling kms:Decrypt:
- Problem: Hits KMS throttling โ
ThrottlingException - Fix 1: Use Data Key Caching (AWS Encryption SDK) โ cache plaintext DEK in memory
- Fix 2: Use S3 Bucket Keys (reduces per-object KMS calls by 99%)
- Fix 3: Request KMS quota increase
๐ Best Practicesโ
- Use CMKs for sensitive data (full control, audit trail)
- Enable automatic rotation for all CMKs
- Separate key admin from key usage in key policies
- Use grants for temporary access (revoke when done)
- Cache data keys to reduce KMS API calls
- Use S3 Bucket Keys with SSE-KMS to avoid throttling
- Never expose plaintext DEK โ delete from memory after use
๐ฏ DVA-C02 Exam Tipsโ
- Envelope encryption = for data >4KB. GenerateDataKey โ encrypt locally
- ReEncrypt = re-encrypt without exposing plaintext (server-side only)
- Key policy required โ IAM policies alone can't grant KMS access without it
- Automatic rotation keeps old backing keys (no re-encryption needed)
- Multi-region keys = same key material in multiple regions
- ThrottlingException โ cache data keys or use S3 Bucket Keys
- BYOK (imported keys) cannot be auto-rotated
- KMS + CloudTrail = every Encrypt/Decrypt call is logged
- SSE-KMS adds audit trail but costs per-API call
- Grants = temporary programmatic KMS access
๐งช Practice Questionsโ
Q1. Lambda needs to encrypt a 50MB file. Which approach?
A) Call kms:Encrypt with 50MB
B) Envelope encryption โ GenerateDataKey + encrypt locally
C) SSE-S3
D) GenerateDataKey 50 times
โ Answer & Explanation
B โ KMS encrypts max 4KB directly. Use envelope encryption: generate a data key, encrypt data locally, store encrypted DEK alongside ciphertext.
Q2. Re-encrypt S3 objects from Key A to Key B without exposing plaintext. Which API?
A) Decrypt โ Encrypt
B) ReEncrypt
C) GenerateDataKey
D) RotateKey
โ Answer & Explanation
B โ ReEncrypt performs server-side re-encryption entirely within KMS. Plaintext never leaves KMS.
Q3. High-throughput Lambda with SSE-KMS gets ThrottlingException. Best fix?
A) Switch to SSE-S3
B) Enable S3 Bucket Keys
C) Increase Lambda memory
D) Use larger KMS key
โ Answer & Explanation
B โ S3 Bucket Keys generate a bucket-level key that derives per-object keys, reducing KMS API calls by ~99%.
Q4. A CMK has automatic rotation enabled. What happens to data encrypted with the old key version?
A) Data must be re-encrypted manually
B) Old key version is kept โ data decrypts normally
C) Data becomes inaccessible
D) KMS auto re-encrypts all data
โ Answer & Explanation
B โ KMS keeps all previous backing key versions. Old ciphertext references the old key version and decrypts normally. Only new encryptions use the new version.