KMSの直接暗号化と4KBの壁 ── データキーによるエンベロープ暗号化
記事の「実践」を 1 項目ずつ進めるためのページです。AWS マネジメントコンソールで操作したら、右の一覧にチェックを入れてください。チェックはこのブラウザに保存されます。
前提条件
- リージョン:
ap-northeast-1(東京) - KMSの操作権限とAWS CloudShellの利用権限を持つIAMユーザーまたはロールでコンソールにログイン済みであること
このハンズオンで作成するリソースは以下のとおりです。
| リソース種別 | リソース名 | 用途 |
|---|---|---|
| KMSカスタマー管理キー | envelope-lab-key | 直接暗号化とデータキー発行の検証用 |
> [!NOTE] > KMSのカスタマー管理キーは1本あたり月 1 USD の課金対象で、日割りされます。APIコールにも料金がかかりますが、この手順の回数であればごくわずかです。
手順1: カスタマー管理キーを作成する
AWSマネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。
上部の検索バーに KMS と入力し、表示された「Key Management Service」を選択します。
左側ナビゲーションの「カスタマー管理型のキー」を選択し、「キーの作成」をクリックします。
キーの作成は6つのステップに分かれたウィザードになっています。
ステップ1「キーを設定」 では、以下が既定で選ばれていることを確認して「次へ」をクリックします。
- キーのタイプ:
対称 - キーの使用法:
暗号化および復号化 - キーマテリアルのオリジン:
KMS - 推奨 - リージョン:
単一リージョンキー
ステップ2「ラベルを追加」 で、エイリアスに envelope-lab-key を入力します。説明とタグは空のままにします。
ステップ3「キーの管理アクセス許可を定義」 と ステップ4「キーの使用法アクセス許可を定義」 で、自分がログインしているIAMユーザーまたはロールにチェックを入れて「次へ」をクリックします。ステップ5「キーポリシーを編集」は、生成されたポリシーをそのまま使うので変更しません。
ステップ6「確認」 で内容を確認し、「完了」をクリックします。
作成されたキーの詳細画面で、キーIDとエイリアスを確認します。
手順2: 小さな値を直接暗号化する
コンソール画面左下のフッターにある「CloudShell」をクリックして、AWS CloudShellを開きます。初回は「AWS CloudShell へようこそ」のダイアログが表示されるので、「閉じる」をクリックします。シェルが起動して ~ $ のプロンプトが出れば準備完了です。
データベースのパスワードを想定した短い文字列を暗号化します。
aws kms encrypt \
--key-id alias/envelope-lab-key \
--plaintext "$(echo -n 'P@ssw0rd-for-db' | base64)" \
--query CiphertextBlob \
--output text
Base64の暗号文が返ります。これが Encrypt APIによる直接暗号化です。
手順3: 4KBを超えるデータで失敗することを確認する
5KBのダミーファイルを作り、同じように暗号化を試みます。
head -c 5120 /dev/urandom > big.bin
aws kms encrypt \
--key-id alias/envelope-lab-key \
--plaintext fileb://big.bin \
--query CiphertextBlob \
--output text
ValidationException となり、平文が長すぎる旨のエラーが返ります。対称暗号化キーの上限である4,096バイトを超えたためです。
手順4: データキーを発行する
GenerateDataKey でデータキーを発行します。平文のデータキーと、暗号化されたデータキーの両方が返ることを確認します。
aws kms generate-data-key \
--key-id alias/envelope-lab-key \
--key-spec AES_256 \
--output json > datakey.json
# 平文のデータキー(このあとファイルの暗号化に使う)
jq -r .Plaintext datakey.json | base64 --decode > plain.key
# 暗号化されたデータキー(暗号化データと一緒に保管する)
jq -r .CiphertextBlob datakey.json | base64 --decode > encrypted.key
datakey.json の中身を表示し、Plaintext と CiphertextBlob の2つが入っていることを確認します。平文のデータキーをそのまま画面に出さないよう、先頭だけを表示します。
jq '{KeyId, CiphertextBlob: (.CiphertextBlob[0:44] + "..."), Plaintext: (.Plaintext[0:8] + "...(省略)")}' datakey.json
KeyId には、このデータキーを暗号化したKMSキーのARNが入ります。復号のときにキーを指定しなくてよいのは、この情報が暗号化データキーの中に含まれているためです。
手順5: データキーで大きなファイルを暗号化する
手順3で作った5KBのファイルを、平文のデータキーで暗号化します。暗号化はローカルで行うため、KMSのサイズ制限は関係ありません。
openssl enc -aes-256-cbc -pbkdf2 \
-in big.bin -out big.bin.enc \
-pass file:plain.key
# 平文のデータキーは役目を終えたので削除する
shred -u plain.key 2>/dev/null || rm -f plain.key
ls -l big.bin big.bin.enc encrypted.key
暗号化されたファイル big.bin.enc と、暗号化されたデータキー encrypted.key が残ります。この2つをセットで保管するのがエンベロープ暗号化です。
手順6: 復号する
保管しておいた暗号化データキーをKMSに渡して平文のデータキーを取り戻し、ファイルを復号します。
aws kms decrypt \
--ciphertext-blob fileb://encrypted.key \
--query Plaintext \
--output text | base64 --decode > plain.key
openssl enc -d -aes-256-cbc -pbkdf2 \
-in big.bin.enc -out big.bin.dec \
-pass file:plain.key
# 元のファイルと一致することを確認する
cmp big.bin big.bin.dec && echo "一致しました"
cmp が差分なしで終わり、元のファイルと復号したファイルが一致することを確認します。
> [!NOTE] > decrypt では暗号化に使ったKMSキーを指定していません。暗号化データキーの中にどのキーで暗号化されたかの情報が含まれているためです(対称キーの場合)。復号する側には、そのキーに対する kms:Decrypt の権限が必要です。