🔑 密钥轮换会重新加密所有数据吗? — 云KMS的真相

“定期轮换密钥”听起来很简单……但这是否意味着要重新加密所有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内部,不会暴露到外部。

“信封”的比喻

让我们想象写一封信。

  1. 撰写信件内容(数据)。
  2. 将信件放入信封(DEK)并密封。
  3. 将信封放入另一个安全的保险箱(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标准的密钥存储以及多区域密钥复制策略。


Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注