JKになりたい

何か書きたいことを書きます。主にWeb方面の技術系記事が多いかも。

AWS KeyManagementService

AWS KeyManagementServiceについて調査したので、そのときのメモ書きを置いておきます。

KeyManagementServiceというサービス単体で存在しているわけではないので、サービス検索でKMSなど入力しても出てこないので注意><

IAM→暗号化キーからKMSのコンソールにいくことができます。

What's KMS?

・データの暗号化に使用される暗号化キーの作成と管理を容易にするマネージド型サービス
AWS KMS で作成したマスターキーは、FIPS 140-2 検証済み暗号化モジュールによって保護
(FIPS・・連邦情報処理標準、FIPS140-2は暗号モジュールのためのセキュリティ要求)

KMSに統合されている他のAWSサービスの一覧はこちら。大体対応していることがわかります。
https://aws.amazon.com/jp/kms/details/#integration

マスターキーとデータキー

公開鍵暗号化方式ではないので、公開鍵/秘密鍵といった用語は出てきません。

データキーでデータの暗号化/復号化どちらも可能です。
そのデータキー自体を暗号化/復号化するためのものがマスターキーになります。

KMSではこのマスターキーを管理(作成、更新、無効化、有効化)する仕組みを提供しています。
マスターキーは自身のローカルストレージなどに保存されることはありません。常にKMSを経由して使用します。

データキーに関しましては、作成とデータキー自体の暗号化/復号化が可能です。
データキーは作成した段階で、暗号化済みデータキーと平文のデータキーがダウンロードされます。
平文データキーが漏れると誰でもデータを復号化できてしまいますので、暗号化処理が終わったら破棄するようにしましょう。
(逆に、暗号化済みデータキーを破棄するとデータを復号できなくなります)

How to use KMS?

KMS APIPython経由で叩いて動作を試してみます。

データキー(平文/暗号化済み)の作成

#coding: utf-8
import boto3

kms_client = boto3.client('kms')

key_id = 'arn:aws:kms:us-east-1:****:key/******'

response = kms_client.generate_data_key(
    KeyId=key_id,
    KeySpec='AES_256'
)

plaintext_key = response['Plaintext']

encrypted_key = response['CiphertextBlob']



key_idですが、ARNじゃなくてKeyIdそのものでも、エイリアス名でも良いらしいです。
(ただしエイリアス名の時はalias/をつける)

・・・すると、何かエラーが出ました。

botocore.errorfactory.NotFoundException: An error occurred (NotFoundException) when calling the GenerateDataKey operation: Invalid arn

マスターキーを作成したリージョンと作業中のリージョンが違うのが原因みたいです。
この点を修正すると、うまく通りました。

plaintext_key、encrypted_keyを出力すると、バイト列が出てきます。

暗号化/復号化

次に、平文のデータキーを使ってデータを暗号化して見ます。
Encrypt用のAPIが用意されているので、それを使って暗号化していきます。

response = kms_client.encrypt(
    KeyId=key_id,
    Plaintext=b'hoge_password'
)

encrypted = response['CiphertextBlob']

これで、暗号化された文字列を取得できます。

encryptedを複合してみます。

decryptResponse = kms_client.decrypt(
    CiphertextBlob=encrypted,
)

decrypted = decryptResponse['Plaintext']

暗号化文字列の中にメタデータとしてどのマスターキーが使われたかなどの情報があるようですね。
暗号化コンテキストが存在する場合は、復号化のときも同じコンテキストを指定しないと復号に失敗します。

DynamoDBとの連携

次は、DynamoDBに書き込むデータを暗号化してみます。

DynamoDBでテーブルを作る時に「保管時の暗号化」というオプションがあるので、これを有効にします。
こうすることで、該当テーブル内のすべて(テーブル、インデックス)のデータが暗号化されます。
また、全てのテーブルの暗号化で単一のサービスデフォルトキーを使用します。
DynamoDB ストリームのデータは暗号化されません。

この状態で適当なデータを書き込んで見ます。

import boto3
from boto3.dynamodb.conditions import Key, Attr
 
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('TestKMS')

with table.batch_writer() as batch:
    batch.put_item(
        Item={
            'id': '1',
            'password': 'hoge_password'
        }
    )
    batch.put_item(
        Item={
            'id': '2',
            'password': 'hoge_hoge_password'
        }
    )

scanしてみます。

response = table.scan()
print(response["Items"])
[{'id': '2', 'password': 'hoge_hoge_password'}, {'id': '1', 'password': 'hoge_password'}]

Boto3が暗黙的に復号してくれるようなので、様子はわからないですね。。

また、IAMの暗号化キーへ行ってみると「aws/dynamodb」というエイリアスがついたマスターキーが生成されていることがわかります。


全データだけでなく、特定カラムのみ暗号化させたい場合や、DynamoDB ストリームを使う場合、マスターキーをローテションさせたい場合などは直接暗号化済み文字列を流してやる必要があるようです。