64. 封筒暗号は現場で動いている — S3 SSE-KMSとEBS暗号化の内側
S3編で「鍵を包む鍵」として学んだエンベロープ暗号化が、SSE-KMSとEBS暗号化という2つの現場で実際にどう動くのかを図で追っていきます。
① PutObject:S3はKMSにデータキーを作らせ、平文キーは使い捨てる
- 公式の手順:S3が平文データキーと「KMS鍵で暗号化されたコピー」を要求 → KMSが生成して両方返す → S3がデータで暗号化し「使用後できるだけ早く平文キーをメモリから削除」 → 暗号化データキーをメタデータとして暗号化データと一緒に保存
- S3編の封筒暗号レッスンで見た「データ鍵を包んで保存する」構造そのもの。違いは、包む鍵(ルート鍵)の管理をAWS KMSに委ねている点
- 根拠: https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html (SSE-KMS encryption workflow)
SSE-KMSのアップロードは、S3がKMSにデータキーを1本作らせ、その平文キーでオブジェクトを暗号化し、平文キーは即座にメモリから捨てる。手元に残るのは「包まれた(暗号化された)データキー」だけで、それをオブジェクトのメタデータとして同居させる。
② GET:包まれたデータキーをKMSに戻して解いてもらう
- だから必要なKMS権限が非対称になる:アップロードには kms:GenerateDataKey、ダウンロードには kms:Decrypt(マルチパートアップロードは両方必要)。DVA頻出の落とし穴
- GET/HEADで暗号化リクエストヘッダを送るとHTTP 400 Bad Requestになる(鍵はメタデータ側にあり、リクエストで渡すものではない)
- 根拠: https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html (Permissions / decryption workflow)
ダウンロードは逆再生で、S3が保存済みの暗号化データキーをDecryptに渡し、KMSが同じKMS鍵で平文に戻す。暗号化にはGenerateDataKey、復号にはDecrypt——権限が2つに割れているのは、封筒を「作る」操作と「開ける」操作が別だからだ。
③ Bucket Keys:KMSリクエストコストを最大99%削減する『鍵のキャッシュ』
- Bucket Keysなし:オブジェクト1個ごとにKMSを叩く。Bucket Keysあり:S3からKMSへのリクエストトラフィックが減り、KMSリクエストコストを最大99%削減(公式表現は「reduce your AWS KMS request costs by up to 99 percent」)
- やっていることは鍵の階層をもう1段足すこと=鍵のキャッシュ。頻繁なアクセスを手前の階層で吸収するキャッシュの原理を、暗号鍵に適用した版
- 根拠: https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html (Amazon S3 Bucket Keys / "request costs by up to 99 percent")
S3 Bucket Keysは、KMSに一度だけバケットレベルの中間キーを作らせ、それをS3内で一定時間使い回してオブジェクトごとのデータキーを生成する。KMSへのリクエストが減り、KMSリクエストコストは最大99%削減——正体は「鍵階層をもう1段足したキャッシュ」で、頻繁なアクセスを手前で吸収するキャッシュの原理の暗号鍵版だ。
④ encryption context:どの鍵をどこで使うかをKMSに縛る
- デフォルトのencryption contextは、Bucket Keys無効ならオブジェクトARN、Bucket Keys有効ならバケットARN。CloudTrailのログにも aws:s3:arn として現れ、どのARNがどの鍵で処理されたか追跡できる
- Bucket Keysで単位がオブジェクトからバケットに繰り上がるのは、③で見た中間キーがバケット単位だから。contextの粒度も自然にバケットへ揃う
- 根拠: https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html (Encryption context)
encryption contextは「この鍵はこのARNのために使う」という条件をKMSに追加認証データ(AAD)として渡す仕組みで、暗号化時と復号時で一致しなければ復号は失敗する。デフォルト値はBucket Keys無効ならオブジェクトARN、有効ならバケットARN——中間キーの単位がそのままcontextの粒度になる。
⑤ EBS:ボリュームごとに鍵を作り、アタッチで初めて解く
- S3との違い:EBSは作成時点では平文キーが要らないので GenerateDataKeyWithoutPlaintext を使う。暗号化データキーだけを受け取り、ボリューム情報と一緒に保存する
- アタッチのタイミングでEC2がKMSに CreateGrant(この操作を許可する一時的な委任)を出し、Decryptで平文データキーを取得。それをNitroハードウェアのメモリに置いてディスクI/Oを暗号化する
- CreateGrantは、KMSが「誰がこの鍵をこの用途で使ってよいか」を一時的に委任する仕組み。エンベロープ暗号化の鍵を、実際に使う現場(EC2のNitro)へ橋渡ししている
- 根拠: https://docs.aws.amazon.com/ebs/latest/userguide/how-ebs-encryption-works.html
EBSはボリュームごとにデータキーを作るが、作成時は平文キーを受け取らないGenerateDataKeyWithoutPlaintextを使い、暗号化データキーをボリュームメタデータと共に保存する。平文キーが登場するのはアタッチの瞬間だけ——EC2がCreateGrantとDecryptで鍵を解き、Nitroハードウェアのメモリに置いてディスクI/Oを暗号化する。封筒暗号のデータキーが「使われる現場」がここだ。
⑥ デタッチと鍵の無効化:データキーはKMS鍵に依存している証拠
- アタッチ中はデータキーで暗号化しているので、KMS鍵を無効化しても即座には影響しない。だがデタッチでNitroから平文データキーが消えると、次のアタッチは暗号化データキーを復号できず失敗する
- これは「暗号化データキーはKMS鍵がないと解けない」という依存関係の直接の証拠。データキーはKMS鍵にぶら下がっている
- 根拠: https://docs.aws.amazon.com/ebs/latest/userguide/how-ebs-encryption-works.html (How unusable KMS keys affect data keys)
デタッチするとNitroのメモリから平文データキーが消える。アタッチ中はデータキーで暗号化しているのでKMS鍵を無効化しても即座には止まらないが、一度デタッチすると次のアタッチは暗号化データキーを復号できず失敗する。データキーがKMS鍵に依存している——エンベロープ暗号化の二段構造が、そのまま可用性の依存関係として表れる。