“定期轮换密钥”听起来很简单……但这是否意味着要重新加密所有TB级的数据呢? 😱
##

##
🎯 本文涵盖内容
- 云KMS中“密钥轮换”的实际含义
- 信封加密(Envelope Encryption)概念 — 不了解它就无法理解密钥轮换
- AWS KMS与Azure Key Vault的密钥轮换行为比较
- 轮换自建密钥(BYOK/CMK)时的注意事项
- 不同服务行为差异的原因

📌 引言 / 背景
在查阅云安全指南时,总会看到这样一句话:
“加密密钥应每年定期轮换(Rotation)。”
初次看到这份指南的人自然会产生疑问:
“那么,是不是意味着所有已加密的现有数据都需要用新密钥重新加密?如果S3上有100TB的数据,难道要全部重新处理吗?”
结论是,大多数情况下并非如此。原因是云KMS采用了信封加密(Envelope Encryption)的结构。理解这个概念,你就会立刻明白为什么密钥轮换是一个轻量且快速的操作。
🔍 核心概念:信封加密(Envelope Encryption)
密钥分为两层。
云KMS并非只使用一个密钥的结构。它分为两个层次。
[ 실제 데이터 ]
↑ 암호화
[ DEK: Data Encryption Key ] ← 실제 데이터를 암호화하는 키
↑ 암호화 (wrapping)
[ KEK: Key Encryption Key ] ← DEK를 암호화하는 키 (KMS가 관리)
图示如下:
- DEK (Data Encryption Key): 实际用于加密数据的密钥。通常是AES-256对称密钥。
- KEK (Key Encryption Key): 用于加密(封装)和保护DEK本身的密钥。由KMS、Key Vault等服务管理。
存储中保存的是加密的DEK + 加密的数据。KEK仅存在于KMS内部,不会暴露到外部。
“信封”的比喻
让我们想象写一封信。
- 撰写信件内容(数据)。
- 将信件放入信封(DEK)并密封。
- 将信封放入另一个安全的保险箱(KEK)并上锁。
密钥轮换是只更换保险箱的锁(KEK)。信封和信件内容保持不变。
🔍 密钥轮换时实际发生的事情
AWS KMS — 自动密钥轮换(Automatic Rotation)
在AWS KMS中启用CMK(客户主密钥)的自动轮换会发生什么?
# 使用AWS CLI启用密钥自动轮换
aws kms enable-key-rotation
--key-id arn:aws:kms:ap-northeast-2:123456789012:key/abcd-1234-...
启用后,AWS会每年生成新的密钥材料。此时重要的一点是:
- 使用此密钥加密的现有数据不会自动重新加密。
- 现有数据可以继续使用旧版本的密钥材料进行解密。AWS会在内部维护旧版本。
- 从新的加密请求开始,将使用新的密钥材料。
# boto3示例:使用KMS加密数据
import boto3
kms = boto3.client('kms', region_name='ap-northeast-2')
# 加密:KMS生成DEK,并用KEK(CMK)封装DEK
response = kms.generate_data_key(
KeyId='arn:aws:kms:ap-northeast-2:123456789012:key/abcd-1234-...',
KeySpec='AES_256'
)
plaintext_dek = response['Plaintext'] # 实际用于加密的DEK(仅在内存中使用)
encrypted_dek = response['CiphertextBlob'] # 要存储的加密DEK
# 之后,使用plaintext_dek对实际数据进行AES加密
# plaintext_dek会立即从内存中删除
# 将encrypted_dek + 加密数据一起存储
解密时:
# 解密:将存储的encrypted_dek发送到KMS以获取plaintext_dek
decrypt_response = kms.decrypt(
CiphertextBlob=encrypted_dek
# 无需显式指定KeyId:AWS内部知道是哪个密钥版本用于加密
)
plaintext_dek = decrypt_response['Plaintext']
# 使用plaintext_dek解密数据
AWS KMS在CiphertextBlob内部包含元数据,指示是哪个密钥版本用于加密,因此即使在轮换后,也能够无缝解密旧数据。
Azure Key Vault — 密钥版本(Key Version)方式
与AWS KMS不同,Azure Key Vault通过版本(Version)来管理密钥。
# 使用Azure CLI创建新版本密钥(= 密钥轮换)
az keyvault key create
--vault-name myVault
--name myEncryptionKey
--kty RSA
--size 2048
# 执行此命令时,将创建一个新版本并自动成为“current”版本
# 查看当前密钥的所有版本
az keyvault key list-versions
--vault-name myVault
--name myEncryptionKey
当版本轮换时:
- 旧版本密钥:可以禁用,但通常保持启用状态以解密现有数据。
- 新加密:使用新版本密钥。
- 现有数据:不重新加密(可以使用旧版本解密)。
# 使用特定版本加密
az keyvault key encrypt
--vault-name myVault
--name myEncryptionKey
--version <old-version-id> # 旧版本也可以解密
--algorithm RSA-OAEP
--value "base64-encoded-data"
🔍 不同服务之间有差异吗?
是的。密钥轮换时的行为根据服务和实现方式分为三种情况。
情况1:完全自动 — 无数据重新加密(最常见)
这些是基于KMS/Key Vault管理的信封加密服务。
| 服务密钥 | 轮换行为 |
|---|---|
| AWS S3 SSE-KMS | 仅轮换KEK,不重新加密数据 |
| AWS EBS 卷加密 | 仅轮换KEK,不重新加密现有卷 |
| Azure Blob Storage (CMK) | 指定新密钥版本,自动重新封装DEK |
| Azure SQL Database TDE | 指定新保护器密钥,不重新加密数据 |
情况2:自动DEK重新封装 — 无数据重新加密
Azure Storage、Azure Disk Encryption等部分服务在KEK轮换时,会自动使用新的KEK重新封装存储的DEK。由于DEK本身没有改变,因此不会重新加密数据。
[이전] 데이터 ← DEK ← KEK(v1)
[이후] 데이터 ← DEK ← KEK(v2) ← DEK가 새 KEK로 재래핑됨
(데이터는 동일)
情况3:完全重新加密 — 罕见但存在
这种情况适用于应用程序直接管理密钥,或在特定数据库中轮换主密钥。
# 应用程序直接管理密钥时需要重新加密的模式
def rotate_dek(data_store, old_key, new_key):
"""
앱이 직접 DEK를 관리할 경우, 기존 데이터를 모두 재암호화해야 함
→ 봉투 암호화를 쓰지 않는 경우의 안티패턴
"""
for record in data_store.all():
plaintext = aes_decrypt(record.ciphertext, old_key) # 使用旧密钥解密
record.ciphertext = aes_encrypt(plaintext, new_key) # 使用新密钥重新加密
data_store.save(record)
这些情况要么是由于不使用信封加密的错误设计,要么是需要有意更改数据本身的加密密钥。如果正确使用云托管服务,这种情况几乎不会发生。
⚠️ 注意事项 / 常见错误
🚨 轮换BYOK(自带密钥)时,不要过早删除旧版本密钥。
这是最常见的错误。如果在新密钥轮换后删除了旧密钥,将无法解密用旧密钥加密的DEK,导致现有数据永久无法访问。
# AWS KMS:密钥删除有7-30天的等待期(故意的安全措施)
aws kms schedule-key-deletion
--key-id <old-key-id>
--pending-window-in-days 30 # 最少7天,最多30天
# Azure Key Vault:建议启用软删除 + 清除保护
az keyvault update
--name myVault
--enable-soft-delete true
--enable-purge-protection true
🚨 密钥轮换 ≠ 数据安全级别立即提高
密钥轮换是为了缩小密钥泄露时的损害范围,而不是重新保护已经用泄露密钥解密的数据。如果怀疑密钥泄露,仅进行密钥轮换是不够的,还需要检查该密钥可访问的数据范围。
🚨 不要在应用程序端硬编码密钥版本。
# 不好的例子:硬编码特定版本
KEY_ID = "arn:aws:kms:...:key/abcd-1234"
KEY_VERSION = "version-2023-01" # 轮换后需要修改应用程序代码 → 风险
# 好的例子:使用别名(AWS),自动引用最新版本(Azure)
KEY_ID = "arn:aws:kms:ap-northeast-2:123456789012:alias/my-app-key"
# 别名总是指向当前密钥
✅ 总结 / 结束语
| 问题 | 答案 |
|---|---|
| 密钥轮换时是否需要重新加密所有现有数据? | ❌ 大多数情况下不需要 |
| 轮换后现有数据是否仍可解密? | ✅ 是的,因为旧密钥版本会保留 |
| 轮换后的新数据呢? | ✅ 使用新的KEK |
| 不同服务之间有差异吗? | ✅ 是的,行为方式有差异(例如是否重新封装) |
| 如果应用程序直接管理密钥? | ⚠️ 可能需要重新加密 |
核心要点总结如下:
在信封加密结构中,密钥轮换仅更换封装DEK的KEK,而不触及实际用于数据加密的DEK和数据本身。
如果正确使用云托管服务(如S3、EBS、Azure Blob等),密钥轮换是一个轻量且安全的操作。一旦理解了信封加密,重新加密所有数据的担忧就会消失。
深入学习方向,我们推荐HSM(硬件安全模块)、符合FIPS 140-2/3标准的密钥存储以及多区域密钥复制策略。
发表回复