3. AWS KMS 核心概念:密钥类型、密钥层级与 CMK
迁移前的第一件事,就是把 AWS KMS 里的“家底”盘清楚。
密钥类型、密钥层级、CMK 和数据密钥……这些概念如果不搞明白,到了 Azure 那边你会发现自己连门都摸不到。说白了,KMS 里的密钥就是个分层体系,每一层有自己的职责。
3.1 密钥类型:对称 vs 非对称
AWS KMS 支持两种密钥类型:对称密钥和非对称密钥。这俩的区别,不仅仅是数学原理不同,在实际使用场景上差别也很大。
对称密钥:加密和解密用同一把密钥。速度快,适合大量数据加密。
非对称密钥:公钥加密,私钥解密。适合签名验证、密钥交换,性能相对慢一些。
我个人习惯,只要不是必须用非对称的场景(比如数字签名),一律优先选对称密钥。为什么?因为对称密钥快啊,而且 AWS 的对称 CMK 是托管服务,你根本看不到明文密钥,安全性天然有保障。
我在项目中遇到过这么个事:有个团队在 API 网关做请求签名验证,死活要用非对称密钥。结果发现每次调用 KMS 做签名操作,延迟都比预期高。后来改成本地缓存公钥,用本地 OpenSSL 验证签名,好多了。所以你看,非对称密钥虽好,但它的场景要选对。
迁移需要注意什么?
Azure Key Vault 同样支持对称和非对称密钥。但有个差异得记住:AWS KMS 的非对称密钥默认支持 RSA 和 ECC 两种算法簇,而 Azure Key Vault 对 ECC 的支持在某些区域和 SKU 上有差异。迁移前,先确认你的非对称密钥算法类型,再查一下 Azure 那边支不支持。
3.2 密钥层级:从上到下的三层结构
AWS KMS 的密钥体系,说白了就是个分层模型。从上到下分三层:
- 根密钥(CMK):在 KMS 中创建或导入的顶级密钥,永远不离开 KMS 硬件安全模块(HSM)。
- 密钥加密密钥(KEK):CMK 本身就是一种 KEK,它专门用来加密其他密钥。
- 数据密钥(Data Key):由 CMK 生成,用于实际加密你的业务数据。可以拿到本地使用,用完建议立刻丢弃。
你想想看,如果每一份业务数据都直接调 KMS API 加密,那 KMS 的调用量和费用直接起飞。所以 AWS 设计了这套分层机制:用 CMK 生成数据密钥,用数据密钥去加密你的文件或数据库记录。数据密钥用完就丢掉,下次用再重新生成。
我的个人经验:在 S3 服务器端加密(SSE-KMS)场景中,AWS 自动帮你做这个分层。但在自建加密(比如客户端加密 S3 文件)时,你需要手动管理数据密钥的生成和缓存。我建议把数据密钥的明文使用周期控制在 5 分钟内,超过就重新生成,别省那点 KMS 调用费。
| 层级 | 名称 | 是否可导出明文 | 典型场景 |
|---|---|---|---|
| 第1层 | CMK(根密钥) | 否(永远在 HSM 内) | 加密其他密钥、授权管理 |
| 第2层 | KEK(加密密钥的密钥) | 否(同 CMK) | 保护下级数据密钥 |
| 第3层 | 数据密钥 | 是(可导出明文/密文对) | 加密业务数据 |
3.3 CMK 与数据密钥:一对“夫妻”
CMK(Customer Master Key)和数据密钥(Data Key)是 KMS 里最核心的一对概念。很多人刚开始容易搞混,其实记住一句话就行:CMK 是管钥匙的,数据密钥是开锁的。
CMK 的特点:
- 由 AWS KMS 托管,不能直接使用加解密功能操作业务数据。
- 每个 CMK 有唯一的 ARN 和 Key ID。
- 支持自动轮换(每年一次)和手动轮换。
- 可以配置别名、密钥策略、授权。
数据密钥的特点:
- 由 CMK 通过
GenerateDataKeyAPI 生成。 - 返回一对密钥:明文数据密钥(用于本地加密)和密文数据密钥(用于安全存储)。
- 密文数据密钥只能由同一个 CMK 解密。
- 数据密钥本身不存储,每次用的时候重新生成或从密文恢复。
我曾经踩过的一个坑:在迁移时,有个同事直接把 CMK 的 Key Material 拷贝到 Azure Key Vault 里,想“一对一对等”迁移。结果发现 Azure 那边用的是不同的密钥包装格式,根本解密不了 AWS 生成的数据密钥密文。正确的做法是:在 Azure Key Vault 里新建密钥,然后用旧 CMK 重新加密数据,或者用包装解锁(unwrap key)的方式过渡。
实际代码示例:用 CMK 生成数据密钥加密文件
以下是一个典型的客户端加密流程,用 AWS SDK for Python (boto3) 实现:
import boto3
from cryptography.fernet import Fernet
kms = boto3.client('kms')
# 1. 用 CMK 生成数据密钥
response = kms.generate_data_key(
KeyId='alias/my-key',
KeySpec='AES_256'
)
plaintext_key = response['Plaintext'] # 用于加密
ciphertext_key = response['CiphertextBlob'] # 用于存储
# 2. 加密文件
fernet = Fernet(plaintext_key)
with open('data.csv', 'rb') as f:
encrypted_data = fernet.encrypt(f.read())
# 3. 保存加密数据 + 密文数据密钥
with open('data.encrypted', 'wb') as f:
f.write(ciphertext_key + b'\n' + encrypted_data)
print("加密完成。数据密钥的密文已随文件保存。")
解密时,先读取密文数据密钥,用 kms.decrypt 解密得到明文密钥,再用 Fernet 解密文件内容。注意,不要保存明文数据密钥,用完立刻从内存中清理。
3.4 一张图看懂本章核心逻辑
下面这张 SVG 图把密钥类型、层级和 CMK/数据密钥的关系串起来了。你把它打印出来贴在工位上,迁移时随时对照。
从这张图可以看到,CMK 在最顶层,只做两件事:生成数据密钥、解密数据密钥。真正的业务加密,全部由数据密钥在本地完成。这种设计不仅避免了频繁调用 KMS 的延迟和费用,还让密钥管理职责更清晰。
迁移要点总结:
- 对称密钥迁移到 Azure Key Vault,直接创建对应的 RSA/AES 密钥即可。
- 非对称密钥要注意算法兼容性,尤其是 ECC 和 RSA-OAEP 填充方式。
- CMK 不能直接迁移,需要在 Azure 重建,并用旧 CMK 解密数据密钥后再用新 CMK 重新加密。
- 数据密钥本身不迁移,迁移的是“用数据密钥加密过的业务数据”和“用 CMK 加密过的密文数据密钥”。