AWS KMS โ Key Management Service
Core concept: KMS manages encryption keys. AWS services use KMS internally (S3 SSE-KMS, RDS, EBS). You can also use KMS directly in your apps.
Key Typesโ
| Type | Description | Rotation | Use |
|---|---|---|---|
| AWS Managed Key | Auto-created when you enable encryption in a service | Every 1 year (automatic) | Service-default encryption |
| Customer Managed Key (CMK) | You create and control | You configure (annual or manual) | Full control, cross-account, custom policies |
| AWS-Owned Key | Shared across customers, not visible | AWS managed | Free, no audit trail |
CMK Key Materialโ
| Source | Description |
|---|---|
| KMS (default) | AWS generates and stores key material |
| External (BYOK) | You import your own key material |
| CloudHSM | Key material stored in a hardware security module |
Envelope Encryptionโ
KMS directly encrypts data up to 4KB. For larger data, use envelope encryption:
Your Plaintext Data (large)
โ
โผ
Generate Data Key (DEK)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Plaintext DEK โ encrypt data โ
โ Encrypted DEK โ stored with data โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
Store: Encrypted Data + Encrypted DEK
To Decrypt:
KMS decrypts the Encrypted DEK โ Plaintext DEK โ decrypts data
KmsClient kms = KmsClient.create();
// Generate a data key
GenerateDataKeyResponse keyResponse = kms.generateDataKey(
GenerateDataKeyRequest.builder()
.keyId("arn:aws:kms:us-east-1:123456789012:key/my-key-id")
.keySpec(DataKeySpec.AES_256)
.build());
byte[] plaintextKey = keyResponse.plaintext().asByteArray(); // Encrypt data locally
byte[] encryptedKey = keyResponse.ciphertextBlob().asByteArray(); // Store alongside ciphertext
// Decrypt data key later
DecryptResponse decryptResponse = kms.decrypt(
DecryptRequest.builder()
.ciphertextBlob(SdkBytes.fromByteArray(encryptedKey))
.build());
KMS API Operationsโ
| API | Description |
|---|---|
Encrypt | Encrypt up to 4KB directly |
Decrypt | Decrypt ciphertext |
GenerateDataKey | Get plaintext + encrypted DEK |
GenerateDataKeyWithoutPlaintext | Get only encrypted DEK (encrypt later) |
ReEncrypt | Re-encrypt under a different key (plaintext never leaves KMS) |
DescribeKey | Get key metadata |
Key Policiesโ
Every CMK has a resource-based policy. Without a key policy that grants access, IAM policies alone won't work:
{
"Sid": "Allow Lambda role to use this key",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:role/LambdaRole" },
"Action": ["kms:Decrypt", "kms:GenerateDataKey"],
"Resource": "*"
}
KMS Limits (API Throttling!)โ
| API | Default TPS |
|---|---|
| Encrypt / Decrypt | 5,500 โ 30,000/s (region-dependent) |
| GenerateDataKey | Same |
If your Lambda is called 10,000 times/second and each call does kms:Decrypt, you'll hit KMS throttling.
Fix: Use Data Key Caching (with AWS Encryption SDK) โ cache the plaintext DEK in memory, reducing KMS calls.
๐งช Practice Questionsโ
Q1. A Lambda function needs to encrypt a 50MB file before storing it in S3. Which approach should the developer use?
A) Call kms:Encrypt with the 50MB file
B) Use Envelope Encryption โ generate a data key and encrypt locally
C) Use S3 SSE-S3
D) Call kms:GenerateDataKey 50 times
โ Answer & Explanation
B โ KMS can only encrypt data up to 4KB directly. For larger data, use envelope encryption: GenerateDataKey โ encrypt data locally with the plaintext DEK โ store encrypted DEK alongside ciphertext โ delete plaintext DEK from memory.
Q2. A developer wants to re-encrypt S3 objects that were encrypted with Key A, using Key B instead, without ever exposing the plaintext. Which KMS API should they use?
A) Decrypt โ Encrypt
B) ReEncrypt
C) GenerateDataKey
D) RotateKey
โ Answer & Explanation
B โ ReEncrypt performs a server-side re-encryption entirely within KMS. The plaintext never leaves KMS and never appears in your code.