Post

AWS KMS — Key Management, Envelope Encryption, and CloudHSM

SCS-C03 Domain 5 — KMS key types, key policies, grants, envelope encryption, imported key material, multi-region keys, CloudHSM, and cross-account access

AWS KMS — Key Management, Envelope Encryption, and CloudHSM

What is AWS KMS?

AWS Key Management Service (KMS) is a managed service for creating, storing, and controlling cryptographic keys used to encrypt your data. KMS integrates natively with over 100 AWS services — S3, EBS, RDS, Secrets Manager, Lambda, SQS, and more. Every encryption operation goes through KMS, giving you centralised control, auditing, and access control over all encryption in your account.


Key Types

Key TypeCreated ByManaged ByUse Case
AWS managed keyAWSAWS (auto-rotated every year)Default encryption in AWS services (e.g. aws/s3, aws/ebs)
Customer managed key (CMK)YouYou (control key policy, rotation)Your own applications, cross-account sharing, fine-grained access
AWS owned keyAWSAWS (not visible to you)Internal AWS service use — you cannot see or control these

Always use customer managed keys (CMKs) when you need:

  • Custom key policies
  • Cross-account access
  • Key deletion control
  • Audit trail of all key usage
  • Key rotation control

Key Material Origin

OriginMeaning
KMS generatedDefault — KMS generates and stores the key material
External (imported)You generate the key material outside AWS and import it into KMS
CloudHSMKey material generated and stored in your CloudHSM cluster (Custom Key Store)

Imported Key Material

Use imported key material when you need to:

  • Maintain a copy of the key material outside AWS (regulatory requirement)
  • Rotate keys on your own schedule with your own material
  • Immediately delete the key material (delete from KMS — the CMK shell remains but becomes unusable)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. Create a CMK with no key material (EXTERNAL origin)
aws kms create-key --origin EXTERNAL --description "Imported key for compliance"

# 2. Get the public key and import token
aws kms get-parameters-for-import \
  --key-id key-id-here \
  --wrapping-algorithm RSAES_OAEP_SHA_256 \
  --wrapping-key-spec RSA_2048

# 3. Encrypt your key material with the wrapping public key (done offline)
# 4. Import the encrypted key material
aws kms import-key-material \
  --key-id key-id-here \
  --encrypted-key-material fileb://encrypted-key.bin \
  --import-token fileb://import-token.bin \
  --expiration-model KEY_MATERIAL_EXPIRES \
  --valid-to 2027-01-01T00:00:00Z

Exam gotcha: You cannot enable automatic key rotation on a key with imported key material — you must manually rotate by importing new material.


Envelope Encryption

Envelope encryption is the pattern AWS services use to encrypt large amounts of data efficiently. KMS keys have a 4KB limit on what they can directly encrypt. For larger data, you use a two-key system.

1
2
3
4
5
6
7
8
9
Step 1: KMS generates a data key (plaintext + encrypted copy)
Step 2: Use the plaintext data key to encrypt your data (locally, fast)
Step 3: Store the encrypted data key alongside the encrypted data
Step 4: Discard the plaintext data key from memory

To decrypt:
Step 1: Call KMS to decrypt the encrypted data key → get plaintext data key
Step 2: Use plaintext data key to decrypt your data locally
Step 3: Discard plaintext data key from memory
1
2
3
4
5
6
7
8
9
10
11
12
13
# Generate a data key
aws kms generate-data-key \
  --key-id arn:aws:kms:eu-west-1:123456789012:key/mrk-abc123 \
  --key-spec AES_256

# Response contains:
# - Plaintext: the raw data key (use to encrypt, then discard)
# - CiphertextBlob: the encrypted data key (store with your data)

# Decrypt the data key (to get plaintext for decryption)
aws kms decrypt \
  --ciphertext-blob fileb://encrypted-data-key.bin \
  --key-id arn:aws:kms:eu-west-1:123456789012:key/mrk-abc123

Key Policies

Every KMS key has a key policy — a resource-based policy that controls who can use and manage the key. Unlike IAM policies, the key policy is the primary access control for KMS. If a key policy does not explicitly allow an action, IAM policies cannot grant it.

Default Key Policy

The default key policy gives the AWS account full control:

1
2
3
4
5
6
7
8
9
10
11
{
  "Statement": [
    {
      "Sid": "Enable IAM User Permissions",
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::123456789012:root"},
      "Action": "kms:*",
      "Resource": "*"
    }
  ]
}

This allows the account root to delegate key permissions via IAM policies. Without this statement, you could lock yourself out of the key.

Key Policy for Cross-Account Access

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "Statement": [
    {
      "Sid": "Allow root account",
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::111122223333:root"},
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "Allow cross-account decrypt",
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::999988887777:role/app-role"},
      "Action": ["kms:Decrypt", "kms:DescribeKey"],
      "Resource": "*"
    }
  ]
}

For cross-account access, the key policy must allow the external account AND the external account’s IAM policy must also allow the KMS action. Both the key policy (resource side) and the IAM policy (identity side) must allow the action.


Grants

Grants are an alternative to key policy statements for programmatic, temporary delegation of key usage. Use grants when an AWS service needs to use your key on behalf of another account or service. Grants can be retired (revoked) without modifying the key policy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create a grant allowing a specific role to decrypt
aws kms create-grant \
  --key-id arn:aws:kms:eu-west-1:123456789012:key/mrk-abc123 \
  --grantee-principal arn:aws:iam::123456789012:role/lambda-execution-role \
  --operations Decrypt GenerateDataKey \
  --name "lambda-decrypt-grant"

# List grants on a key
aws kms list-grants --key-id mrk-abc123

# Retire a grant (revoke access)
aws kms retire-grant \
  --key-id mrk-abc123 \
  --grant-token <token>

Key Rotation

Automatic key rotation rotates the key material every year while keeping the same key ID. Old key material is retained so previously encrypted data can still be decrypted. New data is encrypted with the new key material automatically.

1
2
3
4
5
6
7
8
# Enable automatic annual rotation
aws kms enable-key-rotation --key-id mrk-abc123

# Check rotation status
aws kms get-key-rotation-status --key-id mrk-abc123

# On-demand rotation (rotate now, outside the annual schedule)
aws kms rotate-key-on-demand --key-id mrk-abc123

Cannot rotate: Asymmetric keys, HMAC keys, keys with imported key material, keys in CloudHSM custom key stores.


Multi-Region Keys (MRKs)

Multi-Region Keys are a set of interoperable KMS keys in different regions that share the same key material and key ID prefix (mrk-). Data encrypted with an MRK in one region can be decrypted using the replica in another region — without re-encryption.

1
eu-west-1: mrk-abc123 ──same key material──► us-east-1: mrk-abc123

Use cases:

  • Encrypted data replicated across regions (S3 CRR, Aurora Global Database)
  • Active-active multi-region applications
  • Disaster recovery where you need to decrypt in the failover region immediately
1
2
3
4
5
6
7
8
9
10
11
# Create a primary MRK
aws kms create-key \
  --description "Primary MRK" \
  --multi-region \
  --region eu-west-1

# Replicate to another region
aws kms replicate-key \
  --key-id arn:aws:kms:eu-west-1:123456789012:key/mrk-abc123 \
  --replica-region us-east-1 \
  --region eu-west-1

Key Deletion

Keys cannot be immediately deleted — there is a mandatory waiting period of 7 to 30 days. During the waiting period, the key is disabled and cannot be used. Cancel the deletion during this window if you change your mind.

1
2
3
4
5
6
7
# Schedule deletion (7-day minimum waiting period)
aws kms schedule-key-deletion \
  --key-id mrk-abc123 \
  --pending-window-in-days 7

# Cancel deletion
aws kms cancel-key-deletion --key-id mrk-abc123

Exam tip: If you delete a KMS key, all data encrypted with it is permanently unrecoverable. Always use CloudTrail to audit KMS key usage before deletion.


AWS CloudHSM

CloudHSM is a cloud-based Hardware Security Module (HSM) that provides dedicated, tamper-evident hardware for cryptographic operations.

 KMSCloudHSM
Key storageAWS-managed HSMs (multi-tenant)Your dedicated HSM hardware (single-tenant)
ControlAWS manages the HSMYou manage everything — AWS cannot access your keys
ComplianceFIPS 140-2 Level 2FIPS 140-2 Level 3
APIAWS KMS APIPKCS#11, JCE, CNG
CostPer API callPer HSM hour (~$1.45/hr)
Use whenStandard encryption requirementsRegulatory mandates for dedicated HSM, custom cryptography

CloudHSM integrates with KMS via a Custom Key Store — KMS uses CloudHSM to store and use key material, giving you the KMS API with CloudHSM-backed security.


Exam Key Points

  • Key policy is required — IAM alone cannot grant access to a KMS key; the key policy must also allow it
  • Envelope encryption: KMS encrypts the data key, you encrypt the data — never encrypt large payloads directly with KMS
  • Cross-account: key policy must allow the external account + the external account’s IAM must also allow KMS actions (both required)
  • Imported key material: no automatic rotation, you can delete the key material immediately (key becomes unusable instantly)
  • MRK: same key ID prefix (mrk-), data encrypted in one region can be decrypted in any replica region
  • Deletion: 7–30 day waiting period, data is unrecoverable after key is deleted
  • CloudHSM: FIPS 140-2 Level 3, single-tenant, you control everything, AWS cannot access keys

Quick Reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Keys
aws kms list-keys --output table
aws kms describe-key --key-id alias/my-key
aws kms list-aliases

# Create CMK
aws kms create-key --description "My CMK" --key-usage ENCRYPT_DECRYPT
aws kms create-alias --alias-name alias/my-key --target-key-id key-id-here

# Encrypt / decrypt
aws kms encrypt --key-id alias/my-key --plaintext "hello" --output text --query CiphertextBlob
aws kms decrypt --ciphertext-blob fileb://encrypted.bin --output text --query Plaintext | base64 -d

# Key policy
aws kms get-key-policy --key-id alias/my-key --policy-name default
aws kms put-key-policy --key-id alias/my-key --policy-name default --policy file://policy.json

# Audit: all KMS API calls by key
# Use CloudTrail Athena query filtering on eventsource = kms.amazonaws.com
This post is licensed under CC BY 4.0 by the author.