第2章:AWS KMS 基础概念:CMK、密钥层级、密钥标识符

做过加解密的同学都知道,密钥管理是安全体系的根基。AWS KMS 里,最核心的概念就是 CMK(Customer Master Key),也就是客户主密钥。嗯,你可以把它理解成一把“万能钥匙”——不过这把钥匙不会直接去加密你的业务数据,它只用来“生成”和“保护”其他加密密钥。

2.1 什么是 CMK?

CMK 是 KMS 里的顶级密钥对象。你创建好之后,它就一直安全地待在 KMS 的硬件安全模块(HSM)里。它有两种类型:对称 CMK 和非对称 CMK。

核心要点

  • 对称 CMK:用一个密钥同时做加密和解密。99% 的场景都够用,性能也好。我个人习惯,只要没特殊需求,一律选对称 CMK。
  • 非对称 CMK:包含一对公钥和私钥。公钥可以公开下载,私钥永远不出来。适合数字签名场景,比如验证代码签名、API 请求的签名。

我在项目中遇到过一回,同事误把非对称 CMK 当对称 CMK 使,调用 Encrypt API 时报错说“不支持的密钥类型”。当时排查了半天,才发现是密钥类型选错了。所以创建之前先想清楚你要加密还是签名,选对类型。

2.2 密钥层级:CMK 与数据密钥

为什么说 CMK 不直接加密数据?你想想看,如果每次加密都调用 CMK,那 KMS 的 API 调用量会爆炸,而且性能会受影响。KMS 设计了一套优雅的层级:

  • 第一层:CMK 永远锁在 HSM 里,从不物理导出。
  • 第二层:数据密钥(Data Key) 由 CMK 生成,用于在本地加密实际数据。数据密钥是明文和密文同时返回的——你来决定用明文加密,然后立即丢弃明文,只保留密文。
  • 第三层:加密后的数据 与数据密钥的密文一起存储,解密时先用 CMK 解密数据密钥密文,再用明文数据密钥解密数据。

说白了,就是一个“主密钥保护子密钥,子密钥保护数据”的套娃结构。我在架构里通常把这种关系叫做“密钥层次树”。

💡 我的经验

数据密钥有 GenerateDataKey(返回明文+密文)和 GenerateDataKeyWithoutPlaintext(只返回密文)两种 API。如果你只是想在服务器端解密密文,用后者更安全——你根本没机会触碰到明文数据密钥。

下面的 SVG 图帮你把这层关系画清楚了:

AWS KMS 密钥层级关系 Customer Master Key (CMK) 对称 CMK 非对称 CMK Data Key(数据密钥) 加密后的数据(Ciphertext) CMK 用于生成和保护 Data Key,Data Key 用于加密实际数据 只有 CMK 的密钥材料驻留在 HSM 中,Data Key 以密文形式外置存储

2.3 密钥标识符:Key ID、ARN、Alias

你在代码里总要引用 CMK 吧?KMS 给了三种标识方式,各有各的适用场景。

标识符 示例格式 特点
Key ID 1234abcd-12ab-34cd-56ef-1234567890ab UUID 格式,创建时自动生成,唯一且不可变
ARN arn:aws:kms:us-east-1:123456789012:key/1234abcd-... 全局唯一,含区域和账号信息,跨账号引用时必用
Alias alias/my-key 人类可读,可以随时换绑到不同的 CMK,适合版本轮换

你可能会问:平时用哪个?我建议在代码里固定使用 ARN,因为它明确,不会混淆。但如果你有密钥轮换的需求,或者想在环境间迁移(比如从开发环境 key 换到生产环境 key),那就用 Alias——你只需要把 alias 指向新的 CMK,代码完全不用改。

⚠️ 我之前踩过的坑

我曾经在 CloudFormation 模板里直接硬编码了 Key ID,后来手动轮换了密钥,ID 变了,堆栈更新就报错找不到旧 key。从那以后,所有需要动态引用的地方,我全改成 Alias + 参数化。轮换时只要更新 Alias 的映射,简单又安全。

顺便提一句,Alias 本身也是个 KMS 资源,有独立的 ARN。你可以创建多个 Alias 指向同一个 CMK,但一个 Alias 只能指向一个 CMK。反过来呢?一个 CMK 可以被多个 Alias 指向。灵活得很。

下面是一个常见的代码片段,展示了如何在 AWS CLI 中使用这三种标识符调用 Encrypt 命令:

# 使用 Key ID
aws kms encrypt --key-id 1234abcd-12ab-34cd-56ef-1234567890ab \
                --plaintext fileb://my-secret.txt

# 使用 ARN
aws kms encrypt --key-id arn:aws:kms:us-west-2:123456789012:key/1234abcd-... \
                --plaintext fileb://my-secret.txt

# 使用 Alias
aws kms encrypt --key-id alias/my-key \
                --plaintext fileb://my-secret.txt

实际用起来,--key-id 参数三种写法都能识别。但要注意,如果把 Alias 删了再重建(同名不同 key),代码里依然会用这个 alias,但实际加密的 CMK 已经变了。所以轮换时务必确认旧密文对应的 CMK 还能解密,不然就尴尬了。

2.4 一张表总结 CMK、数据密钥、标识符

概念 作用 生命周期 示例
CMK 保护数据密钥,不直接用于业务加密 创建后可启用/禁用/计划删除 arn:aws:kms:...:key/xxx
数据密钥 加密业务数据的对称密钥 由 CMK 生成后,明文用完即弃 随数据存储的密文 ENC(Data Key)
Key ID CMK 的 UUID 标识 不可变 1234abcd-...
ARN AWS 资源全局唯一标识 不可变(除非删除重建) arn:aws:kms:...
Alias 用户友好名称,可重新映射 可变,可指向不同 CMK alias/my-app-key

好,这些基础概念是后面所有策略配置和轮换操作的基石。你理解了 CMK 的层级,就明白为什么轮换只需要换 Alias 指向新 CMK,而不用动历史数据——因为历史数据用的是旧 CMK 生成的 Data Key,只要旧 CMK 不删,解密链就还能走通。这是 KMS 设计里最精妙的地方。