結論先行
大型檔案通常不會直接拿 KMS 裡的 master key 加密。成熟的做法是使用兩層不同用途的密鑰:
- DEK(Data Encryption Key):直接加密檔案或資料。
- KEK(Key Encryption Key):只負責加密,也就是 wrap DEK;保存在 KMS 或 HSM 裡。
flowchart LR
F[原始檔案] -->|DEK · AES-GCM| C[加密檔案]
D[DEK] -->|KEK · KMS wrap| E[Encrypted DEK]
K[KEK<br/>不離開 KMS] --> E
實際系統也不只是兩把固定的 key,而是少量 KEK 保護許多 DEK:
一把 KEK
├── 包裝 File A 的 DEK
├── 包裝 File B 的 DEK
└── 包裝 File C 的 DEK
Google Cloud 建議 DEK 在本地產生、以加密形式保存,並可依資料粒度使用不同 DEK;KEK 則集中保存在 KMS。Google Cloud Envelope Encryption
為什麼需要兩層?
如果直接用同一把 master key 加密所有檔案,會遇到幾個問題:
- KMS encrypt API 通常只適合小型資料,不是大型檔案傳輸工具。
- 每次讀寫整份檔案都必須經過 KMS,延遲與費用增加。
- 更換 master key 時,可能需要解密並重新加密所有檔案。
- 單一 key 的使用範圍過大,事件發生時影響面難以控制。
Envelope encryption 把大量資料的高速對稱加密交給 DEK,只讓 KMS 保護很小的 DEK。AWS 也建議用 data key 加密資料,並把 encrypted data key 與 ciphertext 一起保存;明文 data key 使用後應盡快從記憶體移除。AWS KMS Data Keys
寫入與讀取流程
加密檔案
- 產生隨機 DEK。
- 使用 DEK 和 authenticated encryption,例如 AES-256-GCM,加密檔案。
- 呼叫 KMS,以 KEK wrap DEK。
- 儲存 encrypted file、encrypted DEK 與必要 metadata。
- 清除記憶體中的 plaintext DEK。
{
"objectId": "document-123",
"ciphertextUri": "oss://secure/document-123.enc",
"encryptedDek": "base64...",
"kekId": "document-encryption-key",
"kekVersion": "v3",
"algorithm": "AES-256-GCM",
"nonce": "base64...",
"authTag": "base64...",
"encryptionSchema": 1
}
解密檔案
- 從 metadata 取得 encrypted DEK 和 KEK 資訊。
- 將 encrypted DEK 交給 KMS unwrap。
- 使用暫時取得的 plaintext DEK 解密檔案。
- 驗證 authentication tag。
- 清除 plaintext DEK。
應用程式不需要,也不應該把 KEK 放在環境變數或設定檔裡。它只持有呼叫 KMS 的身分與最小權限。
Rotation 不等於重新加密所有檔案
密鑰輪替至少有三種不同情況。
一般週期性 rotation
KMS 為同一個邏輯 key 建立新的 key material version:
舊資料 → KEK v1 / v2 仍可解密
新資料 → 一律使用目前的 KEK v3
這通常不會重新加密既有檔案,也不會自動更換舊 DEK。AWS KMS rotation 會在解密時選擇正確的舊版本;Key ID 本身不必改變。AWS KMS Key Rotation
淘汰舊 KEK:Rewrap DEK
如果目標是讓所有資料不再依賴舊 KEK,不需要先動大型檔案。只要將每個 encrypted DEK 重新包裝:
flowchart LR
O[Encrypted DEK<br/>old KEK] --> U[Unwrap]
U --> D[DEK<br/>memory only]
D --> W[Wrap with new KEK]
W --> N[Encrypted DEK<br/>new KEK]
F[Encrypted file] -. 不需變更 .-> F
在支援 re-encrypt/rewrap 的 KMS 中,轉換甚至可以不把 plaintext DEK 暴露給應用程式。這也是兩層密鑰最大的營運價值:輪替的是小型 key material,不是所有大型資料。
DEK 或演算法遭到影響
如果 DEK 本身已洩漏,單純 rewrap 沒有用。攻擊者已經取得真正能解密檔案的 key,因此必須:
- 產生新 DEK。
- 解密原始 ciphertext。
- 使用新 DEK 重新加密檔案。
- 用安全 KEK wrap 新 DEK。
- 淘汰舊 ciphertext 與 key material。
如果 KEK 疑似遭到入侵,也要依攻擊者是否可能取得 encrypted DEK、ciphertext 與備份,評估是否需要全面更換 DEK。AWS 官方也明確說明,KMS key rotation 不會修復 compromised data key。
安全的輪替流程
正式環境不能在建立新 key 後立刻刪除舊 key。比較安全的順序是:
1. 建立新的 KEK version
2. 新寫入全部改用新版本
3. 背景 rewrap 舊 encrypted DEK
4. 統計仍依賴舊版本的資料
5. 驗證讀取與備份還原
6. 保留舊 KEK 觀察
7. Disable 舊 KEK
8. 再次觀察與演練 rollback
9. 確認無依賴後才排程 destroy
Rewrap job 應具備 idempotency、checkpoint、retry、audit log 和安全的 metadata 更新。若檔案剛好同時被更新,可以用版本欄位或 compare-and-swap,避免 migration 把較新的 encrypted DEK 覆蓋掉。
最容易遺漏的是備份
即使 production 已全部改用 v3,舊 snapshot 可能仍依賴 v1:
Production data → KEK v3
Six-month-old backup → KEK v1
只要 backup 還需要被還原,就不能銷毀 v1。選擇只有三種:遷移備份、等它超過 retention period,或明確接受銷毀 key 後舊資料永久不可恢復。
Google Cloud 也警告,rotation 不會自動重新加密既有資料;在確認所有依賴消失以前銷毀舊 key version,可能造成永久資料遺失。Google Cloud Key Rotation
權限也應分層
成熟的 key management 不只處理演算法,也處理誰能做什麼:
- 應用程式只能使用指定 KEK 執行 wrap/unwrap。
- Security 管理者可以設定 rotation policy,但不能讀取業務資料。
- 維運工作可以執行 migration,但不能匯出 KEK。
- Disable 與 destroy 需要額外核准和等待期。
- Generate、decrypt、rewrap、disable 與 destroy 都應留下 audit log。
如果 KMS 支援 encryption context/AAD,可以把 tenant、object type 或用途綁進密文,避免 key 在錯誤情境下被使用;但這些欄位可能出現在 audit log,因此不能放敏感資料。AWS KMS Encryption Context
實務決策表
| 狀況 | 處理方式 |
|---|---|
| 一般定期 rotation | 新資料使用新 KEK version,保留舊版本解密 |
| 準備淘汰舊 KEK | Rewrap DEK,不重新加密大檔案 |
| 更換 KMS 或 key hierarchy | 舊 KEK unwrap,交由新 KEK wrap |
| DEK 洩漏 | 產生新 DEK,重新加密受影響資料 |
| KEK 疑似洩漏 | 評估暴露範圍,必要時更換 DEK 並重加密 |
| 演算法不再安全 | 使用新演算法重新加密資料 |
| 希望資料永久不可恢復 | 銷毀對應 key,但先處理所有備份與依賴 |
最後的原則
Key rotation 不是每隔一段時間替換一個環境變數。完整的設計必須回答:哪些資料由哪個 DEK 加密、DEK 由哪個 KEK version 保護、舊版本還被哪些正式資料與備份依賴,以及何時能安全淘汰。
Envelope encryption 的價值,是把資料生命週期和 master key 生命週期分開。大部分輪替只需搬動 encrypted DEK;真正發生洩漏或演算法淘汰時,才支付重新加密大型資料的成本。