KMS のエンベロープ暗号化でデータをデータベースに保存する方法 ── 暗号化したデータキーを一緒に持つ
記事の「実践」を 1 項目ずつ進めるためのページです。AWS マネジメントコンソールで操作したら、右の一覧にチェックを入れてください。チェックはこのブラウザに保存されます。
前提条件
- リージョン:
ap-northeast-1(東京) - 以下のリソースが作成済みであること
まだ用意していない場合は、「作り方の参考」の記事の手順で作れます。リソース名は、この記事の名前に読み替えてください。
| リソース種別 | リソース名 | 内容 | 作り方の参考 |
|---|---|---|---|
| KMS キー | alias/dva-envelope-lab | カスタマー管理キー(対称・暗号化と復号) | AWS KMS で暗号鍵を作成しデータを暗号化・復号する |
| DynamoDB テーブル | dva-secure-records | パーティションキー record_id(文字列) | Amazon DynamoDB でテーブルを作り Lambda から読み書きする |
作成するリソース一覧
| リソース種別 | リソース名 | 用途 |
|---|---|---|
| DynamoDB の項目 | customer-001 | 暗号化して保存するレコード |
手順 1: KMS キーを確かめる
AWS マネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。上部の検索バーに KMS と入力し、「Key Management Service」を選択します。左側ナビゲーションの「カスタマー管理型のキー」で、エイリアスが dva-envelope-lab のキーを開きます。「暗号化設定」タブで、キーのタイプが「対称」、キーの用途が「暗号化および復号化」であることを確認します。
手順 2: スクリプトを用意する
コンソール上部の CloudShell のアイコンから CloudShell を開き、使うライブラリを入れます。
pip3 install --user boto3 cryptography
次の内容で envelope.py を作ります(cat > envelope.py <<'EOF' で貼り付け、最後に EOF と入力します)。
import os
import sys
import boto3
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
kms = boto3.client("kms", region_name="ap-northeast-1")
table = boto3.resource("dynamodb", region_name="ap-northeast-1").Table("dva-secure-records")
KEY_ID = "alias/dva-envelope-lab"
def put(record_id, plaintext):
# 1. データキーをもらう(平文と、KMS キーで暗号化したものの 2 つが返る)
dk = kms.generate_data_key(KeyId=KEY_ID, KeySpec="AES_256")
# 2. 平文のデータキーで、手元でデータを暗号化する
nonce = os.urandom(12)
ciphertext = AESGCM(dk["Plaintext"]).encrypt(nonce, plaintext.encode("utf-8"), None)
# 3. 暗号文と「暗号化したデータキー」を一緒に保存する。平文のデータキーは保存しない
table.put_item(Item={
"record_id": record_id,
"ciphertext": ciphertext,
"nonce": nonce,
"encrypted_data_key": dk["CiphertextBlob"],
})
print(f"saved {record_id}: ciphertext {len(ciphertext)} bytes, encrypted data key {len(dk['CiphertextBlob'])} bytes")
def get(record_id):
item = table.get_item(Key={"record_id": record_id})["Item"]
# 4. 暗号化したデータキーを KMS で復号し、手元でデータを復号する
data_key = kms.decrypt(CiphertextBlob=item["encrypted_data_key"].value)["Plaintext"]
plaintext = AESGCM(data_key).decrypt(item["nonce"].value, item["ciphertext"].value, None)
print(f"{record_id}: {plaintext.decode('utf-8')}")
if __name__ == "__main__":
if sys.argv[1] == "put":
put(sys.argv[2], sys.argv[3])
else:
get(sys.argv[2])
手順 3: 暗号化して保存する
次のコマンドで、レコードを暗号化して保存します。
python3 envelope.py put customer-001 "name=Banana Taro, card=4111-1111-1111-1111"
暗号文と暗号化したデータキーのバイト数が表示されます。
DynamoDB のコンソールの左側ナビゲーションで「項目を探索」を選択し、テーブルの一覧から dva-secure-records を選んで、customer-001 のリンクを開きます。ciphertext・nonce・encrypted_data_key はバイナリ(Base64 で表示)で、どこにも元の文字列やデータキーの平文は入っていません。
手順 4: 保存したものだけで復号する
次のコマンドで、テーブルから読み出したものだけを使って復号します。
python3 envelope.py get customer-001
customer-001: name=Banana Taro, card=4111-1111-1111-1111 と、元のレコードが表示されます(画像ではカード番号の部分を伏せています)。スクリプトは KMS キーの ID を Decrypt に渡していませんが、暗号化したデータキーにどの KMS キーで暗号化したかが含まれているので、復号できます。
手順 5: KMS の使用状況を確かめる
KMS キーへのリクエストは CloudTrail に記録されます。CloudTrail のコンソールの「イベント履歴」で、イベントソース kms.amazonaws.com に絞ると、GenerateDataKey と Decrypt のイベントが記録されていることを確認できます(反映まで数分かかることがあります)。データの暗号化そのものは手元で行ったので、KMS に記録されるのはデータキーのやり取りだけです。