Skip to content

Use SSE-C

Last reviewed: 3 months ago

The following tutorial shows some snippets for how to use Server-Side Encryption with Customer-Provided Keys (SSE-C) on R2.

Before you begin

  • When using SSE-C, make sure you store your encryption key(s) in a safe place. In the event you misplace them, Cloudflare will be unable to recover the body of any objects encrypted using those keys.
  • While SSE-C does provide MD5 hashes, this hash can be used for identification of keys only. The MD5 hash is not used in the encryption process itself.

Workers

interface Environment {
R2: R2Bucket
/**
* In this example, your SSE-C is stored as a hexadecimal string (preferably a secret).
* The R2 API also supports providing an ArrayBuffer directly, if you want to generate/
* store your keys dynamically.
*/
SSEC_KEY: string
}
export default {
async fetch(req: Request, env: Env) {
const { SSEC_KEY, R2 } = env;
const { pathname: filename } = new URL(req.url);
switch(req.method) {
case "GET": {
const maybeObj = await env.BUCKET.get(filename, {
onlyIf: req.headers,
ssecKey: SSEC_KEY,
});
if(!maybeObj) {
return new Response("Not Found", {
status: 404
});
}
const headers = new Headers();
maybeObj.writeHttpMetadata(headers);
return new Response(body, {
headers
});
}
case 'POST': {
const multipartUpload = await env.BUCKET.createMultipartUpload(filename, {
httpMetadata: req.headers,
ssecKey: SSEC_KEY,
});
/**
* This example only provides a single-part "multipart" upload.
* For multiple parts, the process is the same(the key must be provided)
* for every part.
*/
const partOne = await multipartUpload.uploadPart(1, req.body, ssecKey);
const obj = await multipartUpload.complete([partOne]);
const headers = new Headers();
obj.writeHttpMetadata(headers);
return new Response(null, {
headers,
status: 201
});
}
case 'PUT': {
const obj = await env.BUCKET.put(filename, req.body, {
httpMetadata: req.headers,
ssecKey: SSEC_KEY,
});
const headers = new Headers();
maybeObj.writeHttpMetadata(headers);
return new Response(null, {
headers,
status: 201
});
}
default: {
return new Response("Method not allowed", {
status: 405
});
}
}
}
}

S3-API

import {
UploadPartCommand,
PutObjectCommand, S3Client,
CompleteMultipartUploadCommand,
CreateMultipartUploadCommand,
type UploadPartCommandOutput
} from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: process.env.R2_ENDPOINT,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
},
});
const SSECustomerAlgorithm = "AES256";
const SSECustomerKey = process.env.R2_SSEC_KEY;
const SSECustomerKeyMD5 = process.env.R2_SSEC_KEY_MD5;
await s3.send(
new PutObjectCommand({
Bucket: "your-bucket",
Key: "single-part",
Body: "BeepBoop",
SSECustomerAlgorithm,
SSECustomerKey,
SSECustomerKeyMD5,
}),
);
const multi = await s3.send(
new CreateMultipartUploadCommand({
Bucket: "your-bucket",
Key: "multi-part",
SSECustomerAlgorithm,
SSECustomerKey,
SSECustomerKeyMD5,
}),
);
const UploadId = multi.UploadId;
const parts: UploadPartCommandOutput[] = [];
parts.push(
await s3.send(
new UploadPartCommand({
Bucket: "your-bucket",
Key: "multi-part",
UploadId,
// filledBuf()` generates some random data.
// Replace with a function/body of your choice.
Body: filledBuf(),
PartNumber: 1,
SSECustomerAlgorithm,
SSECustomerKey,
SSECustomerKeyMD5,
}),
),
);
parts.push(
await s3.send(
new UploadPartCommand({
Bucket: "your-bucket",
Key: "multi-part",
UploadId,
// filledBuf()` generates some random data.
// Replace with a function/body of your choice.
Body: filledBuf(),
PartNumber: 2,
SSECustomerAlgorithm,
SSECustomerKey,
SSECustomerKeyMD5,
}),
),
);
await s3.send(
new CompleteMultipartUploadCommand({
Bucket: "your-bucket",
Key: "multi-part",
UploadId,
MultipartUpload: {
Parts: parts.map(({ ETag }, PartNumber) => ({
ETag,
PartNumber: PartNumber + 1,
})),
},
SSECustomerAlgorithm,
SSECustomerKey,
SSECustomerKeyMD5,
}),
);
const HeadObjectOutput = await s3.send(
new HeadObjectCommand({
Bucket: "your-bucket",
Key: "multi-part",
SSECustomerAlgorithm,
SSECustomerKey,
SSECustomerKeyMD5,
}),
);
const GetObjectOutput = await s3.send(
new GetObjectCommand({
Bucket: "your-bucket",
Key: "single-part",
SSECustomerAlgorithm,
SSECustomerKey,
SSECustomerKeyMD5,
}),
);