---
cuppy_hub: aws
cuppy_path: reference/s3/encryption.md
cuppy_section: docs
cuppy_doc: YVKG-Q7G2
cuppy_doc_uuid: c7c55b39-25f6-4e15-9cdc-b14be44d7acd
cuppy_permalink: https://cuppy.io/aws/d/YVKG-Q7G2
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/reference/s3/encryption
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 5452ffb259507b0fe721eab311f5b71712f51b13b96fed8f2d8acc0b12d353e6
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# S3 encryption

**Every object is encrypted at rest; you choose who holds the key.** S3 applies SSE-S3 (S3-managed keys) to every bucket by default. Choosing SSE-KMS adds a KMS key policy as a second gate on every read and write. SSE-C has been blocked by default since April 2026.

## At rest: what each option asks of the caller

| Option | Caller needs, beyond S3 permissions | Notes |
|---|---|---|
| **SSE-S3** (default) | Nothing | Base level for every bucket |
| **SSE-KMS, customer managed key** | `kms:GenerateDataKey` to upload; `kms:Decrypt` to download **and for multipart uploads** — allowed by the key policy and the caller's IAM policy (both sides when cross-account) | The key policy is a separate policy that must name the caller |
| **SSE-KMS, AWS managed key (`aws/s3`)** | The caller must be in the account that owns the key | Cannot be shared cross-account: use a customer managed key for cross-account data |
| **SSE-C** (customer-provided key) | Sends the key with each request | Blocked by default (below) |

Check what a bucket and an object use:

```
aws s3api get-bucket-encryption --bucket <bucket>
aws s3api head-object --bucket <bucket> --key <key>
```

KMS errors from S3 (400): `KMS.DisabledException`, `KMS.NotFoundException`, `KMS.KMSInvalidStateException`, `KMS.InvalidKeyUsageException` (`reference/s3/bucket-and-object-errors.md`).

Destinations such as SNS topics and SQS queues encrypted with an **AWS managed** key (`aws/sns`, `aws/sqs`) cannot receive S3 event notifications; use a customer managed key that allows `s3.amazonaws.com` (`patterns/s3-to-lambda.md`).

## Bucket Keys (SSE-KMS)

A Bucket Key lets S3 derive data keys from a short-lived bucket-level key instead of calling KMS for every object, cutting KMS request costs (AWS cites up to 99%).

- **Encryption context becomes the bucket ARN, not the object ARN.** IAM or key policies that condition on the object ARN in the encryption context stop matching. Update them before enabling.
- CloudTrail shows fewer KMS events, logged with the bucket ARN; access is still recorded at least once per requester.
- Requests served from the bucket-level key do not re-check the KMS key policy each time.
- Applies only to new objects; re-encrypt existing ones with `CopyObject`. Not supported with DSSE-KMS.

Enable as the bucket default:

```
aws s3api put-bucket-encryption --bucket <bucket> --server-side-encryption-configuration '{
  "Rules": [{
    "ApplyServerSideEncryptionByDefault": { "SSEAlgorithm": "aws:kms", "KMSMasterKeyID": "<key ARN>" },
    "BucketKeyEnabled": true
  }]
}'
```

Or per object with `--bucket-key-enabled` (header `x-amz-server-side-encryption-bucket-key-enabled`), which overrides the bucket setting. CloudFormation: `BucketKeyEnabled` on `AWS::S3::Bucket`.

## SSE-C blocked by default (April 2026)

From 2026-04-06, rolling out across Regions over several weeks:

- **New general purpose buckets:** SSE-C blocked.
- **Existing buckets in accounts with no SSE-C objects:** SSE-C blocked.
- **Accounts that already had SSE-C objects in any bucket:** existing buckets keep SSE-C.

A blocked SSE-C write (`PutObject`, `CopyObject`, POST, multipart, replication) returns 403 `AccessDenied` ending `because this bucket has blocked upload requests that specify Server Side Encryption with Customer provided keys (SSE-C). Please specify a different server-side encryption type`.

The control is `BlockedEncryptionTypes` in the bucket's encryption configuration (`PutBucketEncryption`, needs `s3:PutEncryptionConfiguration`): `SSE-C` blocks, `NONE` allows. Templates and scripts that depend on SSE-C must set it explicitly on new buckets.

## In transit

S3 supports TLS 1.2 and TLS 1.3 on all API endpoints; clients negotiate the strongest both support. TLS 1.3 is not available on AWS PrivateLink for S3 or Multi-Region Access Points. Server access logs and CloudTrail record the TLS version per request.

Enforce with explicit Deny statements in the bucket policy (also possible in SCPs, RCPs and VPC endpoint policies), each with `Principal` `"*"`, `Action` `s3:*`, and both the bucket ARN and `<bucket ARN>/*` as resources:

- **HTTPS only:** deny when `aws:SecureTransport` is `false` (`Bool`).
- **Minimum TLS version:** deny when `s3:TlsVersion` is numerically less than `1.2` (or `1.3`).

Because these are Deny statements, they override every Allow, including the caller's own.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
