KMSはAWSの他のサービスを使うときになんとなく設定する程度で、KMSそのものの動きはいままで理解できていませんでした。そこで、awscliで手動で暗号化・復号化の処理をしてみることで、理解を進めました。
キー作成
aws kms create-key
コマンドでキーを作成します。
$ aws kms create-key
{
"KeyMetadata": {
"AWSAccountId": "123456789012",
"KeyId": "2f94xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"Arn": "arn:aws:kms:ap-northeast-1:123456789012:key/2f94xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"CreationDate": 1620370256.542,
"Enabled": true,
"Description": "",
"KeyUsage": "ENCRYPT_DECRYPT",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
"EncryptionAlgorithms": [
"SYMMETRIC_DEFAULT"
]
}
}
ここで作成したキーは、存在は見えるものの、キーそのもの(CMK、Customer Master Key)を見ることはできません。
AWSアカウントにあるキー一覧を確認
aws kms list-keys
コマンドでキーの一覧を確認できます。さきほど作成したキーはこの中に含まれています。
$ aws kms list-keys
{
"Keys": [
{
"KeyId": "2f94xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"KeyArn": "arn:aws:kms:ap-northeast-1:123456789012:key/2f94xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
{
"KeyId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"KeyArn": "arn:aws:kms:ap-northeast-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
{
"KeyId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"KeyArn": "arn:aws:kms:ap-northeast-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
...
]
}
マネジメントコンソールで見ると、作成したキーはCustomer managed keysに入っていました。
さきほど作成した以外のキーはAWS managed keysに入っていました。
エイリアスは aws kms list-aliases
コマンドなどで確認できます。
$ aws kms list-aliases
{
"Aliases": [
{
"AliasName": "alias/aws/backup",
"AliasArn": "arn:aws:kms:ap-northeast-1:123456789012:alias/aws/backup",
"TargetKeyId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"CreationDate": 1594564129.245,
"LastUpdatedDate": 1594564129.245
},
{
"AliasName": "alias/aws/dms",
"AliasArn": "arn:aws:kms:ap-northeast-1:123456789012:alias/aws/dms",
"TargetKeyId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"CreationDate": 1598578567.162,
"LastUpdatedDate": 1598578567.162
},
{
"AliasName": "alias/aws/dynamodb",
"AliasArn": "arn:aws:kms:ap-northeast-1:123456789012:alias/aws/dynamodb",
"TargetKeyId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"CreationDate": 1604415194.916,
"LastUpdatedDate": 1604415194.916
},
{
"AliasName": "alias/aws/ebs",
"AliasArn": "arn:aws:kms:ap-northeast-1:123456789012:alias/aws/ebs"
},
...
CDK作成
aws kms generate-data-key
コマンドによりCDKを作成します。CDKはデータを暗号化・復号化するためのキーです。
$ aws kms generate-data-key --key-id 2f94xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --key-spec AES_256
{
"CiphertextBlob": "AQID...",
"Plaintext": "zNn6...",
"KeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/2f94xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
レスポンスにある、Plaintextはキーそのもので、CiphertextBlobはそのキーを最初に作成したキー(CMK、Customer Master Key)で暗号化したものです。どちらもBASE64でエンコードされた長い文字列です。このあとで試しますが、aws kms decrypt
コマンドで、CiphertextBlobからPlaintextを復元できます。
CDKは aws kms generate-data-key
コマンドを実行するごとに異なるキーが返されます。
データを暗号化
Hello, World!
というテキストを暗号化してみます。データの暗号化・復号化は、awscliではなく、適当なツールを使えばよいようです。openssl
を使いました。パスフレーズにさきほど作成したCDKのPlaintextを指定します。BASE64でデコードが必要です。
$ echo 'Hello, World!' | openssl enc -e -aes256 -pbkdf2 -pass file:<(echo 'zNn6...' | base64 -d) | base64
U2FsdGVkX19Qvh8L0t+zpptfNhgHX4a/RzpNB2nql9g=
openssl enc -e
コマンドの -pass
には、パスフレーズの書かれたファイルを file:FILEPATH
のようにファイル名で指定します。ここではファイル作成の手間を惜しんで、コマンドの中に <(...)
で埋め込んでいます。<(...)
の中身はPlaintextをBASE64でデコードした結果です。
openssl enc
コマンドの出力は暗号化された結果です。バイナリなので、コンソール上に表示できるようにBASE64でエンコードしています。
暗号化したデータを保存
データベースなどにデータを暗号化して保存する際は U2FsdGVkX19Qvh8L0t+zpptfNhgHX4a/RzpNB2nql9g=
という暗号化したデータとともに、CDKのCiphertextBlobの AQID....
という文字列(またはそれをBASE64でデコードしたバイナリ)を保存しておきます。CiphertextBlobのから直接データを復号化することはできません。CDKのPlaintextが必要です。Plaintextを得るには、KMSにCiphertextBlobからの復元をリクエストする必要があります。
このあとは、暗号化したデータとCiphertextBlobが保存されていることを想定して、そこからKMSを使って復号化する手順を踏みます。
CDKのPlaintextを復元
aws kms decrypt
コマンドによりCDKのCiphertextBlobからPlaintextを復元します。
$ aws kms decrypt --ciphertext-blob fileb://<(echo 'AQID...' | base64 -d)
{
"KeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/2f94xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"Plaintext": "zNn6...",
"EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}
--ciphertext-blob
オプションには fileb://FILEPATH
のように、CiphertextBlobの AQID....
という文字列をBASE64でデコードしたバイナリを保存したファイル名を指定します。ここではファイル作成の手間を惜しんで、コマンドの中に <(...)
で埋め込んでいます。<(...)
の中身はCiphertextBlobをBASE64でデコードした結果です。
レスポンスされたPlaintextが、暗号化したときのPlaintextと一致しています。暗号化したときのパスフレーズを保存しておかなくても、KMSとCiphertextBlobから復元できることがわかります。
KMSはPlaintextを復元する際にはCMKを識別するためのKeyIdが必要なはずですが、 aws kms decrypt
コマンドではKeyIdの指定が不要です。CiphertextBlobにCMKを識別するための情報が埋め込まれているらしいです。
データを復号化
openssl enc -d
で復号化します。無事 Hello, World!
が復元できました。
$ echo 'U2FsdGVkX19Qvh8L0t+zpptfNhgHX4a/RzpNB2nql9g=' | base64 -d | openssl enc -d -aes256 -pbkdf2 -pass file:<(echo 'zNn6...' | base64 -d)
Hello, World!
暗号化したデータは openssl enc -d
コマンドの標準入力に渡しています。 U2FsdGVk...
はBASE64でエンコードされた文字列ですので、標準入力前にBASE64でデコードしています。
openssl enc -d
コマンドの -pass
には、暗号化したときと同様にコマンドの中に <(...)
で埋め込んでいます。