Dev Study
AWSサービスの内部原理 コース

61. 封筒暗号 — データはデータキーで、データキーはKMSキーで守る

「鍵を暗号化する鍵も守らなければならない」という無限後退を、AWS KMSがどう断ち切るのか——大きなデータを暗号化する一連の流れを、上から下へ図で追っていきます。

① なぜ「鍵を守る鍵」が無限に必要になるのか

平文データ
鍵Aを暗号化する → 鍵Bが必要
鍵A守らないと平文同然
KMSの断ち切り方: 最上位の1本だけ金庫へ
鍵Bこれも守らないと…
無限後退 ∞
このデータキーだけを暗号化する
データキーデータを暗号化する鍵
KMSキー(ルートキー)最上位。KMSの外に平文で出ない
連鎖はここで止まる
  • KMS公式overviewの言い回し——「あなたの鍵を暗号化したら、今度はその暗号化鍵を守らねばならない。最終的に、データを守る階層の最上位の暗号化鍵(root key と呼ぶ)を守らねばならない。そこでAWS KMSの出番だ」。
  • KMSキーは「FIPS 140-3 Security Level 3 認定のHSM(ハードウェアセキュリティモジュール)」で保護され、「決してAWS KMSの外に平文で出ることはない(They never leave AWS KMS unencrypted)」。

無限後退は「最上位の鍵を1本だけ選び、それを絶対に外に出さない金庫に預ける」ことで止まる。KMSはその金庫であり、預けられた鍵がKMSキー(ルートキー)。

② なぜ大きなデータをKMSに直接送らないのか — 4,096バイトの壁

暗号化したいデータ
行き先が分かれる
小さい(≤4,096B)例: DBパスワード, 個人ID
大きい / 大量動画やDB全体
①4,096B上限 ②本体の往復は遅い
Encrypt に直接暗号文が返る
KMSには送らない→ 封筒暗号へ(③へ)
  • Encrypt API 公式——「最大4,096バイトの平文を暗号化する」。上限の内訳は SYMMETRIC_DEFAULT: 4096バイト、RSA_2048+RSAES_OAEP_SHA_256: 190バイト、RSA_4096+同: 446バイト(非対称キーはもっと小さい)。
  • 用途例も公式の言い回し——「個人識別子やデータベースパスワードのような、小さな任意データの暗号化に使える」。

KMSの直接暗号化は「小さな秘密」専用の窓口。4,096バイトという上限そのものが、大きなデータには別の仕組み(封筒暗号)が要ることを教えている。

③ 封筒暗号の心臓 — GenerateDataKey が返す2つの鍵

アプリ
同じデータキーを2つの姿で返す
KMSKMSキーで新しいデータキーを包む
使い方が正反対
Plaintext平文データキー = 生のAES-256鍵
CiphertextBlobKMSキーで暗号化された同じ鍵
いますぐ使う手元で暗号化→使い終わったら即消す ✗
いま保存する復号時までずっと保管 ✔
  • GenerateDataKey 公式——「データキーの平文コピーと、指定した対称暗号化KMSキーで暗号化されたコピーを返す」。
  • 鍵長は KeySpec で指定し、AES_256(256ビット=32バイト)または AES_128。平文キーの中身は「ランダムなバイト列で、呼び出し元やKMSキーとは無関係」。

GenerateDataKeyは「同じ鍵を、いますぐ使う平文版と、あとで使うための暗号版の両方」で返す。この二枚組が封筒暗号を成立させる。

④ 暗号化の流れ — 平文キーは使ったら即消す

GenerateDataKeyPlaintext と CiphertextBlob が返る
使い終わったら
ローカル暗号化AES-256で手元のデータを暗号化
データ本体はKMSへ送らない
暗号文と暗号化鍵をセットで保存
平文鍵を消去 ✗メモリから消す。ここが安全の要
暗号化データ封筒の中身
CiphertextBlob封をした鍵
2つを一緒に置く = 封筒
  • GenerateDataKey 公式手順——「平文データキー(Plaintext)でデータをKMSの外で暗号化する。そのあと平文データキーをメモリから消去する(erase the plaintext data key from memory)」「暗号化済みデータキー(CiphertextBlob)を暗号化データと一緒に保存する」。
  • Plaintext フィールドの説明も——「このデータキーでKMSの外のデータを暗号化する。そしてできるだけ早くメモリから取り除く(remove it from memory as soon as possible)」。

保存されるのは「暗号文+暗号化済みデータキー」の封筒だけで、平文の鍵はどこにも残らない。これがS3編で入口だけ見たSSE暗号化(保存データの暗号化)の内部で起きていること——AWS側がこの封筒の手順を代行している。

⑤ 復号の流れ — 往復するのは小さな鍵だけ

暗号化データ手元に留まる(KMSへ行かない)
CiphertextBlob暗号化済みデータキー
平文データキーが返る
KMS: DecryptKMSキーで復号
使い終わったら平文鍵を即消去 ✗
ローカルで復号手元の暗号化データを平文に戻す
元の平文データ
  • GenerateDataKey の復号手順(公式)——「Decrypt で暗号化済みデータキーを復号すると平文コピーが返る。その平文データキーでKMSの外のデータを復号し、そのあと平文データキーをメモリから消去する」。
  • 往復するのは CiphertextBlob(最大6,144バイト)のような小さな鍵だけで、データ本体ではない。

復号は暗号化の鏡写しで、KMSと往復するのは常に小さな鍵、データ本体は手元に留まる。この2段構造(遅いKMS往復は鍵にだけ、速いAES-256は本体に)は、TLSのハイブリッド暗号——公開鍵で共通鍵を渡し、本文は速い共通鍵で暗号化する——とまったく同じ発想だ。

⑥ encryption context — 「どの文脈で暗号化したか」を鍵に縛りつける

暗号化するときGenerateDataKey に context を渡す
復号時に同じ context を渡せるかで分岐
暗号化済み鍵文脈の指紋が暗号文に紐づく
結果
完全一致 ✔Decrypt(context={"tenant":"A"})
不一致 ✗{"tenant":"B"} や大小文字違い
平文鍵が返る
復号は失敗InvalidCiphertextException
  • 公式——「encryption context は、追加認証データ(additional authenticated data)を表す非秘密のキーバリューの集まり。暗号化に使ったら、復号時に同じ(大文字小文字まで正確に一致する)encryption context を指定しなければならない。さもなければ InvalidCiphertextException で失敗する」。
  • 「対称暗号化KMSキーでのみ有効。任意だが強く推奨される(optional, but strongly recommended)」。
  • 「機密情報を入れてはいけない。CloudTrailログなどに平文で表示されうる」。

encryption contextは「暗号化した文脈」を鍵に縛りつけるAAD。鍵と暗号文が盗まれても、文脈が一致しなければ復号できない——封筒の封蝋のように、正しい文脈でしか開かない仕組みを足せる。

公式ドキュメントで詳しく ↗