SSE-C 基本概念与工作原理

SSE-C,全称 Server-Side Encryption with Customer-Provided Encryption Keys。说白了,就是你把加密密钥交给 S3,S3 用这把密钥帮你加密数据,但 S3 不保存 你的密钥。密钥用完即弃?嗯,准确说,每次请求你都得带上密钥,S3 只临时用一下,加密完就扔掉。我在项目中遇到过不少团队对这一点理解有偏差,以为 S3 会记住密钥,结果下次下载时没带密钥,死活拿不到数据……

一句话总结:SSE-C 让你 自己管钥,S3 只负责 临时借用 加密/解密,用完就忘。

核心要素

要搞懂 SSE-C,先抓住四个关键点:

  • 客户提供的密钥:一个 256 位的 AES 对称密钥,由你在客户端生成。
  • 密钥的 MD5 哈希:将密钥做一次 MD5,作为校验令牌发给 S3。S3 不会存你的原钥,只存这个哈希,用于后续验证。
  • 加密算法:固定用 AES-256-CBC,没得选。
  • S3 不存储密钥:我特意强调一下——密钥只在请求生命周期内存在,请求结束立刻丢弃。你想想看,如果 S3 真的把密钥持久化了,那还叫什么“客户提供”?

工作流程

我们以一次 上传 和一次 下载 为例,把整个过程走一遍。这里我习惯用 三步走 来记:

  1. 生成密钥:你在客户端生成一个 256 位 AES 密钥,自己保管好。
  2. 上传对象:在 PUT 请求的 HTTP Header 里带上三个参数:
    x-amz-server-side-encryption-customer-algorithm: AES256
    x-amz-server-side-encryption-customer-key: Base64(你的密钥)
    x-amz-server-side-encryption-customer-key-MD5: Base64(MD5(你的密钥))
    S3 收到后,用你提供的密钥加密对象,将密文存入磁盘,并把密钥的 MD5 哈希记录在对象的元数据中。
  3. 下载对象:你再次发送 GET 请求,同样带上那三个 Header。S3 先校验你给的密钥的 MD5 是否与存储的哈希一致——通过了就用它解密,返回明文;不通过直接返回 403 Forbidden。

我曾经遇到过一个大坑:客户端 SDK 默认会对密钥进行 Base64 编码,但有人手动拼接请求时忘了这一步,直接把二进制密钥塞进 Header……结果 S3 报错说“非法密钥格式”。嗯,整整排查了半天。所以 务必确认你的密钥是经过正确 Base64 编码的字符串。

小技巧:用 AWS CLI 或 SDK 时,--sse-customer-key 参数会自动帮你做 Base64 编码,省心很多。但如果你用 curl 手动调试,一定要自己转一下。

注意事项与避坑

  • 密钥丢失 = 数据丢失:S3 不帮你保存密钥,你丢了密钥,就等于丢了加密后的对象。我见过有人把密钥存到笔记本的便签里,结果笔记本丢了……
  • 每次请求都得带密钥:不适合高频访问的场景,因为每次都要传递 32 字节的密钥,网络开销和延迟都变大了。
  • 必须使用 HTTPS:密钥在 HTTP Header 中明文传输(Base64 只是编码,不是加密),如果走 HTTP,相当于把密钥裸奔。我个人建议 永远不要用 HTTP 访问 SSE-C 的对象。
  • 已加密对象无法更新元数据:如果你想修改对象的元数据(比如 Content-Type),必须先下载解密,再重新上传加密。因为元数据本身也是加密存储的。

一张图看懂 SSE-C 的工作流程

下面这张流程图把上传和下载两条路径画到一起了。你从上往下看,客户端左边是上传,右边是下载。

SSE-C 工作流程(上传与下载) 客户端(PUT请求) 携带密钥 + AES-256 算法标识 提供密钥 S3 使用密钥加密对象 临时用密钥 → 立即丢弃 存储密文+密钥MD5哈希 S3 存储桶(密文 + 元数据) 下载时再次提供密钥 客户端(GET请求) 携带相同密钥 + MD5哈希 验证MD5哈希 S3 解密并返回明文 密钥MD5匹配 → 解密成功 ⚠ 密钥不持久存储 每次请求都必须提供 ⚙ 必须使用 HTTPS 防止密钥在传输中被截获
再次强调:SSE-C 不提供密钥托管能力。如果你需要 S3 帮你管理密钥,请用 SSE-S3(使用 AWS 托管的密钥)。但如果你有合规要求,必须自己掌控密钥,SSE-C 就是你的菜。

SSE-C 与 SSE-S3 的差异速览

这里先给一张简表,让你快速区分。后面的章节会详细展开。

特性 SSE-C SSE-S3
密钥管理方 客户 AWS
密钥存储位置 不在 S3 内存储(仅存 MD5 哈希) AWS 内部自行管理
每次请求需携带密钥 是 否
支持的加密算法 AES-256-CBC AES-256-XTS(实际是 S3 内部实现)
密钥轮换 手动重新上传对象 自动(可选)