Encrypting data using SSE-C
With SSE-C (Server-Side Encryption with Customer-provided Keys), the storage service encrypts your objects on the server side using AES-256 – but with a key that only you possess. The key is sent with every request, is used exclusively in the server’s RAM and is not stored.
Key features
- Without the key, the objects are permanently unreadable – there is no way to recover them via centron.
- The key must be provided with every access (
PUT,GET,HEAD,COPY); even object listings only display metadata. - Transmission must take place via HTTPS.
- Alternative with full client control: client-side encryption (e.g. restic/Kopia encrypt the data themselves – SSE-C is not required in this case).
Loss of key = loss of data
Store the key securely and in multiple locations (password manager, secret store). Losing an SSE-C key will result in irretrievable data loss.
Generate key
An SSE-C key is a random 256-bit value (32 bytes, Base64-encoded):
openssl rand -base64 32 > ssec.key
cat ssec.key
# z. B.: 5FZDMoAyVqzUmYOMzR3xkJXnxSTdVk9fQZfSrYupdrQ=
Using it with the AWS CLI
KEY=$(cat ssec.key)
# Upload mit SSE-C
aws s3 cp geheim.pdf s3://mein-bucket/geheim.pdf \
--sse-c AES256 --sse-c-key "$KEY" \
--endpoint-url https://s3.internet1.de
# Download – derselbe Schlüssel ist erforderlich
aws s3 cp s3://mein-bucket/geheim.pdf ./geheim.pdf \
--sse-c AES256 --sse-c-key "$KEY" \
--endpoint-url https://s3.internet1.de
# Zugriff ohne Schlüssel schlägt fehl (HTTP 400)
aws s3 cp s3://mein-bucket/geheim.pdf ./test.pdf \
--endpoint-url https://s3.internet1.de
When copying encrypted objects on the server side, also specify the source key (--copy-source-sse-c-key).
Using boto3 (Python)
import base64, os, boto3
key = base64.b64decode(open("ssec.key").read().strip())
s3 = boto3.client(
"s3",
endpoint_url="https://s3.internet1.de",
region_name=" ",
aws_access_key_id=os.environ["S3_ACCESS_KEY"],
aws_secret_access_key=os.environ["S3_SECRET_KEY"],
)
s3.put_object(
Bucket="mein-bucket", Key="geheim.pdf",
Body=open("geheim.pdf", "rb"),
SSECustomerAlgorithm="AES256",
SSECustomerKey=key,
)
obj = s3.get_object(
Bucket="mein-bucket", Key="geheim.pdf",
SSECustomerAlgorithm="AES256",
SSECustomerKey=key,
)
Using curl
The API expects three headers: the algorithm, the Base64-encoded key and the MD5 checksum of the key:
KEY_B64=$(cat ssec.key)
KEY_MD5=$(echo -n "$KEY_B64" | base64 -d | openssl md5 -binary | base64)
curl -X PUT "https://s3.internet1.de/mein-bucket/geheim.pdf" \
--user "IHR_ACCESS_KEY:IHR_SECRET_KEY" \
--aws-sigv4 "aws:amz:de-central-1:s3" \
-H "x-amz-server-side-encryption-customer-algorithm: AES256" \
-H "x-amz-server-side-encryption-customer-key: $KEY_B64" \
-H "x-amz-server-side-encryption-customer-key-MD5: $KEY_MD5" \
--upload-file ./geheim.pdf
Guidance on key management
- One key per data class/application instead of a single global key – this simplifies key rotation and limits the extent of any damage.
- Rotation: SSE-C objects can be re-encrypted using server-side
CopyObjectwith an old source key and a new destination key, without downloading the data. - Check whether the tools you are using support SSE-C (rclone: yes, via
sse_customer_key; many GUI clients: limited support).