Use SSE-C
Last reviewed: 5 months ago
The following tutorial shows some snippets for how to use Server-Side Encryption with Customer-Provided Keys (SSE-C) on R2.
- 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.
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 }); } } }}
/** * 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.*/export default { async fetch(req, 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 }); } } }}
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, }),);