AWS KMS Fundamentals
KMS (Key Management Service) is AWS’s managed cryptographic service. It stores keys, performs cryptographic operations, and — most importantly — serves as the trust root almost every other AWS service uses for encryption at rest. Understanding KMS is prerequisite to understanding S3, EBS, RDS, Secrets Manager, and any serious compliance posture.
What KMS does
Three distinct things:
- Stores cryptographic keys in FIPS 140-2 validated HSMs, never exported
- Performs operations with those keys (encrypt, decrypt, sign, verify, generate data key)
- Integrates with other AWS services so they can encrypt customer data without custom crypto code
The keys themselves never leave KMS. Applications call the KMS API; KMS does the work.
KMS Keys (the canonical name)
What used to be called “CMKs” (Customer Master Keys) are now called KMS keys. Three types by ownership:
| Type | Who owns / manages | Typical use |
|---|---|---|
| AWS-owned | AWS fully manages; invisible to you | Default encryption on services where you don’t configure a key (e.g. DynamoDB default) |
| AWS-managed | AWS creates in your account, you can see them but not modify | Default for service-specific keys (aws/s3, aws/ebs, etc.) |
| Customer-managed (CMK) | You create; you control policy, rotation, deletion | Production workloads, compliance, cross-account sharing |
Practical rule: use customer-managed keys for anything you care about. AWS-managed keys are fine for quick starts but offer less control (can’t grant cross-account access, can’t set custom rotation, limited visibility in CloudTrail).
Key spec / origin / usage
- Key spec — symmetric (
SYMMETRIC_DEFAULT, AES-256-GCM) or asymmetric (RSA, ECC, SM2) - Key usage —
ENCRYPT_DECRYPTorSIGN_VERIFY(asymmetric can also beKEY_AGREEMENT) - Origin —
AWS_KMS(KMS generates),EXTERNAL(BYOK — you import material),AWS_CLOUDHSM(backed by CloudHSM cluster),EXTERNAL_KEY_STORE(XKS — external HSM)
Most real-world use: symmetric, AWS-origin, customer-managed.
Envelope encryption — the pattern to internalise
KMS does not encrypt arbitrary large payloads directly (4 KB limit per call). Instead, every service uses envelope encryption:
1. App/service calls GenerateDataKey(KMS key)
↓
2. KMS returns two things:
- plaintext data key (AES-256 key)
- encrypted data key (the key, wrapped by the KMS key)
↓
3. App encrypts the payload with the plaintext data key (AES-GCM)
↓
4. App stores: ciphertext + encrypted data key
↓
5. App forgets the plaintext data key
To decrypt later:
- Call
Decrypt(encrypted data key)→ KMS returns plaintext data key - Use the plaintext data key to decrypt the payload
- Forget the plaintext data key again
Benefits:
- The large payload never leaves your infra
- The KMS call is small and cheap
- KMS key never touches plaintext data
- Auditable: every key-unwrap is logged in CloudTrail
Every SSE-KMS-encrypted S3 object, EBS volume, RDS volume, Secrets Manager secret, etc. is using this pattern under the hood.
Key policies — who can use the key
Unlike most AWS resources, KMS keys use resource-based policy (the key policy) as the primary access control, not IAM alone. The key policy is required — a key without one is unusable, including by its creator.
The typical pattern:
{
"Statement": [
{
"Sid": "EnableRootAccess",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::<account>:root" },
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "KeyAdmins",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::<account>:role/KeyAdmin" },
"Action": ["kms:Create*", "kms:Describe*", "kms:Put*", "kms:Tag*", ...],
"Resource": "*"
},
{
"Sid": "KeyUsers",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::<account>:role/AppRole" },
"Action": ["kms:Encrypt","kms:Decrypt","kms:GenerateDataKey*","kms:DescribeKey"],
"Resource": "*"
}
]
}Key insight: the root statement delegates access control to IAM for that account. Without it, only the principals explicitly named in the key policy can use the key — dangerous if you lose your admin principal.
Cross-account access
Allow the other account’s principal in the key policy, and that other account must grant its own principals IAM permission to use the key. Both sides must say yes.
Grants — scoped, temporary delegation
A grant is a narrow, time-limited permission attached to a key. Typically used by AWS services that need temporary access on your behalf (e.g. RDS creating a snapshot). Grants can be revoked independently without editing the key policy. Prefer grants over broad key-policy additions for service integrations.
Rotation
Automatic rotation (customer-managed symmetric keys)
- Enable automatic annual rotation → KMS rotates the backing key material yearly
- The key ID stays the same; old key material is retained so existing ciphertext still decrypts
- Transparent to apps
- Recommended for any long-lived customer-managed key
Manual rotation
- Create a new key, update aliases/configuration to point at the new key
- Re-encrypt old data (optionally) via
ReEncrypt— decrypts with old key, encrypts with new, all inside KMS (plaintext never emitted)
Asymmetric keys
- Automatic rotation not supported on asymmetric keys (would break signature verification)
- Must roll keys manually
Aliases
An alias is a friendly name → key-ID pointer. alias/prod-data. Rotate the underlying key by repointing the alias. Referenced in code/config instead of raw key IDs.
Services reference aliases (aws/s3, alias/my-key). You reference aliases. One key can have many aliases; each alias points to exactly one key.
Cost model
- **0.0014)
- Usage: $0.03 per 10,000 requests — cheap for normal use, can add up in high-QPS decrypt paths
- AWS-managed keys — free
- Symmetric vs asymmetric — asymmetric costs more per request and per key
At high request rates (data-key wrap/unwrap in tight loops), consider data-key caching — reuse a plaintext data key for multiple encrypt operations over a short window. The AWS Encryption SDK supports this.
Deletion — the scheduled-delete safety
KMS keys cannot be deleted immediately. You schedule deletion with a 7-30 day waiting period (30 default). During that window the key is “pending deletion” but can still decrypt — you cancel deletion if you need to restore. After the window, the key material is destroyed and all data encrypted with it is cryptographically unrecoverable.
Audit before scheduling deletion. Once the window passes, recovery is impossible.
Multi-Region keys
A multi-Region key is a key with the same key material replicated across regions. Ciphertext encrypted in region A can be decrypted in region B by the replica key — useful for multi-region DR, global applications. Policy, aliases, rotation are managed independently per region.
Don’t confuse with KMS grants across regions (different thing).
What “encryption at rest” actually means on AWS
When you tick “encrypt this volume” / “SSE-KMS this bucket”:
- Service calls
GenerateDataKeyagainst your CMK - Service gets
(plaintextDataKey, encryptedDataKey) - Service encrypts your data with
plaintextDataKey - Service stores
encryptedDataKeyalongside the ciphertext - On read, service calls
Decrypt(encryptedDataKey)to get backplaintextDataKey - Service decrypts the data
Your bill for KMS here is one Decrypt per S3 object / one per EBS volume mount / etc.
Integration highlights
| Service | KMS usage |
|---|---|
| S3 | SSE-KMS for server-side encryption; bucket keys reduce Decrypt calls |
| EBS | Volume encryption; snapshots inherit; one decrypt at attach time |
| RDS / Aurora | Storage + snapshot + backup encryption |
| Secrets Manager / Parameter Store | Secrets encrypted under a KMS key; choose which key |
| SSM Session Manager logs | KMS-encrypted |
| SNS/SQS | Message-level encryption via KMS |
| DynamoDB | Tables encrypted; choose AWS-owned / AWS-managed / customer-managed |
Common pitfalls
- Deleting the “root” statement from a key policy → locks you out. Don’t edit the default statement without a thorough review.
- Cross-account access requires both sides. Key policy on the owner side, IAM policy on the caller side.
- Using AWS-managed keys when you need cross-account — AWS-managed keys can’t be shared. Use customer-managed.
- Forgetting
kms:*-level actions in policies —kms:GenerateDataKey*(note the wildcard) is often required alongsidekms:Decrypt. - Throttling at high QPS — KMS has per-region quotas. Enable S3 Bucket Keys to dramatically reduce calls for high-throughput S3 + SSE-KMS workloads.
- Asymmetric key confusion — asymmetric keys split into
ENCRYPT_DECRYPT,SIGN_VERIFY,KEY_AGREEMENT. Can’t mix. ViaServicecondition — powerful IAM/Key-policy condition restricting a key’s use to a specific AWS service. Commonly missing in tighter policies.
Mental model
- KMS = trust root. It’s the place where key material lives and where unwrap decisions happen. Everything else is a data plane.
- Envelope encryption = the pattern. Data keys (cheap, fast, disposable) encrypt data; KMS keys (expensive, audited, long-lived) wrap data keys.
- Key policy = the gate. IAM alone isn’t enough; the key policy must also admit you.
- Aliases = indirection. Point them at new keys to rotate without app changes.
- Grants = the service delegation primitive.