This document describes MinIO's server-side encryption (SSE) implementation, covering encryption types, key management, object encryption/decryption workflows, and integration with Key Management Systems (KMS). For information about the IAM system that controls access to encrypted objects, see Identity and Access Management.
MinIO supports three server-side encryption modes compatible with the AWS S3 encryption API:
Encryption is transparent to clients after the initial request. Objects are encrypted at rest using AES-256-GCM, and encryption keys are sealed using either customer-provided keys (SSE-C) or keys managed by an external KMS (SSE-S3/SSE-KMS).
Sources: cmd/encryption-v1.go49-61 cmd/api-errors.go222-242
SSE-C requires clients to provide encryption keys with each request. The key is transmitted via HTTP headers and never stored on the server.
Request Headers:
X-Amz-Server-Side-Encryption-Customer-Algorithm: Must be AES256X-Amz-Server-Side-Encryption-Customer-Key: Base64-encoded 32-byte keyX-Amz-Server-Side-Encryption-Customer-Key-MD5: MD5 of the key for validationResponse Headers:
X-Amz-Server-Side-Encryption-Customer-Algorithm: Echoed back as AES256X-Amz-Server-Side-Encryption-Customer-Key-MD5: Echoed back for verificationSSE-C requests are rejected unless made over TLS to protect key material in transit.
Sources: cmd/generic-handlers.go450-469 cmd/object-handlers.go516-519
SSE-S3 uses a KMS to generate and manage encryption keys automatically. Clients request encryption by setting a single header.
Request Headers:
X-Amz-Server-Side-Encryption: Must be AES256Response Headers:
X-Amz-Server-Side-Encryption: Set to AES256The server automatically generates a unique data encryption key (DEK) for each object and seals it using the KMS.
Sources: cmd/object-handlers.go281-282 cmd/object-handlers.go508-509
SSE-KMS is similar to SSE-S3 but allows clients to specify a particular KMS key ID and optional encryption context.
Request Headers:
X-Amz-Server-Side-Encryption: Must be aws:kmsX-Amz-Server-Side-Encryption-Aws-Kms-Key-Id: ARN or ID of the KMS keyX-Amz-Server-Side-Encryption-Context: Optional base64-encoded JSON contextResponse Headers:
X-Amz-Server-Side-Encryption: Set to aws:kmsX-Amz-Server-Side-Encryption-Aws-Kms-Key-Id: KMS key ARNX-Amz-Server-Side-Encryption-Context: Encryption context if providedSources: cmd/object-handlers.go283-288 cmd/object-handlers.go510-515
Sources: cmd/encryption-v1.go63-76 cmd/encryption-v1.go89-101
For SSE-C, the client-provided key is never stored. Instead, MinIO derives a unique object encryption key (OEK) using HMAC with the bucket and object name as context. This derived key is sealed and stored in the object metadata.
Sources: cmd/encryption-v1.go566-601 cmd/object-api-options.go64-72
For SSE-S3 and SSE-KMS, MinIO generates a random data encryption key (DEK), seals it using the KMS, and stores the sealed key in the object's metadata. The DEK encrypts the actual object data.
Sources: cmd/encryption-v1.go436-481 cmd/encryption-v1.go522-563
During a PUT operation, MinIO:
sio.EncryptReader using the derived keyThe encryption is done in a streaming fashion using the sio library, which implements the DARE (Data At Rest Encryption) format with 64 KiB packages.
Sources: cmd/encryption-v1.go522-563 cmd/encryption-v1.go602-652
For multipart uploads, each part is encrypted independently using the same object encryption key (OEK). The OEK is derived or generated during InitiateMultipartUpload and stored in the upload metadata.
Workflow:
NewMultipartUpload(): Generate/derive OEK, seal it, store in upload metadataPutObjectPart(): Encrypt each part with the same OEKCompleteMultipartUpload(): Combine encrypted parts, preserve metadataSources: cmd/encryption-v1.go719-783 cmd/encryption-v1.go827-873
During a GET operation:
Sources: cmd/object-handlers.go313-578 cmd/encryption-v1.go216-257
Encrypted objects have special ETag handling:
{hash}-{partCount})The DecryptETags() function processes batches of objects efficiently using bulk KMS decryption when available.
Sources: cmd/encryption-v1.go103-208 cmd/object-handlers.go520-521
MinIO integrates with external Key Management Systems through the kms.KMS interface. The KMS is used to:
Sources: cmd/encryption-v1.go436-481 cmd/encryption-v1.go85-101
For operations that need to decrypt multiple objects (e.g., ListObjects with metadata), MinIO uses bulk decryption APIs:
This reduces the number of KMS calls and improves performance for listing operations.
Sources: cmd/encryption-v1.go113-178
SSE-C encrypted objects cannot be directly replicated because the customer key is never stored. Instead, MinIO uses special internal headers to replicate the sealed encryption metadata:
Internal to Replication Header Mapping:
X-Minio-Internal-Server-Side-Encryption-Sealed-Key → X-Minio-Replication-Server-Side-Encryption-Sealed-KeyX-Minio-Internal-Server-Side-Encryption-Seal-Algorithm → X-Minio-Replication-Server-Side-Encryption-Seal-AlgorithmX-Minio-Internal-Server-Side-Encryption-Iv → X-Minio-Replication-Server-Side-Encryption-IvX-Minio-Internal-Encrypted-Multipart → X-Minio-Replication-Encrypted-MultipartX-Minio-Internal-Actual-Object-Size → X-Minio-Replication-Actual-Object-SizeFor SSE-C objects, the encrypted checksum is transmitted via:
X-Minio-Replication-Ssec-Crc: Encrypted CRC of the objectSources: cmd/handler-utils.go96-114 cmd/bucket-replication.go800-814
For SSE-S3 and SSE-KMS objects, the sealed key metadata is replicated along with the encrypted object data. The target cluster must have access to the same KMS or a compatible key to unseal the keys.
Sources: cmd/bucket-replication.go778-873
| Error Code | Description | HTTP Status |
|---|---|---|
ErrInsecureSSECustomerRequest | SSE-C request over non-TLS connection | 400 Bad Request |
ErrInvalidSSECustomerAlgorithm | Invalid algorithm (must be AES256) | 400 Bad Request |
ErrInvalidSSECustomerKey | Invalid customer key format | 400 Bad Request |
ErrMissingSSECustomerKey | SSE-C key not provided | 400 Bad Request |
ErrMissingSSECustomerKeyMD5 | SSE-C key MD5 not provided | 400 Bad Request |
ErrSSECustomerKeyMD5Mismatch | Key MD5 doesn't match | 400 Bad Request |
ErrInvalidEncryptionMethod | Invalid encryption method | 400 Bad Request |
ErrKMSNotConfigured | KMS not configured for SSE-S3/SSE-KMS | 500 Internal Error |
ErrKMSKeyNotFoundException | Specified KMS key not found | 400 Bad Request |
ErrIncompatibleEncryptionMethod | Conflicting encryption headers | 400 Bad Request |
Sources: cmd/api-errors.go227-242 cmd/encryption-v1.go49-61
SSE-C requests are explicitly rejected if not made over TLS:
if !globalIsTLS && (crypto.SSEC.IsRequested(r.Header) || crypto.SSECopy.IsRequested(r.Header)) {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInsecureSSECustomerRequest), r.URL)
return
}
This validation occurs in the request validity middleware before any handler is invoked.
Sources: cmd/generic-handlers.go450-469
MinIO uses the Data At Rest Encryption (DARE) format implemented by the sio library:
This format enables:
Sources: cmd/encryption-v1.go71-76 cmd/encryption-v1.go602-652
Encryption metadata is stored in the object's UserDefined map with reserved keys:
Sources: cmd/encryption-v1.go436-481 cmd/object-api-options.go35-132
Encrypted objects are slightly larger than their plaintext counterparts due to DARE package overhead:
encrypted_size = plaintext_size + (num_packages * SSEDAREPackageMetaSize)
where num_packages = ceil(plaintext_size / SSEDAREPackageBlockSize)
The GetActualSize() method reverses this calculation to return the original plaintext size.
Sources: cmd/encryption-v1.go260-302
Refresh this wiki