Amazon SCS-C02: Encryption, KMS & Secrets — Study Guide
Part of the AWS Security Specialty SCS-C02 — Study Guide. Practice with verified answers in the Amazon exam hub, or take timed practice tests on ExamRoll.io.
AWS KMS Key Types, Policies, and Access Control
AWS KMS supports three broad categories of keys, and picking the right one dictates who controls the key material, where it lives, and how it can be rotated. AWS-owned keys are invisible to you, cost nothing, and are used by services like S3 when you enable SSE-S3. AWS-managed keys (aliased aws/<service>) let a service encrypt on your behalf, but you cannot modify their key policy, which is why they are unsuitable for cross-account access or fine-grained governance. Customer-managed keys (CMKs) are the workhorse: you control the key policy, rotation (annual automatic or on-demand), grants, aliases, and deletion window.
Two specialized variants matter. Imported key material is used when regulatory or BYOK requirements force you to generate key material outside AWS and import it into a KMS key. Imported material is the only way to configure explicit expiration of key material—AWS-generated CMKs never expire. You cannot enable AWS automatic annual rotation on imported keys; you must re-import material yourself. Multi-Region keys share the same key ID and material across Regions through replica keys, so a ciphertext produced in us-east-1 can be decrypted in us-west-1 without re-encryption. Each replica has its own independent key policy and aliases, but the cryptographic material is synchronized.
The single most misunderstood control is the KMS key policy. Unlike most AWS resources where IAM policies alone grant access, KMS keys use their key policy as the root authorization. An IAM policy granting kms:Decrypt on a key is inert unless the key policy also delegates access to IAM (via a Principal of the account root plus an appropriate statement, or by naming the principal directly). This is why an engineer with AdministratorAccess can still receive AccessDenied when calling Decrypt on a CMK whose policy does not trust the account. The canonical delegation statement looks like this:
{
"Sid": "EnableIAMPermissions",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:root" },
"Action": "kms:*",
"Resource": "*"
}
Grants and ViaService conditions add layered restrictions—for example, forcing that a key be used only through S3 in a specific Region with kms:ViaService: s3.us-east-1.amazonaws.com.
Server-Side and Client-Side Encryption
For S3, the two common server-side options differ mainly in control and auditability:
- SSE-S3 (AES-256): S3 manages keys entirely; there is no customer-visible key policy, no per-key CloudTrail decrypt events, and no cross-account governance. Adequate for baseline encryption at rest but insufficient when compliance requires proving who decrypted data.
- SSE-KMS: Uses a CMK you control; every decrypt call is logged, key policy enforces access, and you can restrict by encryption context. It costs more per request and consumes KMS TPS quotas, so bucket keys (
BucketKeyEnabled: true) should be enabled to reduce KMS calls dramatically.
Client-side encryption using the AWS Encryption SDK is appropriate when data must be encrypted before it leaves the application, or when the storage service must never see plaintext. High-throughput workloads should wrap the SDK with the caching cryptographic materials manager (CachingCryptoMaterialsManager), which reuses data keys across many messages within configurable byte, message, and TTL limits. Without caching, every encrypt call triggers a GenerateDataKey request, quickly saturating KMS request quotas and inflating cost.
from aws_encryption_sdk import CachingCryptoMaterialsManager, LocalCryptoMaterialsCache
cache = LocalCryptoMaterialsCache(capacity=100)
ccmm = CachingCryptoMaterialsManager(
master_key_provider=mkp, cache=cache,
max_age=600.0, max_messages_encrypted=10000)
Secrets Manager and Parameter Store
Secrets Manager stores credentials encrypted with a KMS CMK and supports automatic rotation through a Lambda function—AWS provides templates for RDS, Redshift, and DocumentDB, and custom Lambdas handle anything else. Rotation runs a four-step state machine (createSecret, setSecret, testSecret, finishSecret) that stages new credentials under the AWSPENDING label before promoting them to AWSCURRENT. Applications should catch authentication failures, refresh the secret, and retry—this pattern eliminates downtime because the previous credential remains valid via AWSPREVIOUS briefly.
When the rotation Lambda runs inside a VPC (typical for reaching a private RDS instance), it needs outbound network access to the Secrets Manager service endpoint. In a private VPC without NAT, you must deploy an interface VPC endpoint (com.amazonaws.<region>.secretsmanager) and permit the Lambda’s security group to reach the endpoint on port 443. Forgetting this is a classic failure: rotation appears configured but every invocation times out.
For cross-Region resilience, use a multi-Region KMS key and Secrets Manager’s replica-secret feature. The primary secret in us-east-1 is encrypted with the primary CMK; the replica in us-west-1 decrypts using the replica CMK. Aliases like alias/prod-db can be pointed at a new key ID on demand for rapid key rotation without changing application code.
Parameter Store’s SecureString type is a lightweight alternative when you don’t need rotation. Both services expose dynamic references in CloudFormation ({{resolve:secretsmanager:MySecret:SecretString:password}}) so stack templates never embed plaintext.
EBS, RDS, Aurora, and Snapshot Encryption
Encryption at rest is enabled per-volume or per-instance at creation time and cannot be toggled in place. The remediation pattern for a noncompliant unencrypted resource is a snapshot copy with encryption:
aws ec2 copy-snapshot --source-snapshot-id snap-abc \
--source-region us-east-1 --encrypted \
--kms-key-id alias/prod-ebs
aws ec2 create-volume --snapshot-id snap-newEncrypted ...
For RDS and Aurora, restore the encrypted snapshot into a new instance and cut over. Cross-account recovery requires sharing the snapshot and granting the destination account kms:CreateGrant and kms:Decrypt on the CMK via the key policy—sharing the snapshot alone will fail because the destination cannot decrypt the data key. Account-level EBS default encryption should be enabled so newly created volumes are always encrypted regardless of caller behavior.
TLS: ACM and ALB Policies
ACM issues and auto-renews public certificates at no charge when bound to integrated services (ALB, CloudFront, API Gateway). Certificates cannot be exported, so EC2-terminated TLS requires either ACM Private CA (for private certs you can export) or an imported certificate. A pragmatic pattern: terminate public TLS at the ALB with an ACM cert, and use a self-signed or private-CA cert for ALB-to-EC2 hop if end-to-end encryption is required. Force clients onto modern ciphers with a security policy such as ELBSecurityPolicy-TLS13-1-2-2021-06, which disables TLS 1.0/1.1 and weak suites.
Common Traps
IAM without key policy. Granting kms:Decrypt in an IAM policy while the CMK’s key policy omits the account principal produces AccessDenied. KMS treats the key policy as authoritative; IAM permissions can only further restrict what the key policy allows.
Rotation Lambda without VPC endpoint. If the Lambda runs in private subnets and the VPC has no NAT and no secretsmanager interface endpoint, the rotation call to secretsmanager.<region>.amazonaws.com cannot resolve or connect. The security group on the endpoint must also allow 443 from the Lambda’s SG.
SSE-S3 equated with SSE-KMS. SSE-S3 uses an AWS-owned key with no customer-editable policy, no per-object decrypt logging in CloudTrail, and no cross-account key sharing. It satisfies “encrypt at rest” checkboxes but cannot enforce which principals decrypt specific objects—only SSE-KMS with a CMK provides that governance.
Practical Problem: Use-Case Scenario
Scenario: Meridian Financial runs a multi-account AWS Organization with a Security account, separate Prod/NonProd accounts, ALB fronted microservices in ECS/EKS, RDS/Aurora clusters, EBS-backed EC2 instances, and S3 data lakes. Developers and automation currently use a mix of AWS-managed keys, plaintext SSM parameters, and occasional manual snapshot sharing between accounts.
Challenge: An engineer accidentally shared an unencrypted RDS snapshot to a third-party account and several API credentials were discovered stored as plaintext SecureString parameters, creating risk of data exfiltration and unauthorized restore access.
Recommended Approach:
- Create an organization-scoped, customer-managed AWS KMS symmetric CMK in the Security account with a key policy granting use via aws:PrincipalOrgID to member accounts and enable automatic rotation; use grants for short-lived cross-account operations.
- Remediate existing artifacts by copying the unencrypted RDS snapshot and any EBS snapshots while selecting the new CMK to produce encrypted copies, then delete the original unencrypted snapshots; set account defaults so new RDS and EBS create encrypted resources by default.
- Migrate secrets into AWS Secrets Manager (or SSM Parameter Store SecureString) encrypted with the CMK, enable Secrets Manager automatic rotation for DB credentials via Lambda, and restrict access using resource-based policies and least-privilege IAM roles.
- Enforce encryption in transit by provisioning ACM-managed TLS certificates and attaching them to ALBs with a modern TLS policy (TLS 1.2/1.3), and configure databases and clients to require TLS connections.
- Prevent recurrence with guardrails: apply Service Control Policies to deny creation/share of unencrypted snapshots and unencrypted S3 puts, enable AWS Config rules for encrypted resources, and monitor KMS and Secrets Manager usage via CloudTrail and CloudWatch Alarms.
Rationale: Central CMKs with org-level policies, automated re-encryption, Secrets Manager for secret lifecycle, TLS enforcement, and preventive guardrails follow AWS least-privilege and defense-in-depth best practices to eliminate plaintext secrets and unauthorized snapshot access.
Customer Managed CMKs: Multi-Region, Imported Material, and Key Policies
A customer managed CMK is the control plane for every cryptographic operation on data you own in AWS. The three properties that most often determine whether a design succeeds or produces an outage are the key’s Region topology, the origin of its key material, and the policy attached to it.
Multi-Region keys are a set of KMS keys in different Regions that share the same key ID and, crucially, the same underlying key material. They are not replicated automatically the way DynamoDB global tables are — you explicitly create replicas from a primary key using ReplicateKey. Because the key material is identical across replicas, ciphertext produced in us-east-1 can be decrypted in us-west-1 without cross-Region KMS calls. This is exactly the property required when replicating a Secrets Manager secret across Regions: the replica secret in the failover Region must be decryptable locally, both to eliminate cross-Region latency on every GetSecretValue and to survive a Regional outage of the primary. A single-Region CMK cannot back a Secrets Manager replica in another Region, so the correct pattern is to encrypt the primary secret with a multi-Region CMK, replicate the key to the destination Region, and then replicate the secret while pointing at the replica CMK.
aws kms create-key --multi-region --region us-east-1
aws kms replicate-key --key-id mrk-abc123 \
--replica-region us-west-1
aws secretsmanager replicate-secret-to-regions \
--secret-id prod/db \
--add-replica-regions Region=us-west-1,KmsKeyId=mrk-abc123
Imported key material (external origin, Origin=EXTERNAL) exists when you generate the raw AES-256 material outside AWS and import it into a KMS key shell. AWS never has a copy of that material outside the HSM’s protected memory, and there is no backup. If you delete the imported material — whether via DeleteImportedKeyMaterial or because its expiration date passed — the key becomes PendingImport and all ciphertext produced under that key is unrecoverable unless you reimport the exact same bytes. This is the recovery path when, for example, an EBS volume fails to attach because its encrypted data key cannot be decrypted: reimport the identical key material from your offline escrow, and the volume becomes usable again. There is no AWS-side restore, no rotation trick, and no support ticket that recovers deleted imported material. Treat the offline copy as tier-zero infrastructure.
Key policies are the root of trust for every KMS key. Unlike IAM alone, KMS requires an explicit allow in the key policy itself; an IAM policy granting kms:Decrypt is ineffective unless the key policy delegates to IAM ("Principal": {"AWS": "arn:aws:iam::111122223333:root"} combined with a conditional statement). For cross-account use, the key’s policy must name the external account or principal explicitly, and the external account must then grant its own users permission via IAM. Forgetting the key policy side is the single most common cause of cross-account Secrets Manager failures — the Secrets Manager resource policy allows the external principal to call GetSecretValue, but the underlying Decrypt fails because the CMK still refuses the caller.
Secrets Manager and Parameter Store SecureString Patterns
Secrets Manager and SSM Parameter Store SecureStrings both delegate encryption to KMS, but they differ in cost, rotation semantics, and cross-Region behavior. Secrets Manager supports native multi-Region replication, versioning with staging labels (AWSCURRENT, AWSPENDING), and Lambda-backed rotation. Parameter Store SecureString is cheaper, integrates with hierarchical paths, and works well for configuration-style secrets that rotate infrequently.
For cross-account access, you must update both the resource policy on the secret (or the IAM policy in the Parameter Store consumer account) and the KMS key policy on the encrypting CMK. Assuming Secrets Manager permissions alone are enough is wrong because the retrieval workflow always performs an implicit kms:Decrypt against the CMK; without a key-policy allow for the external account, the caller receives AccessDeniedException on the Decrypt step even though the secret’s own policy is satisfied.
- Secrets Manager: managed rotation, JSON structure, ~$0.40/secret/month, multi-Region replication built-in
- Parameter Store SecureString (Advanced): cheaper for many values, 8 KB per parameter, no built-in cross-Region replication
- Parameter Store SecureString (Standard): free tier for KMS-encrypted parameters up to 4 KB, no rotation framework
Envelope Encryption, Bucket Keys, and Grant Tokens
Envelope encryption means KMS never touches your bulk data. You call GenerateDataKey, which returns both a plaintext data key (used locally to encrypt your payload with AES-GCM) and an encrypted copy of that data key (stored alongside the ciphertext). To decrypt, you call Decrypt on the wrapped data key and re-derive the plaintext key locally. This pattern is essential because KMS has request quotas (per Region, per key) and pricing per API call. If you encrypt each 4 KB record with a direct Encrypt call, you will hit throttling and cost cliffs; if you generate one data key per batch or per file, throughput scales linearly with your local crypto library.
S3 Bucket Keys apply the same principle inside S3 for SSE-KMS. Without a bucket key, every PUT and GET of an SSE-KMS object generates a GenerateDataKey or Decrypt call. On a bucket receiving thousands of objects per second this produces both KMS throttling and a surprising KMS bill. Enabling a bucket key causes S3 to generate one short-lived key at the bucket level and reuse it for many objects, cutting KMS request volume by orders of magnitude:
aws s3api put-bucket-encryption --bucket app-data \
--server-side-encryption-configuration '{
"Rules":[{
"ApplyServerSideEncryptionByDefault":{
"SSEAlgorithm":"aws:kms","KMSMasterKeyID":"alias/app"},
"BucketKeyEnabled":true}]}'
Grants are an alternative to key policies for temporary, fine-grained delegation. They matter operationally because of eventual consistency: after CreateGrant returns, the grant is not immediately visible to every KMS endpoint in the Region. If a client attempts an Encrypt a few milliseconds later, it may receive AccessDeniedException. The response body of CreateGrant includes a GrantToken string that, when passed on subsequent KMS calls via the --grant-tokens parameter, forces KMS to honor the grant immediately regardless of propagation state.
TOKEN=$(aws kms create-grant --key-id $KEY \
--grantee-principal arn:aws:iam::111122223333:role/worker \
--operations Encrypt Decrypt --query GrantToken --output text)
aws kms encrypt --key-id $KEY --plaintext fileb://payload \
--grant-tokens "$TOKEN"
Relying on retry-with-backoff instead of the grant token is a valid but inferior mitigation — it wastes latency and still fails under load. The canonical answer is always: return the grant token from the service that creates the grant, and require callers to present it on their first operation.
Practical Problem: Use-Case Scenario
Scenario: Meridian Financial operates a multi-account AWS environment hosting customer PII in S3, transactional databases in RDS, and serverless processing via Lambda. They use Customer Managed CMKs with imported key material to meet regional key custody rules and replicate keys to a second region for disaster recovery.
Challenge: A recent audit found a misconfigured KMS key policy that allowed cross-account decrypts and an external auditor needs temporary access to decrypt a subset of S3 objects; Meridian also needs secure secret rotation and efficient encryption for large objects to control KMS request costs.
Recommended Approach:
- Rotate the misconfigured CMK policy in AWS KMS to a least-privilege policy that explicitly grants only required IAM principals and roles, and create a multi-Region replica CMK for DR using KMS multi-Region keys.
- Re-import or schedule lifecycle management for imported key material per compliance windows and enable automatic key material expiry/rotation notifications using AWS Config and EventBridge.
- For the auditor, create a KMS grant with a short TTL and use the grant token immediately in the auditor’s assume-role session to allow temporary decrypt operations without changing the key policy.
- Move long-lived credentials into AWS Secrets Manager with Lambda-based rotation tied to the underlying service (RDS or API keys) and store infrastructure parameters as Systems Manager Parameter Store SecureString for non-rotating items, enforcing encryption with the CMK and strict resource-based policies.
- Implement envelope encryption for large S3 objects by calling KMS GenerateDataKey (Encrypt/Decrypt) in application code or via the AWS SDK, and enable S3 Bucket Keys to reduce KMS requests and cost for server-side encryption of large objects.
- Enable CloudTrail logging and KMS key usage logging, and create CloudWatch Alarms/GuardDuty rules to alert on unexpected decrypts or grant creations.
Rationale: This approach enforces least-privilege key access, preserves compliance for imported material and multi-Region continuity, uses temporary grants for safe third-party access, centralizes secrets with rotation, and optimizes KMS usage and cost per AWS best practices.
← Logging · All domains · Data Protection →
Practice these questions → · Timed practice on ExamRoll.io →
Pass the whole exam — not just this question
You found this answer. Get every verified question and explanation in one place, and save hours of prep. Free to start.
Pass your exam →