- 08 Oct 2026
- Print
- DarkLight
Use Server-Side Encryption with Customer-Managed Keys (SSE-C)
- Updated on 08 Oct 2026
- Print
- DarkLight
Backblaze B2 automatically encrypts new object data at rest with Server-Side Encryption using Backblaze-managed keys (SSE-B2). Use Server-Side Encryption with Customer-Managed Keys (SSE-C) when your organization must supply and control the AES-256 key for an individual object. You must provide the same key with every protected request, and Backblaze cannot recover the object if you lose the key.
SSE-C is also called bring your own key (BYOK). It gives a reseller, MSP, or multi-tenant platform control of key custody while Backblaze performs the server-side encryption and decryption.
Prerequisites
Before you begin, ensure that you have the following items:
A Backblaze account with Backblaze B2 enabled
A private Backblaze B2 bucket and its S3 endpoint
An Application Key with access to the bucket and the operations in this guide
Python 3 with
boto3for the S3-Compatible API examplesA secrets-management system or external key-management system that can store tenant encryption keys
A secure record that maps each object or tenant to the correct key version
A tested key backup, recovery, and rotation process
Do not store encryption keys in source code, object names, bucket names, unencrypted object metadata, logs, or support tickets.
Compare SSE-C and SSE-B2
Both options use AES-256 to encrypt object data at rest. The main difference is who manages the key and who is responsible for making it available when the object is accessed.
Consideration | SSE-B2 | SSE-C |
|---|---|---|
Key custody | Backblaze manages the encryption keys. | You generate, store, protect, and supply the customer key. |
Default behavior | Backblaze automatically applies SSE-B2 to new uploads and destination copies when a request does not specify SSE-C. | You select SSE-C for each object request. It cannot be configured as the bucket default. |
Key in requests | You do not provide an encryption key. | You provide the AES-256 key when you upload, download, copy, or otherwise access the encrypted object. |
What Backblaze stores | Backblaze stores the information required to manage and use the encryption key. | Backblaze does not store the customer key. It stores a secure hash that can validate later requests but cannot recover the key. |
Rotation | Backblaze manages the encryption keys. | You must copy or re-upload the object with a new key. |
Lost key | Backblaze manages key availability. | The object cannot be decrypted or recovered without the correct customer key. |
Recommended use | Use for most workloads that need automatic encryption at rest. | Use when a customer, tenant, or security policy requires customer custody of the encryption key. |
Understand How SSE-C Works
SSE-C is an object-level encryption method. For each protected operation, your application sends the customer key over a TLS-encrypted connection. Backblaze uses the key to encrypt or decrypt the object during the request and then discards the key.
Backblaze therefore receives and uses the key during the request, but it does not retain the key. If your security model requires the storage provider never to receive the unwrapped encryption key, use client-side or envelope encryption instead of SSE-C.
SSE-C protects object data at rest. It does not encrypt object names, bucket names, file names, or other object metadata. Do not include personally identifiable information, protected health information, or other sensitive data in those fields.
Configure the S3-Compatible Client
Use the Backblaze B2 S3 endpoint for your account region and a bucket-scoped Application Key. Store the credentials and the SSE-C key separately.
export B2_APPLICATION_KEY_ID="<application-key-id>"
export B2_APPLICATION_KEY="<application-key>"
export B2_S3_ENDPOINT="https://s3.<region>.backblazeb2.com"
export B2_BUCKET_NAME="<bucket-name>"
export B2_SSE_C_KEY_BASE64="<base64-encoded-32-byte-key>"The B2_SSE_C_KEY_BASE64 value represents a 32-byte AES-256 key. Retrieve this value from your secrets-management system at runtime. Do not generate a new key each time the application starts.
Upload and Download an SSE-C Object
The following Python example uses the S3-Compatible API. The AWS SDK for Python calculates the key MD5 value and signs the request.
import base64
import os
import boto3
bucket_name = os.environ["B2_BUCKET_NAME"]
customer_key = base64.b64decode(os.environ["B2_SSE_C_KEY_BASE64"])
if len(customer_key) != 32:
raise ValueError("B2_SSE_C_KEY_BASE64 must decode to exactly 32 bytes")
s3 = boto3.client(
"s3",
endpoint_url=os.environ["B2_S3_ENDPOINT"],
aws_access_key_id=os.environ["B2_APPLICATION_KEY_ID"],
aws_secret_access_key=os.environ["B2_APPLICATION_KEY"],
)
object_key = "tenants/tenant-123/example.txt"
s3.put_object(
Bucket=bucket_name,
Key=object_key,
Body=b"Example tenant data",
SSECustomerAlgorithm="AES256",
SSECustomerKey=customer_key,
)
response = s3.get_object(
Bucket=bucket_name,
Key=object_key,
SSECustomerAlgorithm="AES256",
SSECustomerKey=customer_key,
)
print(response["Body"].read().decode("utf-8"))The upload and download use the same 32-byte key. A download with an incorrect key fails, and a download without the SSE-C headers cannot decrypt the object.
Map the S3 SSE-C Request Headers
S3-Compatible API clients send the following headers. SDKs such as boto3 construct them from the SSE-C parameters.
Operation | Required SSE-C headers | Purpose |
|---|---|---|
Upload an object |
| Selects AES-256, supplies the customer key, and verifies that the key arrived intact. |
Download or inspect an object |
| Supplies the same key that was used for the upload. |
Copy an SSE-C source |
| Decrypts the source object during the copy. |
Encrypt a copied destination with SSE-C |
| Encrypts the destination object with the destination customer key. |
The algorithm value is AES256. For direct HTTP requests, the key is a base64-encoded 32-byte AES-256 key, and the MD5 value is the base64-encoded MD5 digest of the raw key.
Use SSE-C with the Backblaze B2 Native API
Use the following headers when you upload or download an SSE-C object with the Backblaze B2 Native API:
X-Bz-Server-Side-Encryption-Customer-Algorithm: AES256
X-Bz-Server-Side-Encryption-Customer-Key: <base64-encoded-32-byte-key>
X-Bz-Server-Side-Encryption-Customer-Key-Md5: <base64-encoded-MD5-of-the-raw-key>The following example uploads an object with an upload URL and authorization token returned by b2_get_upload_url:
curl --request POST "$B2_UPLOAD_URL" \
--header "Authorization: $B2_UPLOAD_AUTH_TOKEN" \
--header "X-Bz-File-Name: tenants/tenant-123/example.txt" \
--header "Content-Type: text/plain" \
--header "X-Bz-Content-Sha1: $FILE_SHA1" \
--header "X-Bz-Server-Side-Encryption-Customer-Algorithm: AES256" \
--header "X-Bz-Server-Side-Encryption-Customer-Key: $B2_SSE_C_KEY_BASE64" \
--header "X-Bz-Server-Side-Encryption-Customer-Key-Md5: $B2_SSE_C_KEY_MD5_BASE64" \
--data-binary @example.txtProvide the same SSE-C headers when you download the object:
curl --output example.txt \
--header "Authorization: $B2_AUTH_TOKEN" \
--header "X-Bz-Server-Side-Encryption-Customer-Algorithm: AES256" \
--header "X-Bz-Server-Side-Encryption-Customer-Key: $B2_SSE_C_KEY_BASE64" \
--header "X-Bz-Server-Side-Encryption-Customer-Key-Md5: $B2_SSE_C_KEY_MD5_BASE64" \
"$B2_DOWNLOAD_URL/file/$B2_BUCKET_NAME/tenants/tenant-123/example.txt"Large files select their encryption mode when you call b2_start_large_file. All uploaded or copied parts inherit that encryption selection.
Design Key Management for a Multi-Tenant Platform
Choose a key boundary that matches the isolation, recovery, and operational requirements of your service.
Key design | Advantages | Tradeoffs | Typical use |
|---|---|---|---|
One key per tenant | Separates tenant key custody and lets you rotate or revoke one tenant independently. | You must maintain a reliable tenant-to-key-version mapping. | Multi-tenant platforms, resellers, and MSP services |
One key per dataset or sensitivity class | Limits the effect of a compromised key and supports different rotation schedules. | Creates more keys and more complex recovery procedures. | Regulated datasets or workloads with distinct security requirements |
One key per object | Provides the smallest possible key scope. | Requires extensive key indexing, backup, availability, and recovery infrastructure. | Specialized high-isolation systems |
Store tenant keys in an external KMS or secrets-management system. In your control-plane database, record an opaque tenant identifier, the object scope, and the applicable key version. Do not store the unwrapped customer key in the database record or in B2 object metadata.
Keep data-access credentials and encryption keys separate. An Application Key authorizes access to B2, while the SSE-C key encrypts and decrypts the object. Possession of only one of these credentials should not provide complete access to private object data.
Rotate and Re-Encrypt SSE-C Objects
SSE-C does not rotate keys automatically. To change an object's customer key, create a new object version by copying or re-uploading the data. Supply the old key for the source object and the new key for the destination object.
import base64
import os
import boto3
old_key = base64.b64decode(os.environ["B2_OLD_SSE_C_KEY_BASE64"])
new_key = base64.b64decode(os.environ["B2_NEW_SSE_C_KEY_BASE64"])
if len(old_key) != 32 or len(new_key) != 32:
raise ValueError("Each SSE-C key must be exactly 32 bytes")
s3 = boto3.client(
"s3",
endpoint_url=os.environ["B2_S3_ENDPOINT"],
aws_access_key_id=os.environ["B2_APPLICATION_KEY_ID"],
aws_secret_access_key=os.environ["B2_APPLICATION_KEY"],
)
bucket_name = os.environ["B2_BUCKET_NAME"]
object_key = "tenants/tenant-123/example.txt"
s3.copy_object(
Bucket=bucket_name,
Key=object_key,
CopySource={"Bucket": bucket_name, "Key": object_key},
CopySourceSSECustomerAlgorithm="AES256",
CopySourceSSECustomerKey=old_key,
SSECustomerAlgorithm="AES256",
SSECustomerKey=new_key,
)Use the following rotation sequence:
Create and securely store the new customer key.
Copy or re-upload each object with the old source key and the new destination key.
Verify that the new object version can be downloaded with the new key.
Update your object-to-key-version mapping.
Retain the old key while any required object version still uses it.
Delete the old key only after all required versions have been re-encrypted or have expired.
For large objects, use multipart copy or download and re-upload the object. If Object Lock protects an older version, retain the old key for as long as you must be able to read that version.
Use Client-Side or Envelope Encryption
Use client-side or envelope encryption when your platform must encrypt the data before it reaches Backblaze or when the storage provider must never receive the unwrapped encryption key.
In a typical envelope-encryption design, your application generates a data encryption key, encrypts the object locally, and then encrypts the data key with a key encryption key stored in an external KMS. Upload only the encrypted object to B2. Store the wrapped data key and key reference according to your security design.
Client-side encryption gives your platform full responsibility for encryption, decryption, algorithm selection, key availability, rotation, and recovery. Backblaze cannot inspect or recover client-side-encrypted object contents.
Understand the Limits of SSE-C
SSE-C is configured for individual objects. You cannot set SSE-C as a bucket's default encryption method.
You must supply the correct customer key for every protected upload, download, copy, or metadata request that requires decryption.
Backblaze receives the key during the request. Use client-side encryption if the provider must never receive it.
Backblaze does not store or recover SSE-C keys. Losing a key makes the associated object data unrecoverable.
SSE-C encrypts object data at rest, but it does not encrypt object names or other file metadata.
Backblaze does not provide an AWS KMS-style managed key service for B2 SSE-C keys. Use an external KMS or secrets-management system.
SSE-C objects are not available for direct download through the Browse Files page and are not included in snapshots created from that page.
Backblaze Cloud Replication does not support SSE-C objects.
Some S3-compatible integrations do not support supplying SSE-C keys on every required operation. Confirm support before you select SSE-C.
Frequently Asked Questions
Does Backblaze ever see my SSE-C key?
Yes. Your application sends the key to Backblaze over TLS with each protected request. Backblaze uses the key during the request and then discards it. Backblaze stores a secure hash that validates future requests but cannot be used to derive the original key or decrypt the object.
What is the difference between SSE-C and SSE-B2?
With SSE-B2, Backblaze manages the encryption keys and automatically encrypts new object data at rest. With SSE-C, you supply and manage the AES-256 key for each protected object request. Use SSE-C only when your security model requires customer control of the encryption key.
How do I rotate an SSE-C key?
Copy or re-upload the object by using the old key to decrypt the source and the new key to encrypt the destination. Verify the new object version and retain the old key until you no longer need to read any version encrypted with it.
Troubleshooting
Review the following issues if an SSE-C request fails or an encrypted object is unavailable.
The download returns a 403 error
Confirm that the request supplies the same SSE-C key that was used to encrypt the object. Backblaze returns an error when the key does not match.
The application cannot access an SSE-C object
Confirm that the integration sends all required SSE-C parameters on uploads, downloads, copies, and metadata requests. Some S3-compatible applications support SSE-B2 but do not support SSE-C.
A replicated object has a failed status
Backblaze Cloud Replication does not support SSE-C. Use SSE-B2 for objects that must use Cloud Replication, or implement a client-controlled replication workflow that decrypts and re-encrypts the data.
The key was lost or deleted
Backblaze cannot recover an SSE-C key or decrypt the object without it. Restore the key from your secured backup. If no copy of the correct key exists, the object data cannot be recovered.
Additional Resources
Help us improve this guide. If you find an error, notice outdated information, or have suggestions for improvement, email techpubs@backblaze.com.