Key Rotation & Vòng Đời CryptoKeyVersion
Tại sao rotation quan trọng và tại sao nó phức tạp hơn nhiều người nghĩ
Rotation key là một trong những khái niệm bị thực thi sai nhiều nhất trong cloud security. Nhiều team setup automatic rotation mà không hiểu:
- Rotation tạo ra version mới nhưng không tự động re-encrypt data cũ
- Rotation không destroy version cũ — version cũ vẫn tồn tại và vẫn tốn tiền
- Nếu destroy version cũ trước khi re-encrypt data, data bị mất vĩnh viễn
Hiểu đúng rotation mechanics là điều kiện để không gây ra production incidents.
Internal model — Rotation là gì
Về mặt kỹ thuật, "rotate a key" trong Cloud KMS có nghĩa:
- Tạo một CryptoKeyVersion mới với key material mới (được tạo ngẫu nhiên bởi KMS)
- Set version mới làm primary của CryptoKey
- Không làm gì với version cũ — version cũ vẫn ENABLED, vẫn có thể dùng để decrypt
Trước rotation:
CryptoKey: database-key
├── version/1 [ENABLED, PRIMARY] ← dùng để Encrypt() mặc định
└── (chỉ 1 version)
Sau rotation:
CryptoKey: database-key
├── version/1 [ENABLED] ← vẫn tồn tại, có thể decrypt ciphertext cũ
└── version/2 [ENABLED, PRIMARY] ← dùng cho Encrypt() mớiData cũ được mã hoá bằng version/1 vẫn decrypt được — KMS tự biết dùng version nào dựa vào metadata nhúng trong ciphertext.
Data mới được mã hoá bằng version/2 (primary mới).
Không có gì "break" sau rotation. Đây là tính chất backward compatibility của envelope encryption.
Automatic rotation — chỉ cho ENCRYPT_DECRYPT
Automatic rotation chỉ khả dụng cho keys có purpose ENCRYPT_DECRYPT (symmetric). Keys asymmetric (ASYMMETRIC_SIGN, ASYMMETRIC_DECRYPT, MAC) không hỗ trợ automatic rotation.
Tại sao asymmetric keys không support automatic rotation?
Với symmetric keys, chỉ có một key material được giữ bí mật (cả encrypt và decrypt cùng dùng key đó). Rotation tạo key material mới — không ai outside KMS biết key material cũ hay mới.
Với asymmetric keys, public key được distribute ra bên ngoài. Nếu KMS tự động rotate, public key mới được tạo — nhưng tất cả systems đang dùng public key cũ để verify signatures sẽ fail. Rotation asymmetric key phải là coordinated operation: update public key ở tất cả verifiers trước khi signing key thay đổi.
Cấu hình automatic rotation
# Khi tạo key mới
gcloud kms keys create my-key \
--location=us-central1 \
--keyring=prod-keys \
--purpose=encryption \
--rotation-period=7776000s \ # 90 ngày (7,776,000 giây)
--next-rotation-time=2026-07-01T00:00:00Z
# Update rotation schedule cho key hiện có
gcloud kms keys update my-key \
--location=us-central1 \
--keyring=prod-keys \
--rotation-period=7776000s \
--next-rotation-time=2026-07-01T00:00:00ZRàng buộc rotation period:
- Tối thiểu: 86,400 giây (1 ngày)
- Tối đa: 31,536,000 giây (~1 năm) — thực tế là 876,000 giờ (~100 năm)
Điều gì xảy ra khi rotation trigger
Khi đến next_rotation_time:
- Cloud KMS tự động gọi internal
CreateCryptoKeyVersion()→ tạo version mới - Gọi
UpdateCryptoKeyPrimaryVersion()→ set version mới làm primary - Tính toán
next_rotation_timetiếp theo =current_time + rotation_period - Log entry được tạo trong Cloud Audit Logs (Admin Activity log)
Rotation không require downtime. Cả primary version mới và version cũ đều available ngay sau rotation.
Manual rotation
Rotation thủ công thường cần thiết khi:
- Suspect key bị compromise (emergency rotation)
- Cần rotate trước schedule
- Rotate asymmetric key sau khi đã update tất cả public key consumers
# Tạo version mới thủ công
gcloud kms keys versions create \
--location=us-central1 \
--keyring=prod-keys \
--key=my-key
# Lấy version number vừa tạo
gcloud kms keys versions list \
--location=us-central1 \
--keyring=prod-keys \
--key=my-key
# Set version mới làm primary (chỉ cho ENCRYPT_DECRYPT)
gcloud kms keys update my-key \
--location=us-central1 \
--keyring=prod-keys \
--primary-version=3 # version number vừa tạoLưu ý: Manual rotation không ảnh hưởng đến next_rotation_time — automatic rotation schedule vẫn chạy theo cấu hình. Nếu bạn manual rotate ngay trước next_rotation_time, sẽ có thêm một rotation nữa sớm sau đó.
Version lifecycle states
ENABLED
Version đang hoạt động bình thường:
- Có thể dùng để Encrypt (nếu là primary)
- Có thể dùng để Decrypt
- Tốn tiền ($0.06/active version/month cho SOFTWARE, ~$1/month cho HSM)
DISABLED
Version bị vô hiệu hóa:
- Không thể dùng để Encrypt hay Decrypt
- Key material vẫn còn (có thể re-enable)
- Vẫn tốn tiền (disabled versions vẫn bị tính phí)
- Ciphertext được tạo bởi version này không thể decrypt khi version đang DISABLED
DISABLED thường dùng để "tạm dừng" một version trong khi điều tra incident, mà không phải commit vào việc destroy vĩnh viễn.
SCHEDULED_FOR_DESTRUCTION (Destroy Scheduled)
Version đang trong grace period trước khi bị destroy vĩnh viễn.
DestroyCryptoKeyVersion() → SCHEDULED_FOR_DESTRUCTION
│
[destroyScheduledDuration elapsed]
│
▼
DESTROYEDThay đổi quan trọng từ February 1, 2024:
Trước tháng 2/2024:
destroyScheduledDurationmặc định là 24 giờ
Từ tháng 2/2024:destroyScheduledDurationmặc định là 30 ngày
Đây là thay đổi có ý nghĩa lớn cho production:
- Old behavior: Gọi destroy → 24h sau key biến mất (nếu quên re-encrypt data trong 24h → data lost)
- New behavior: Gọi destroy → 30 ngày grace period → nhiều thời gian hơn để phát hiện và cancel
Trong SCHEDULED_FOR_DESTRUCTION state:
- Version không thể dùng để Encrypt hay Decrypt
- Có thể cancel bằng cách gọi
RestoreCryptoKeyVersion()→ version quay về DISABLED
DESTROYED
Version đã bị xóa vĩnh viễn:
- Key material không thể phục hồi
- Ciphertext được mã hoá bằng version này mất vĩnh viễn (không thể decrypt)
- Resource entry vẫn tồn tại (để duy trì audit trail) nhưng không chứa key material
- Không tốn phí (DESTROYED versions không bị tính)
Version numbers không được tái sử dụng: Sau khi version/3 bị destroy, version tiếp theo được tạo sẽ là version/4, không phải version/3 mới. Đây đảm bảo audit trail không bị confuse.
Re-encryption — bước bị bỏ qua quan trọng nhất
Rotation tạo version mới, nhưng data cũ vẫn được mã hoá bằng version cũ. Để thực sự tăng cường bảo mật sau rotation, cần re-encrypt data cũ bằng key version mới — đây gọi là re-encryption.
Cloud KMS documentation chỉ rõ:
"Data encrypted with previous key versions isn't automatically re-encrypted with the new key version. [...] Re-encrypting data removes your reliance on old key versions, allowing you to destroy them."
Re-encryption là gì về mặt kỹ thuật
TRƯỚC RE-ENCRYPTION:
[ciphertext_A] ← được mã hoá bởi DEK_A ← được wrap bởi KEK_version_1
[ciphertext_B] ← được mã hoá bởi DEK_B ← được wrap bởi KEK_version_1
SAU ROTATION (chưa re-encrypt):
[ciphertext_A] ← vẫn dùng KEK_version_1 (vẫn decrypt được)
[ciphertext_B] ← vẫn dùng KEK_version_1 (vẫn decrypt được)
KEK_version_2 ← primary, dùng cho data MỚI
SAU RE-ENCRYPTION:
[ciphertext_A] ← unwrap DEK_A với v1, re-wrap DEK_A với v2
[ciphertext_B] ← unwrap DEK_B với v1, re-wrap DEK_B với v2
KEK_version_1 ← có thể destroy an toàn (không còn DEK nào dùng nó)
KEK_version_2 ← primaryRe-encryption chỉ re-wrap DEK, không re-encrypt actual data. Với large datasets, đây vẫn là non-trivial operation.
Re-encryption workflow thực tế
GCP services hỗ trợ CMEK thường có built-in re-encryption mechanism:
Cloud Storage: Gọi storage.objects.rewrite() với rewriteToken cho large objects. Cloud Storage tự gọi KMS để unwrap DEK cũ và re-wrap với primary version mới.
BigQuery: Không có explicit re-encrypt API — re-encrypt xảy ra khi table được CREATE OR REPLACE hoặc thông qua EXPORT + reimport workflow.
Tự implement cho application-level envelope encryption:
from google.cloud import kms_v1
kms_client = kms_v1.KeyManagementServiceClient()
def re_encrypt_dek(encrypted_dek: bytes, key_name: str) -> bytes:
# Decrypt DEK với version cũ (KMS tự detect version từ ciphertext)
plaintext_dek = kms_client.decrypt(
request={"name": key_name, "ciphertext": encrypted_dek}
).plaintext
# Re-encrypt DEK với primary version (mới nhất)
new_encrypted_dek = kms_client.encrypt(
request={"name": key_name, "plaintext": plaintext_dek}
).ciphertext
# Xóa plaintext_dek khỏi memory (zeroize)
del plaintext_dek
return new_encrypted_dekDestroy schedule và recovery window
Thay đổi destroyScheduledDuration
Default là 30 ngày (từ Feb 2024), nhưng có thể cấu hình khi tạo key:
# Tạo key với custom destroy schedule
gcloud kms keys create my-key \
--location=us-central1 \
--keyring=prod-keys \
--purpose=encryption \
--destroy-scheduled-duration=86400s # 1 ngày (nếu muốn nhanh hơn default)Ràng buộc:
- Tối thiểu: 24 giờ (86,400 giây)
- Tối đa: 120 ngày (10,368,000 giây)
destroyScheduledDuration là property của CryptoKey, áp dụng cho tất cả versions của key đó.
Restore trong SCHEDULED_FOR_DESTRUCTION
Trong grace period, có thể cancel destruction:
# Cancel destruction → version quay về DISABLED state
gcloud kms keys versions restore 3 \
--location=us-central1 \
--keyring=prod-keys \
--key=my-keySau restore, version ở trạng thái DISABLED (không phải ENABLED tự động). Phải explicitly enable lại nếu cần:
gcloud kms keys versions enable 3 \
--location=us-central1 \
--keyring=prod-keys \
--key=my-keyFailure modes
Destroy version trước khi re-encrypt
Kịch bản thảm họa phổ biến nhất:
- Rotate key → version/2 là primary mới
- Destroy version/1 (hoặc đợi 30 ngày)
- Sau đó phát hiện có data cũ vẫn còn DEK được wrap bằng version/1
- Data không thể decrypt → mất vĩnh viễn
Ngăn chặn: Audit xem còn DEK nào dùng version cũ trước khi destroy. Một số services cung cấp re-encryption status API.
Key version bị disable accidentaly
Nếu version hiện tại là primary và bị disable:
- Mọi
Encrypt()call mới fail (FAILED_PRECONDITION) Decrypt()với ciphertext từ version đó cũng fail- Service bị impact ngay lập tức
Recovery: re-enable version (hoặc set primary sang version ENABLED khác).
Cross-project key access bị revoke
Nếu service ở project A dùng CMEK key từ project B, và IAM binding bị xóa:
- Service không thể encrypt/decrypt data
- Tùy service, có thể trigger alerting hoặc service degradation
Monitoring: Set alert trên Cloud KMS permission errors và Audit Log entries với PERMISSION_DENIED.
Best practices cho rotation trong production
1. Rotate nhưng đừng destroy ngay: Sau rotation, để version cũ ở ENABLED trong ít nhất một chu kỳ backup của bạn (ví dụ: 90 ngày). Đảm bảo tất cả backup data đã được re-encrypt hoặc có thể được decrypted trước khi destroy.
2. Monitor key version count: Nếu có 50+ ENABLED versions trên một key, đó là dấu hiệu re-encryption chưa được thực hiện và versions cũ chưa được clean up.
3. Test decryption trước khi destroy: Luôn verify data có thể được decrypt với new version trước khi destroy old version.
4. Tách DEK storage và re-encrypt metadata: Lưu {object_id, encrypted_dek, kek_version_used} riêng để có thể audit và bulk re-encrypt.