AWS KMSを理解してますか?


AWS KMSってよくわかんないけど暗号化してくれるサービスでしょ??という方が対象読者です。

AWS KMSは暗号化もしてくれるサービスなのですが、一番の特徴は暗号化に使う鍵を管理してくれるサービスです。本記事ではAWS CLIからKMSのAPIを使って、暗号化・復号の処理の流れを紹介します。

暗号化の方法

  • サーバーサイド暗号化
  • クライアントサイド暗号化

の二種類あります。そしてAWS KMSの特徴であるEnvelop Encryptionはクライアントサイド暗号化で使用できます。サーバーサイド暗号化ではCMK(Customer Managed Key)を使用した通常の暗号化が可能です。AWS KMSのEnvelop EncryptionではデータをCDK(Customer Data Key)で暗号化し、CDKをCMKで暗号化し、CMKをAWS内で管理・保管してくれます。


AWS Key Management Service Developer Guide

ここからはAWS CLIから実際に、サーバーサイド暗号化とクライアントサイド暗号化を試してみます。

前提条件

CMKの作成がされていて、CLIで使用するIAM UserにCMKを使用する権限が与えられているということを前提とします。また、暗号化するファイルは以下とします

sample.txt
Hello KMS

クライアントサイド暗号化(復号も)

  1. AWS CLIからAWS KMSのAPIを使ってCMKからCDKを生成
  2. Opensslを使用して、CDKを鍵としてAES256でデータを暗号化
  3. 暗号化されたCDKをCMKで復号
  4. 復号された平文のCDKでデータを復号

という流れになります

aws kms generate-data-key \
--key-id {作成したcmkのAlias or Key ID} \
--key-spec AES_256 \
--output json > cdk.json

出力にはKeyIdにCMKのKey IDが含まれます。これはCDKのKey IDではないので注意です。 KMSではCDKを暗号化・復号するためのアルゴリズムのみを保持しているため、CDK自体は保持しません。

cdk.json
{
    "CiphertextBlob": "暗号化されたCDK",
    "Plaintext": "平文のCDK",
    "KeyId": "CMKのKEY ID"
}

CiphertextBlob、Plaintextはbase64でエンコードされた状態で帰ってくるので、暗号化する前にデコードする必要があります。


jq -r '.Plaintext' cdk.json | base64 --decode | xxd -p

この平文のCDKを使用して、データを暗号化します。ちなみにaws kms encryptというAPIがありますが、これはこの後、説明するサーバーサイド暗号化をするためのAPIです。Envelop Encryptionをするためには自身でOpensslなどを使用して暗号化の処理をする必要があります。

openssl enc -aes256 -in sample.txt -out encrypted_sample.txt
enter aes-256-cbc encryption password: base64でデコードしたCDK
Verifying - enter aes-256-cbc encryption password:確認用

これで暗号化完了なので、CDKのPlaintextは用済みになります。CDKはCMKで暗号化されますが、この時点では暗号化されたものと、Plaintextがどちらもローカルに存在することになるので、Plaintextは早急に削除しなくてはなりません。一方で暗号化されたCDKは復号する際に必要になるため、自身で保管する必要があります。

jq -r '.CiphertextBlob' cdk.json | base64 --decode >> encrypted_cdk.txt // 暗号化されたCDKを保存
rm cdk.json // CDKのPlaintextを削除

次に保存した暗号化されたCDKを使用してデータを復号します。

aws kms decrypt \
--ciphertext-blob fileb://encrypted_cdk.txt \
--key-id {CDKの暗号化に使用したCMKのalias or KEY ID} > decrypted_cdk.json

レスポンスはこんな感じ

{
    "KeyId": "復号に使用したCMKのKey ID",
    "Plaintext": "CMKで複合された平文のCDK",
    "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}

こちらもまたbase64でエンコードされてるので

jq -r '.Plaintext' decrypted_cdk.json | base64 --decode | xxd -p

最後にOpensslで上記のhexに変換した結果を使って復号します

openssl enc -aes256 -d -in encrypted_sample.txt -out decrypted_sample.txt
enter aes-256-cbc decryption password: 上記のhexに変換した結果

サーバーサイド暗号化(復号も)

クライアントサイドでの暗号化とはことなり、サーバーサイド暗号化はEnvelop Encryptionをしません。そのため、クライアントからAWSにPlaintextを送信し、AWSの中で暗号化され、暗号化されたデータが帰ってくるということになります。そのため、転送中のデータに関しても暗号が求められる場合はクライアントサイドでの暗号化を選択する必要があります。

aws kms encrypt \
--key-id 暗号化に使用するCMKのalias or Key ID \
--plaintext fileb://sample.txt \
--query CiphertextBlob \
--output text | base64 --decode > serverside_encrypted.txt
aws kms decrypt \
--ciphertext-blob fileb://serverside_encrypted.txt \
--key-id データの暗号化に使用したCMKのalias or Key ID \
--output json > serverside_decrypted.json

複合したデータはbase64でエンコードされているので、

jq -r '.Plaintext' serverside_decrypted.json | base64 --decode

このようにCMKで直接、データを暗号化することもできますが、暗号化できるデータのサイズは4KBまでとなっているため注意が必要です。

さいごに

最後に、KMSで注意が必要なのはCMKへのアクセス権限です。KMSにはキーポリシーやGrant APIなどを使用して、柔軟にアクセス権限をコントロールすることができますが、CMKはリージョン間をまたいで共有できないことに注意してください(クロスアカウントはできます)