Course outline

10.8

S3 Encryption

SSE-S3, SSE-KMS with Bucket Keys, DSSE-KMS, SSE-C, client-side encryption, default encryption and forcing TLS.

26 min Reading + animations

Tip: tap or hover any dotted-underlined term to see what it means.

By the end of this lesson you will be able to

  • Tell encryption at rest from encryption in transit, and say what each one protects against.
  • Explain the five ways to encrypt an S3 object (SSE-S3, SSE-KMS, DSSE-KMS, SSE-C and client-side encryption): who encrypts, where the key lives, the header that asks for it, and what it costs.
  • Describe how S3 uses KMS on every upload and download, why a reader needs permission on the key as well as on the bucket, and why cross-account access needs a customer managed key.
  • Calculate when SSE-KMS traffic hits the KMS request quota, and fix it with an S3 Bucket Key or a quota increase.
  • Force HTTPS with aws:SecureTransport, force a particular encryption with a bucket policy, and explain why bucket policies are evaluated before default encryption.
  • Pick the right option for a scenario, the way the exam words it.

#Why encrypt data that is already locked away?

#Two places data can leak

Lesson 10.2 showed how bucket policies and IAM decide who may ask S3 for an object. Encryption answers a different question: what can someone read if they get the bytes without asking? Bytes can leak in two places, so S3 protects them in two states:

  • In transit (also called in flight): while the request travels between your client and S3. Someone on the network path, a hotel Wi-Fi, a compromised router, could read or change it. The protection is TLS, that is HTTPS.
  • At rest: while the object sits on S3’s disks. A disk that is replaced and not wiped, a backup copy, or a bug that exposes raw storage would reveal plain data. The protection is server-side encryption (S3 encrypts before writing to disk) or client-side encryption (you encrypt before sending).

Encryption does not replace permissions. An object encrypted with SSE-S3 is still handed over, decrypted, to anyone the bucket policy lets in. The stronger options (SSE-KMS and the ones that keep the key outside AWS) add a second gate: the key itself.

#Five ways to encrypt an object

S3 offers four kinds of server-side encryption (SSE), where S3 does the encrypting and decrypting, and you can always do client-side encryption yourself. The exam expects you to know which one fits which situation.

SSE-S3SSE-KMSDSSE-KMSSSE-CClient-side
Who encryptsS3S3S3, twiceS3You, before upload
Where the key livesS3, owned by AWSAWS KMSAWS KMS + S3Your own system; S3 never stores itYour own system (or KMS)
Who controls the keyAWSYou (key policy, rotation, disable)YouYouYou
Request headerx-amz-server-side-encryption: AES256x-amz-server-side-encryption: aws:kmsx-amz-server-side-encryption: aws:kms:dsseThree x-amz-server-side-encryption-customer-* headersNone (S3 sees ordinary bytes)
Audit of key useNoYes, CloudTrailYes, CloudTrailNo (your own logs)Your own logs
Extra costNoneKMS requests (+ key)More than SSE-KMSNoneYour own work
Typical reasonDefault, nothing to manageControl + audit trailRule demands two layersKeys must stay outside AWSAWS must never see plain data

All of them use AES-256. What differs is who holds the key, who can use it, and who can see that it was used.

#SSE-S3: the lock that is always on

With SSE-S3 (server-side encryption with Amazon S3 managed keys), S3 does everything:

  • It generates a unique data key for each object, encrypts the object with AES-256 before writing it to disk, and protects each data key with a root key that S3 owns and regularly rotates. You never see, manage or pay for any of these keys.
  • To ask for it explicitly, a request sends the header x-amz-server-side-encryption: AES256.
  • It is on for every bucket. Since 5 January 2023 S3 applies SSE-S3 as the base level of encryption to all new object uploads in all buckets, at no cost and with no performance impact. Buckets that had no default encryption were switched to SSE-S3; buckets already set to SSE-KMS kept it.
  • No KMS calls: no KMS charges, no KMS quota, no key events in CloudTrail.

What SSE-S3 does not give you: control over the key, a separate permission on the key, or a record of key use. Anyone with s3:GetObject reads the plain object. For a news site’s public photos or a product catalogue that is exactly right. For the hospital’s scans, the regulator’s questions (“who can use the key? show me who used it”) have no answer.

#SSE-KMS: your key, with an audit trail

#How S3 uses KMS on each request

With SSE-KMS, the key that protects your data lives in AWS KMS (Key Management Service, covered in depth in chapter 21). KMS keys never leave KMS’s hardware security modules (HSMs); S3 uses the envelope encryption you met with EBS in lesson 5.2:

  1. Upload. The request carries x-amz-server-side-encryption: aws:kms, optionally with x-amz-server-side-encryption-aws-kms-key-id naming a key (or the bucket’s default encryption names it). S3 calls KMS GenerateDataKey on your behalf.
  2. KMS returns a plaintext data key and the same data key encrypted under your KMS key.
  3. S3 encrypts the object with the plaintext data key, stores the encrypted data key next to the object, and wipes the plaintext key from memory.
  4. Download. S3 sends the encrypted data key to KMS in a Decrypt call, gets the plaintext data key back, decrypts the object and returns it.
  5. Every GenerateDataKey and Decrypt call is recorded in CloudTrail (lesson 19.6) with the caller, the key and the encryption context, which by default is the object’s ARN. That is the audit trail: you can answer “who decrypted scan-0412 last Tuesday?”.

Two rules that trip people up: a GET or HEAD of an SSE-KMS object must not carry encryption headers (that returns 400 Bad Request), and every request for a KMS-encrypted object must use HTTPS and Signature Version 4.

Server-side encryption, step by step

The hospital app uploads a scan and reads it back. Watch who makes the data key, where it is kept, and what lands in CloudTrail.

Scenario

S3 owns and manages every key. Nothing to set up, nothing to pay, nothing to audit.

Hospital appIAM role
Amazon S3scans-example
AWS KMSkey: scans-key
CloudTrailaudit log
PUT scan-0412 · AES256
Step 1 of 5
1

The app uploads over HTTPS. It may send the header x-amz-server-side-encryption: AES256, or no header at all: since January 2023 every new object gets SSE-S3 anyway.

#AWS managed key or customer managed key

AWS managed key aws/s3Customer managed key
Created byS3, automatically, the first time you use SSE-KMS without naming a keyYou, in KMS
Monthly feeNone$1 per key per month
Key policyFixed; you cannot edit itYours to write
RotationAutomatic, yearlyYou choose (automatic or on demand)
Disable or schedule deletionNoYes
Usable by another accountNoYes, if the key policy allows it

Both charge per KMS request (about $0.03 per 10,000 requests in most Regions). The key must be a symmetric KMS key in the same Region as the bucket.

#Two locks: the bucket and the key

With SSE-KMS, reading an object needs two permissions:

  • s3:GetObject on the object, from IAM or the bucket policy (lesson 10.2), and
  • kms:Decrypt on the KMS key, from the key policy and/or the caller’s IAM policy.

Writing needs s3:PutObject and kms:GenerateDataKey. A multipart upload (lesson 10.12) needs both kms:GenerateDataKey and kms:Decrypt, because S3 decrypts the data key again for each part; “single PUT works, large uploads fail” is the classic symptom of a missing kms:Decrypt.

That second lock is what the hospital wanted. The billing team keeps s3:GetObject for its invoices, but the key policy of scans-key lists only the radiology and doctor roles, so the billing role gets AccessDenied on every scan, and the attempt is logged. In an incident, the security team can disable the key and every scan becomes unreadable at once, without touching a single bucket policy.

#Cross-account access needs a customer managed key

Objects encrypted with the AWS managed key cannot be shared across accounts: its key policy only trusts its own account and cannot be changed. If a partner account must read the objects, or upload them, use a customer managed key whose key policy grants that account the KMS actions, plus IAM permissions inside the partner account.

There is a nastier trap on uploads. If a role from another account uploads with aws:kms and no key ID, and the bucket default names no KMS key, S3 uses the AWS managed key of the uploading account. The upload succeeds, but nobody in the bucket owner’s account can ever decrypt the object. AWS’s advice for cross-account writes: name the customer managed key by its full key ARN, not an alias (an alias is resolved in the requester’s account).

#When KMS becomes the bottleneck

#The KMS request quota

Every SSE-KMS upload is one GenerateDataKey call and every download is one Decrypt call. KMS limits how many cryptographic operations per second an account may make in a Region, and all of them share one quota: S3’s calls, EBS, RDS, Lambda, Secrets Manager and your own code. The current defaults for symmetric keys:

RegionShared quota (requests per second)
US East (N. Virginia), US West (Oregon), Europe (Ireland)100,000
US East (Ohio), Asia Pacific (Singapore, Sydney, Tokyo), Europe (Frankfurt, London, Spain)20,000
All other Regions10,000

Above the quota, KMS throttles the extra calls, and S3 hands the failure back to your clients as a throttling error that the SDK retries with backoff. The quota is per account and Region and is adjustable in the Service Quotas console.

Worked example. A news site in a Region with the 10,000 quota serves 11,000 photo downloads and 1,000 uploads per second on election night, all SSE-KMS: 11,000 Decrypt + 1,000 GenerateDataKey = 12,000 KMS calls per second, 2,000 over the quota. S3 itself could take the load (its limits are 3,500 writes and 5,500 reads per second per prefix, lesson 10.6), but KMS cannot.

#S3 Bucket Keys

An S3 Bucket Key is the fix AWS built for exactly this problem:

  1. S3 asks KMS for a short-lived bucket-level key (encryption context: the bucket ARN).
  2. For a limited time, S3 uses that key inside S3 to create the unique data key for each new object. Most uploads and downloads then need no KMS call at all.
  3. S3 still fetches the bucket key at least once per requester (a different role or account), so each requester’s use of the key is still logged.

The effect: up to 99% fewer KMS requests, which means far lower KMS cost and far less pressure on the quota. You enable it on the bucket (default encryption → Bucket Key), or per request with x-amz-server-side-encryption-bucket-key-enabled: true, with no change to your application. What changes:

  • CloudTrail shows the bucket ARN instead of each object ARN, and far fewer KMS events. Auditors who searched by object must search differently.
  • Key policies or IAM conditions that used the object ARN as encryption context must switch to the bucket ARN.
  • Existing objects keep their old settings until you copy them (CopyObject, or Batch Operations for millions, lesson 10.7).
  • Not available with DSSE-KMS.

Cost example. A bucket serves 200 million SSE-KMS requests a month. Without a Bucket Key that is 200,000,000 ÷ 10,000 × $0.03 = $600 of KMS requests. With a Bucket Key and a few requesters, AWS’s “up to 99%” brings it near $6.

The other fix is the one the notes and slides give: request a quota increase through Service Quotas (or AWS Support where Service Quotas does not cover it). Do both when traffic is huge; try the Bucket Key first, because it also cuts the bill.

When KMS becomes the bottleneck

A Dhaka news site serves photos from an SSE-KMS bucket in a Region whose shared KMS quota is 10,000 requests per second.

Scenario

Readers
S3 bucketphotos-example
AWS KMS
CloudTrail
3,000 GET/sDecrypt

This second

S3 requests/sKMS calls/sQuotaResult
3,0003,00010,000OK
Step 1 of 2
1

Every GET of an SSE-KMS photo makes S3 call Decrypt; every new upload makes it call GenerateDataKey. One S3 request, one KMS call.

Self-checkQuestion

A photo app in Europe (Frankfurt), where the shared KMS quota is 20,000 requests per second, stores everything with SSE-KMS and no Bucket Key. At peak it handles 6,000 uploads and 16,000 downloads per second. What happens?

#DSSE-KMS: two layers for strict rules

DSSE-KMS (dual-layer server-side encryption with KMS keys) encrypts each object twice, with two independent layers of AES-256:

  1. First layer: with a unique data key from AWS KMS, as in SSE-KMS.
  2. Second layer: the already encrypted data is encrypted again with a separate AES-256 key managed by S3.

If one layer were ever compromised, the other still protects the data. You ask for it with x-amz-server-side-encryption: aws:kms:dsse. Key control, permissions and the CloudTrail audit trail work as in SSE-KMS.

The costs: more processing and slightly higher latency, a higher charge than SSE-KMS, and no S3 Bucket Keys, so every request calls KMS and counts against the quota. Choose it only when a compliance standard explicitly requires multi-layer encryption, such as the US government’s rules for protecting classified data, which call for two layers of encryption (the notes mention the US Department of Defense).

#SSE-C: bring your own key on every request

#How SSE-C works

With SSE-C (server-side encryption with customer-provided keys), you hold the key outside AWS, but S3 still does the encryption work:

  • Every request that touches the object (PUT, GET, HEAD, copy) carries the key in three headers: x-amz-server-side-encryption-customer-algorithm: AES256, x-amz-server-side-encryption-customer-key (the 256-bit key, base64-encoded) and x-amz-server-side-encryption-customer-key-MD5 (a checksum of the key, so S3 can detect a corrupted key).
  • S3 encrypts or decrypts with that key, then discards it. It keeps only a randomly salted HMAC of the key, so it can reject a wrong key later; the key cannot be rebuilt from it.
  • HTTPS is mandatory. S3 rejects any SSE-C request over plain HTTP. If a key was ever sent over HTTP by mistake, AWS says to treat it as compromised and rotate it.
  • You track which key encrypted which object (and which version, if versioning is on). Lose a key and the object is gone; AWS cannot help, because it never had the key.
  • The S3 console cannot upload SSE-C objects or change them. There is no extra charge.
  • AWS services that need to read your data (analytics, replication tools, Lambda) cannot decrypt SSE-C objects on their own, which is why AWS now steers most workloads to SSE-KMS.

The notes’ exam trick is right: “the company wants to manage its own keys, but have AWS perform the encryption” → SSE-C.

#Blocked by default on new buckets since April 2026

From 6 April 2026 AWS rolled out a new default: SSE-C is disabled for all new general purpose buckets (in every Region except the two Middle East Regions in Bahrain and the United Arab Emirates), and for existing buckets in accounts that had no SSE-C objects. While it is blocked, any PutObject, CopyObject, POST, multipart upload or replication request that asks for SSE-C fails with 403 AccessDenied; existing SSE-C objects can still be read. To use SSE-C, the bucket owner must unblock it with PutBucketEncryption, setting BlockedEncryptionTypes to NONE in the default encryption configuration. You can also block SSE-C on older buckets the same way.

#Client-side encryption: S3 never sees plain data

With client-side encryption you encrypt the data before it leaves your system and decrypt it after you download it. S3 stores ciphertext and has no idea what it contains.

  • Use a client library: the Amazon S3 Encryption Client (which wraps the S3 SDK) or the AWS Encryption SDK. The key can come from your own key store, or from KMS: the library then calls KMS for a data key but still encrypts in your process.
  • You own the whole encryption cycle: algorithms, key storage, rotation, and decryption in every application that reads the data.
  • S3 still applies its default SSE-S3 on top, so the object is encrypted twice, but only you can remove the inner layer.
  • AWS services cannot look inside: no Athena queries, no Macie scans for personal data, no image resizing in Lambda unless that code has the key.

Compare the two “keys in your hands” options, SSE-C and client-side encryption, in both normal and failure cases:

Keys you hold yourself

With SSE-C, S3 still does the encryption but never keeps your key. With client-side encryption, S3 never even sees plain data.

Scenario

You supply a 256-bit key with every request; S3 encrypts with it and forgets it.

Own key storeon premises
Hospital app
Amazon S3scans-example
owner unblocks SSE-C
Step 1 of 7
1

Since April 2026 new buckets block SSE-C. The bucket owner first allows it in the bucket’s default encryption settings (blocked encryption types: none).

#Encryption in transit

#HTTP and HTTPS endpoints

S3’s API endpoints answer on both HTTP (not encrypted) and HTTPS (encrypted in flight with TLS). HTTPS is recommended, current AWS SDKs and the AWS CLI use it by default, and it is mandatory for SSE-C and for requests on KMS-encrypted objects. Since 27 February 2024 every AWS API endpoint, S3 included, requires TLS 1.2 or later, so clients that only speak TLS 1.0 or 1.1 cannot connect at all.

(The static website endpoint of lesson 10.3 is different: it speaks only HTTP, and you put CloudFront in front of it for HTTPS.)

#Forcing HTTPS with aws:SecureTransport

S3 does not refuse HTTP by itself. To make HTTPS mandatory for a bucket, add a Deny to the bucket policy for every request where the global condition key aws:SecureTransport is false (lesson 2.1 introduced it):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyInsecureTransport",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::scans-example",
        "arn:aws:s3:::scans-example/*"
      ],
      "Condition": { "Bool": { "aws:SecureTransport": "false" } }
    }
  ]
}

Why a Deny, not an Allow with "true"? An Allow only adds permission. A user whose IAM policy already allows S3 would still get in over HTTP. An explicit Deny beats every Allow (lesson 2.2), including the account’s own administrators. Both the bucket ARN and /* are listed, so bucket-level and object-level actions are covered.

For a stricter rule, such as “TLS 1.3 only”, add a second Deny on the condition key s3:TlsVersion with NumericLessThan 1.3.

Forcing HTTPS with a bucket policy

The bucket policy has one Deny statement: any request where aws:SecureTransport is false. Two apps try their luck.

Scenario

Legacy apphttp://
Mobile apphttps://
Bucket policy
S3 bucketscans-example
HTTPS GET

Policy evaluation

Requestaws:SecureTransportPolicyResult
HTTPStrueDeny does not match…
Step 1 of 2
1

The mobile app connects to the HTTPS endpoint, so the whole request, headers and body, is inside a TLS tunnel.

#Default encryption and bucket policies

#Default encryption

Every bucket has a default encryption configuration: the encryption S3 applies when an upload carries no encryption header. It is SSE-S3 unless you change it to SSE-KMS (with the AWS managed key or a customer managed key, and optionally a Bucket Key) or DSSE-KMS. An upload that does carry a header gets what the header asks for.

The notes’ exam trick holds: “how do you make sure all new objects in a bucket are encrypted?” → default encryption. Since 2023 that is automatic with SSE-S3, so modern questions usually ask for a specific type, such as “all objects must be encrypted with SSE-KMS using our key” → set default encryption to SSE-KMS with that key, and add a bucket policy if uploads with other headers must be refused.

Things default encryption does not do:

  • It does not touch existing objects. Changing the default from SSE-S3 to SSE-KMS leaves old objects as they were; re-encrypt them by copying them in place with CopyObject or, for millions of objects, S3 Batch Operations Copy (lesson 10.7).
  • It does not override a header. A client that sends AES256 gets SSE-S3 even if the default is SSE-KMS, unless a bucket policy refuses it.

#Forcing one encryption type with a bucket policy

To refuse uploads that do not use SSE-KMS, deny s3:PutObject when the encryption header is anything other than aws:kms:

{
  "Sid": "DenyNonKmsUploads",
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::scans-example/*",
  "Condition": {
    "StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" }
  }
}

To insist on one particular key, add a Deny on s3:x-amz-server-side-encryption-aws-kms-key-id with the key’s full ARN. The older, classic form denies uploads where the header is missing ("Null": { "s3:x-amz-server-side-encryption": "true" }).

Bucket policies are evaluated before default encryption. With the policy above, a request with no header has no s3:x-amz-server-side-encryption value, StringNotEquals is true, and the upload is denied, even if the bucket’s default encryption is SSE-KMS and would have encrypted it correctly. So if you add such a policy, every client must send the header. If you only want “everything encrypted with SSE-KMS”, default encryption alone is enough and kinder to clients.

Try every combination: default encryption, Bucket Key, SSE-C block, the two policy statements, HTTP or HTTPS, each header, missing KMS permission and a caller from another account.

What happens to this upload?

Set up the bucket scans-example (account 111122223333) and one PUT request, then send it. The trace shows every check in order and what is finally stored.

The bucket

Default encryption

The request

Protocol

Encryption header

Press “Send PUT” to see what S3 does.

#Choosing the right option

Which encryption should this data use?

Answer for one data set, then press Recommend. Try the hospital’s scans, a public product-photo bucket, and a bank’s archive that a regulator inspects.

Who must manage the encryption key?

Press Recommend to see the answer.

#What goes wrong

  • “Allowed by S3, denied by KMS.” A role has s3:GetObject but gets AccessDenied on SSE-KMS objects only: it lacks kms:Decrypt in the key policy or its IAM policy. Large uploads failing while small ones work: missing kms:Decrypt for multipart.
  • Cross-account objects nobody can read. A partner uploaded with aws:kms and no key ID, so the objects are under the partner’s aws/s3 key. Fix the process (customer managed key, full ARN in the request or the default), then re-copy the objects from the partner account.
  • KMS throttling during peaks, and other services failing at the same time because the quota is shared. Enable a Bucket Key, watch KMS usage against the quota in Service Quotas and CloudWatch, request an increase.
  • Wrong Region key. A KMS key in us-east-1 cannot encrypt objects in an eu-west-1 bucket; create the key (or a multi-Region replica of it) in the bucket’s Region.
  • Disabled or deleted key. Disable the key and every SSE-KMS object it protects becomes unreadable until it is enabled again; delete it and the data is lost for good. KMS forces a waiting period of 7 to 30 days before deletion for exactly this reason.
  • Lost SSE-C key: permanent data loss. SSE-C over HTTP: rejected, and the key must be rotated. SSE-C on a new bucket: 403 until unblocked.
  • Header-less uploads denied by a “require aws:kms” policy although default encryption is SSE-KMS: the policy is evaluated first.
  • Server access logs that cannot be read. The destination bucket for S3 server access logs (lesson 10.9) must use SSE-S3; with SSE-KMS default encryption, log objects can end up encrypted with a key you cannot use.
  • Replication surprises. SSE-KMS objects are replicated only if you enable that in the replication rule and give the replication role the KMS permissions (lesson 10.4). Unencrypted source objects take the destination’s default encryption, so their ETags differ.
  • Personal data in names or metadata: not encrypted by SSE.

#Encryption in system design

  • Start from the default and add only what a requirement asks for. SSE-S3 is free and invisible. Move to SSE-KMS when someone needs to control or audit the key; to DSSE-KMS only for a two-layer rule; to SSE-C or client-side only when the key must stay outside AWS.
  • Separate duties with key policies. Give the people who manage the bucket and the people who may read the data different permissions. With SSE-KMS, a storage administrator can manage lifecycle rules and replication without being able to read a single scan.
  • One key per data domain. A key for patient scans, another for invoices: each with its own key policy and its own CloudTrail story, and the option to disable one without touching the other.
  • Plan for request volume. For SSE-KMS buckets with heavy traffic, turn on Bucket Keys from day one; check the account’s KMS quota against all KMS users in the Region, not only S3.
  • Make HTTPS non-negotiable with the SecureTransport Deny on every bucket, and with s3:TlsVersion if a standard demands a newer TLS version.
  • Encryption works with the rest of S3. Versioning, replication, lifecycle rules, Object Lock and access points (lessons 10.4, 10.5, 10.9) all work on encrypted objects; pre-signed URLs for SSE-KMS objects must be signed with Signature Version 4 (lesson 10.12).

#Try it yourself

# 1. See the bucket's default encryption (SSE-S3 unless someone changed it)
aws s3api get-bucket-encryption --bucket scans-example

# 2. Upload with an explicit encryption type and check what was stored
aws s3 cp scan-0412.jpg s3://scans-example/scans/0412.jpg --sse AES256
aws s3api head-object --bucket scans-example --key scans/0412.jpg \
  --query '{sse:ServerSideEncryption,key:SSEKMSKeyId,bucketKey:BucketKeyEnabled}'

# 3. A customer managed key, then SSE-KMS with it
aws kms create-key --description "Patient scans" --query KeyMetadata.Arn --output text
aws s3 cp scan-0413.jpg s3://scans-example/scans/0413.jpg --sse aws:kms \
  --sse-kms-key-id arn:aws:kms:ap-south-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab

# 4. Default encryption = SSE-KMS with that key and an S3 Bucket Key
aws s3api put-bucket-encryption --bucket scans-example --server-side-encryption-configuration '{
  "Rules": [{
    "ApplyServerSideEncryptionByDefault": {
      "SSEAlgorithm": "aws:kms",
      "KMSMasterKeyID": "arn:aws:kms:ap-south-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
    },
    "BucketKeyEnabled": true
  }]
}'

# 5. DSSE-KMS for one object
aws s3 cp ledger-2026.csv s3://archive-example/ledger-2026.csv --sse aws:kms:dsse

# 6. SSE-C: unblock it on this bucket first (new buckets block it), then use a local 256-bit key
aws s3api put-bucket-encryption --bucket legal-example --server-side-encryption-configuration '{
  "Rules": [{ "BlockedEncryptionTypes": { "EncryptionType": ["NONE"] } }]
}'
openssl rand -out sse-c.key 32
aws s3 cp contract.pdf s3://legal-example/contract.pdf --sse-c AES256 --sse-c-key fileb://sse-c.key
aws s3 cp s3://legal-example/contract.pdf ./copy.pdf --sse-c AES256 --sse-c-key fileb://sse-c.key

# 7. Force HTTPS: save the DenyInsecureTransport policy above as tls-only.json
aws s3api put-bucket-policy --bucket scans-example --policy file://tls-only.json
curl -I http://scans-example.s3.ap-south-1.amazonaws.com/scans/0412.jpg   # 403: plain HTTP is denied

# 8. Re-encrypt an existing object in place with the new default (copy onto itself)
aws s3 cp s3://scans-example/scans/old-0001.jpg s3://scans-example/scans/old-0001.jpg \
  --sse aws:kms --sse-kms-key-id arn:aws:kms:ap-south-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab

# 9. Who used the key? KMS events in CloudTrail
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=Decrypt \
  --max-results 5 --query 'Events[].{time:EventTime,user:Username}'

# 10. KMS quota for symmetric cryptographic operations in this Region
aws service-quotas list-service-quotas --service-code kms \
  --query "Quotas[?contains(QuotaName,'symmetric')].[QuotaName,Value]"

#Summary

Key takeaways

  • In transit = TLS/HTTPS; at rest = server-side or client-side encryption. Encryption complements permissions; it does not replace them.
  • SSE-S3: S3-owned keys, AES-256, header AES256, default for every new upload since January 2023, free, no KMS, no key audit.
  • SSE-KMS: header aws:kms; S3 calls GenerateDataKey on upload and Decrypt on download; CloudTrail audit trail; reads need kms:Decrypt as well as s3:GetObject; multipart needs both KMS permissions; key in the same Region; cross-account needs a customer managed key (not aws/s3).
  • KMS quota (10,000 / 20,000 / 100,000 cryptographic operations per second per Region, shared by all services) can throttle busy SSE-KMS buckets. S3 Bucket Keys cut KMS calls by up to 99% (CloudTrail then shows the bucket ARN; existing objects need a copy); or request an increase in Service Quotas.
  • DSSE-KMS: header aws:kms:dsse, two independent AES-256 layers, for multi-layer compliance; more cost, no Bucket Keys.
  • SSE-C: your key in three headers on every request, HTTPS only, S3 keeps only a salted HMAC, no console, lose the key = lose the data; blocked by default on new buckets since April 2026 (unblock with BlockedEncryptionTypes = NONE).
  • Client-side: encrypt before upload (S3 Encryption Client, AWS Encryption SDK); S3 sees only ciphertext.
  • HTTPS only → bucket policy Deny when aws:SecureTransport is false; TLS 1.2 is the minimum on all AWS endpoints since 2024; s3:TlsVersion for stricter rules.
  • Default encryption covers uploads without a header, never existing objects (re-encrypt with Batch Operations). Bucket policies are evaluated before default encryption, so a “require header” policy rejects header-less uploads.