以下教程展示如何在 R2 上使用客户提供密钥的服务端加密 (Server-Side Encryption with Customer-Provided Keys, SSE-C) 的代码片段。
- 使用 SSE-C 时,请确保将加密密钥安全存储。如果密钥丢失,Cloudflare 将无法恢复使用这些密钥加密的任何对象正文。
- 虽然 SSE-C 提供 MD5 哈希,但该哈希仅用于密钥标识。MD5 哈希不用于加密过程本身。
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,
}),
);