Back

Blog details

SSE-S3 vs SSE-KMS vs SSE-C: S3 Encryption Types Explained

AIOZ Network
12 min readSeptember 12, 2026
aioz-storage
Server room aisle with rows of dark server racks

SSE-S3 vs SSE-KMS vs SSE-C comes down to one question asked three different ways: who actually holds the encryption key, and what does that choice cost you in exchange for who holds it. AWS's own documentation is direct that these are "mutually exclusive options," you pick exactly one per object, not a menu you combine. This article covers what each one actually does, a real, current example of the tradeoff mattering enough that AWS changed a default over it in 2026, and where AIOZ Storage's own simpler model lines up with this general framework.

TL;DR:

  • SSE-S3 is AWS's default: S3 manages the key entirely, zero setup, and every object uploaded to S3 has been encrypted this way automatically since January 2023.
  • SSE-KMS hands key management to AWS KMS instead, trading zero-setup simplicity for real auditability, per-key access policies, and a visible trail in CloudTrail.
  • SSE-C puts the key entirely in your hands, S3 never stores it, which is exactly why AWS disabled it by default for all new buckets starting April 2026, most workloads found the tradeoff impractical.

Four Options, One Mutually Exclusive Choice Per Object

AWS's own documentation names four server-side encryption options in total: SSE-S3, SSE-KMS, DSSE-KMS (a dual-layer variant of SSE-KMS), and SSE-C, and states plainly that "you can't apply different types of server-side encryption to the same object simultaneously." Every object gets exactly one. The three most commonly discussed, and the ones this article focuses on, differ entirely in who manages the actual encryption key and how much control or auditability that choice buys you.

SSE-S3: The Default, Zero-Setup Option

SSE-S3 is what happens if you do nothing at all. AWS states it directly: "server-side encryption with Amazon S3 managed keys (SSE-S3) is the default encryption configuration for every bucket in Amazon S3," and since January 5, 2023, "all new object uploads to Amazon S3 are automatically encrypted at no additional cost and with no impact on performance." AWS manages every part of the key lifecycle, "each object is encrypted with a unique key," which is itself further encrypted with "a root key that it regularly rotates," using AES-256. You never see or touch a key at any point.

SSE-KMS: More Control, More Auditability

SSE-KMS keeps AWS managing the underlying encryption operation but hands key management to AWS Key Management Service specifically, a real, meaningful step up in control. AWS states the difference plainly: "with AWS KMS, you have more control over your keys. For example, you can view separate keys, edit control policies, and follow the keys in AWS CloudTrail." That last part matters for any workload with a real audit or compliance requirement, since SSE-S3's fully-automatic key management leaves nothing to inspect, while SSE-KMS gives you a genuine, queryable trail of exactly which key touched which object and when.

Close-up of code displayed on a computer screen

SSE-C: You Hold the Key, You Hold the Risk

SSE-C flips the model entirely: you generate and hold the encryption key yourself, and AWS states the mechanism directly, "with the encryption key that you provide as part of your request, Amazon S3 manages data encryption as it writes to disks and data decryption when you access your objects." The critical detail, and the one that makes SSE-C fundamentally different from the other two, is what AWS states next: "S3 never stores the encryption key when you use SSE-C. You must supply the encryption key every time you want anyone to download your SSE-C encrypted data from S3."

That single fact cascades into everything else AWS warns about SSE-C directly: "you are responsible for tracking which encryption key you provided for which object... if you lose the encryption key, you lose the object," and separately, "this design can make it difficult to share your SSE-C key with other users, roles, or AWS services that need to operate on your data." SSE-C isn't a lesser version of the other two options, it's a genuinely different trust model, one where AWS's infrastructure never has the ability to read your data at all, at the cost of every operational safeguard, rotation, sharing, recovery, becoming entirely your own responsibility.

The 2026 Policy Change: SSE-C Now Off by Default

Here's a genuinely current data point that shows this tradeoff isn't just theoretical. AWS's own documentation states that "starting April 2026, SSE-C is disabled by default for all new general purpose buckets," specifically because, in AWS's own words, "most modern workloads use server-side encryption with Amazon S3 managed keys (SSE-S3) or AWS KMS keys (SSE-KMS) instead, because SSE-C requires you to provide the encryption key with every request, making it impractical to share access with other users, roles, or AWS services that operate on your data." Anyone who genuinely needs SSE-C today has to deliberately re-enable it through the PutBucketEncryption API rather than getting it by default, a real signal about how the industry-wide calculus on this tradeoff has settled.

The scope is wider than "new buckets" alone. AWS's FAQ on the change states that if an account had no SSE-C encrypted objects, S3 also disabled SSE-C on all of that account's existing buckets, while accounts that did have SSE-C objects were left untouched. While SSE-C is blocked, any PutObject, CopyObject, PostObject, multipart upload or replication request that specifies it is rejected with an HTTP 403 AccessDenied. To allow it, you set BlockedEncryptionTypes to NONE in the bucket's default encryption configuration. That's a switch that permits SSE-C uploads, not a way to make SSE-C the bucket default: AWS is explicit that PutBucketEncryption doesn't support setting SSE-C as a default method, because the key has to arrive with every request.

How Each Type Is Requested

The three types look different on the wire, and that's where a lot of real-world errors come from.

TypeHow you ask for it
SSE-S3Nothing, it's the default. To be explicit, send the x-amz-server-side-encryption header.
SSE-KMSx-amz-server-side-encryption: aws:kms, optionally with x-amz-server-side-encryption-aws-kms-key-id naming a specific key
SSE-CThree headers on every request: x-amz-server-side-encryption-customer-algorithm (must be AES256), -customer-key (the 256-bit key, base64-encoded), and -customer-key-MD5 (the base64-encoded 128-bit MD5 digest of the key, used as an integrity check)

Two rules trip people up. For SSE-KMS objects, AWS says not to send encryption request headers on GET or HEAD requests, because doing so returns an HTTP 400 Bad Request; S3 already knows which key to ask KMS for. For SSE-C it's the opposite: GET, HEAD and copy requests must all carry the key, and a copy from an SSE-C source needs a second set of x-amz-copy-source-server-side-encryption-customer-* headers for the source object's key.

Both KMS and SSE-C requests must travel over TLS. AWS states that all GET and PUT requests for KMS-encrypted objects must use SSL or TLS and be signed with valid credentials such as Signature Version 4. For SSE-C, S3 rejects requests made over plain HTTP, and AWS recommends treating "any key that you erroneously send over HTTP" as compromised: discard it and rotate as appropriate.

How SSE-KMS Works: Envelope Encryption

SSE-KMS doesn't send your object to KMS. Instead S3 uses envelope encryption, which AWS describes as encrypting your data with a data key and then encrypting that data key with a KMS key. On upload:

  1. S3 asks KMS for a plaintext data key and a copy of it encrypted under your KMS key.
  2. KMS generates the data key, encrypts it under the KMS key, and returns both.
  3. S3 encrypts the object with the plaintext data key and removes that key from memory as soon as possible.
  4. S3 stores the encrypted data key as metadata alongside the encrypted object.

On download, S3 sends the encrypted data key back to KMS in a Decrypt request, gets the plaintext data key, decrypts the object, and again discards the key. The consequence for access control is that reading an SSE-KMS object needs two permissions to line up: S3 access to the object, and KMS access to the key. AWS lists them explicitly: kms:GenerateDataKey to upload, kms:Decrypt to download, and both for a multipart upload. A user with perfect S3 permissions and no KMS permission gets Access Denied on a KMS-encrypted object, which is a frequent surprise when it first happens.

This is also where SSE-KMS's audit trail comes from. Every GenerateDataKey and Decrypt call is a KMS operation that CloudTrail can record, so you can see which principals touched which keys, something SSE-S3's fully managed keys don't expose.

AWS Managed Key vs Customer Managed Key

"SSE-KMS" hides a second choice: which KMS key. If you don't specify one, S3 creates an AWS managed key (aws/s3) in your account the first time you add an SSE-KMS object to a bucket and uses that. You can instead point to a customer managed key you've created, which AWS says gives "more flexibility and control": you can create, rotate and disable it, define its access policy, and audit it.

The difference matters most for sharing. AWS states that objects encrypted with SSE-KMS using AWS managed keys can't be shared cross-account; if you need cross-account access, you must use a customer managed key and configure its key policy to allow the other account. A few related details from the same documentation:

  • If you name a customer managed key, use its fully qualified ARN. A key alias is resolved within the requester's account, which can leave data encrypted under the requester's key instead of the bucket owner's.
  • KMS keys used for SSE-KMS must be in the same Region as the bucket, and S3 supports only symmetric encryption KMS keys.
  • There are additional AWS KMS charges on top of S3's own.

Controlling KMS Cost: S3 Bucket Keys

Because plain SSE-KMS makes KMS requests per object, high-volume buckets can generate a lot of KMS traffic and cost. S3 Bucket Keys address that: AWS says using a bucket-level key "can reduce your AWS KMS request costs by up to 99 percent by decreasing the request traffic from Amazon S3 to AWS KMS." With a Bucket Key enabled, KMS generates one bucket-level key, and S3 derives the unique per-object data keys from it for a limited time instead of calling KMS each time. There's a visible side effect for auditing: the CloudTrail encryption context becomes the bucket ARN instead of the object ARN, so KMS events reference the bucket rather than each individual object.

One more interaction worth knowing: a bucket that receives S3 server access logs must use SSE-S3 for default encryption. AWS warns that if the destination bucket uses SSE-KMS, log objects can be delivered encrypted with a key you can't access, which ties directly to how S3 access logs work.

Enforcing Encryption With a Bucket Policy

Rather than trusting every uploader to send the right headers, you can reject uploads that don't. AWS's example denies s3:PutObject for requests that don't include the KMS key header:

{
  "Version": "2012-10-17",
  "Id": "PutObjectPolicy",
  "Statement": [
    {
      "Sid": "DenyObjectsThatAreNotSSEKMS",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "Null": {
          "s3:x-amz-server-side-encryption-aws-kms-key-id": "true"
        }
      }
    }
  ]
}

The Null condition matches when the header is absent, so the deny fires on any upload that doesn't specify a KMS key. If you want one particular key enforced, compare the key ARN with the same condition key, using the full arn:aws:kms:region:account-id:key/key-id form. AWS notes IAM doesn't check that the string refers to a real key. The same pattern works for SSE-C using the s3:x-amz-server-side-encryption-customer-algorithm condition key, and if you enforce it, the header has to be on every multipart request too: CreateMultipartUpload, UploadPart and CompleteMultipartUpload.

SSE-C in Practice: The Operational Costs

The SSE-C limits AWS documents are what make it hard to run day to day:

  • S3 keeps only a salted HMAC of your key, which it uses to validate later requests. That value can't be used to derive the key or decrypt the object, which is exactly why a lost key means a lost object.
  • With versioning on, each object version can have its own key, and you have to track which key belongs to which version.
  • The S3 console doesn't support it. You can't upload with SSE-C or update an SSE-C object's storage class or metadata from the console.
  • AWS managed services can't natively decrypt SSE-C objects, since they'd need the key on every request.
  • Presigned URLs work only programmatically. The SSE-C headers have to be included, so you can't paste the URL into a browser the way you can with a normal presigned URL.

Each of those follows from the same design fact: the key never lives in S3. That's the appeal for a strict "provider can't read this" model, and it's the reason most workloads pick SSE-KMS or SSE-S3 instead.

Where AIOZ Storage Fits

AIOZ Storage's own encryption model doesn't map onto these three AWS options directly, it's built around two different modes, server-side (the default, comparable in spirit to SSE-S3) and end-to-end (requiring a self-hosted gateway). But the underlying trust tradeoff SSE-C represents, "the provider's infrastructure never holds a key that could decrypt your data, and losing your own key means losing your data permanently," is exactly the same tradeoff AIOZ Storage's own zero-knowledge passphrase model makes. AIOZ's documentation states this directly: it doesn't store your bucket passphrase under either encryption mode, and losing it with no backup means the bucket's contents are unrecoverable. That's SSE-C's core risk profile, worth recognizing as the same shape of tradeoff even though AIOZ Storage doesn't implement AWS's specific SSE-C mechanism.

Frequently Asked Questions

What's the difference between SSE-S3, SSE-KMS, and SSE-C?
Who manages the encryption key. SSE-S3 means AWS manages it entirely with zero setup. SSE-KMS hands key management to AWS KMS for more control and auditability. SSE-C means you generate and hold the key yourself, and AWS never stores a copy.

Which encryption type does S3 use by default?
SSE-S3. AWS states that every object uploaded to S3 has been automatically encrypted this way, at no additional cost, since January 5, 2023.

What happens if you lose your SSE-C encryption key?
You lose the object permanently. AWS states directly that S3 never stores the SSE-C key, so there's no recovery path if you lose it, the same risk profile as AIOZ Storage's own bucket passphrase.

Why did AWS disable SSE-C by default starting in 2026?
Because most workloads found it impractical: SSE-C requires supplying the encryption key on every single request, which makes sharing access with other users, roles, or AWS services difficult. AWS's own documentation states this directly as the reason for the default change.

What does SSE-KMS offer that SSE-S3 doesn't?
Real auditability and control: separate viewable keys, editable key policies, and a queryable trail in AWS CloudTrail showing which key touched which object. SSE-S3's fully automatic key management leaves nothing to inspect by comparison.

Can you apply more than one encryption type to the same object?
No. AWS states these are mutually exclusive options, each object gets exactly one server-side encryption type at a time.

Why do I get Access Denied on an SSE-KMS object when my S3 permissions are correct?
Reading an SSE-KMS object needs KMS permission on the key as well as S3 permission on the object. AWS lists kms:GenerateDataKey for uploads, kms:Decrypt for downloads, and both for multipart uploads. Missing KMS permission returns access denied even with perfect S3 permissions.

What is envelope encryption in SSE-KMS?
S3 encrypts the object with a data key, and KMS encrypts that data key under your KMS key. The encrypted data key is stored with the object as metadata, and S3 asks KMS to decrypt it whenever the object is read. The object data itself is never sent to KMS.

Can I share SSE-KMS encrypted objects with another AWS account?
Only if they use a customer managed KMS key with a key policy that allows the other account. AWS states objects encrypted with AWS managed keys can't be shared cross-account.

What are S3 Bucket Keys?
A bucket-level key used to generate per-object data keys for a limited time, so S3 makes far fewer requests to KMS. AWS says it can reduce KMS request costs by up to 99 percent.

Can I make SSE-C the default encryption for a bucket?
No. PutBucketEncryption can't set SSE-C as a default, because SSE-C requires the key on every request. You can only allow or block SSE-C uploads for the bucket through BlockedEncryptionTypes.

Do I need HTTPS for SSE-C requests?
Yes. S3 rejects SSE-C requests made over HTTP, and AWS recommends treating any key sent over HTTP as compromised.

Does AIOZ Storage use SSE-S3, SSE-KMS, or SSE-C?
None of AWS's specific mechanisms. AIOZ Storage runs its own two-mode model, server-side and end-to-end, but its zero-knowledge passphrase approach shares SSE-C's core trust tradeoff: the provider never holds a recoverable key, and losing it yourself means losing the data.

References

We only send updates when meaningful changes ship, and you can unsubscribe anytime

Related Content

blog thumbnail

AIOZ Storage Super Intelligence Memory: Record and Recall

AIOZ Storage Super Intelligence Memory gives AI agents memory: Record a statement, Recall it by meaning, with no embeddings or vector index to manage yourself.

aioz-storage
7 min readSeptember 29, 2026
blog thumbnail

Static Website Hosting on AIOZ Storage: Full Setup Guide

Static website hosting on AIOZ Storage turns any bucket into a live site with a custom domain. Here is the full setup, from bucket to DNS verification.

aioz-storage
4 min readSeptember 25, 2026
blog thumbnail

What Is an Embedding Model? How AI Turns Text Into Vectors

An embedding model turns text, images, or audio into vectors so similar meanings land close together in space. Here is how embedding models actually work.

aioz-storage
5 min readSeptember 24, 2026
blog thumbnail

What Is Retrieval-Augmented Generation (RAG)? Explained

Retrieval-augmented generation (RAG) grounds an LLM answer in your own data, not its training set. Here is how the retrieval and generation steps work.

aioz-storage
5 min readSeptember 22, 2026
blog thumbnail

How to Build an MCP Server for AIOZ Storage, Step by Step

AIOZ Storage has no official MCP server yet. Here is a working MCP server for AIOZ Storage, built with the official Python SDK and boto3, tools included.

aioz-storage
6 min readSeptember 19, 2026
blog thumbnail

What Is an MCP Server? How AI Agents Access Storage

What is an MCP server: a standard letting AI agents like Claude use tools and data through one protocol. How MinIO and Azure apply it to object storage.

aioz-storage
6 min readSeptember 16, 2026