AWS KMSの対称キー（と非対称キー）についてAWS CLIで学ぶ

KMSについては[優れた記事](https://dev.classmethod.jp/articles/10minutes-kms/)があるのですが、KMSの「対称キー」と「非対称キー」について理解していなかった点があるのでその話を書きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618258/e14ee2e6-fa14-c60b-9fc2-90ce11eb6d71.png)

なお、以下でいう「対称キー」はKMSが生成したもの（外部からインポートしたものではなく）、「非対称キー」はRSA 4096ビットのもののみを考えます（ECCについては除外します）。

# KMSの対称キーは非対称キーの上位互換である
KMSの対称キーは次のような用途に利用できます。

* S3のサーバーサイド暗号化
* EBSの暗号化
* RDSのストレージ暗号化
* SNSのメッセージ暗号化
* ……
* **非対称キーの生成**
* キーの自動ローテーション

# 非対称鍵では公開鍵と秘密鍵が固定される
当たり前といえば当たり前ですが、一度KMSで非対称キー（すなわち秘密鍵と公開鍵のペア）を作成したら変更はできません。**キーIDと秘密鍵・公開鍵は一意に固定**されます。権限がなければキーを削除することもできません。

逆に言えば、このような用途に利用したい場合に**のみ**非対称鍵は有効です。

# なんでこういうことになるのか
KMSの対称キーとは、つまるところ

* 何か（最大4096バイト[^1]）を入れるとそれが暗号化されて出てくる
* 暗号化されたデータを入れると元に戻る
* キーそのものは決して外に出てくることはない
* キーにはIAM PolicyとKey Policyで保護がかけられている

というブラックボックスであるととらえることができます。
[^1]: https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html より、Plaintextの長さは最大4096バイトであることから

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618258/917ce526-3e50-bdaa-1bcb-1a6ceb6077d0.png)

「4096バイト」というサイズ制限が厳しいので、なんでもかんでもKMSに突っ込むわけにはいきません（もちろんこれで十分なケースもあります。パスワードとか）。しかしKMSは十分にランダムな[^2]乱数を生成するAPIも持っています。これと「ブラックボックス」を組み合わせると何ができるかを見ていきます。
[^2]: つまり暗号学的に安全なやつ

## 例1: AES 256ビットの暗号化キーをもらう
### キー生成
KMSで対称キーを作り、そのキーを使ってAWS CLI経由で[GenerateDataKey API](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html)を叩いてみます。

```sh
aws kms generate-data-key \
  --key-id deadbeef-deaf-beef-deaf-beefbeadbeef \
  --key-spec AES_256
```

次のようなJSONが得られました。（これは実行するたびに変わります）

```json
{
    "CiphertextBlob": "AQIDAHgjnCLqIzJ0vSDVeFv0F6upPLnpikd00qPCK4mCNcl3tAFsloiSofee6YIoUd96AyU4AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMMvqesGTcFkW5VwCnAgEQgDu+0DtmeJ8UDH4F+RswNj1Tzb+kGSsDlN2n9u80b9vl2OKDUsBFfbbm4Q4oMQR8AstGqylw+b7aTL6uQg==",
    "Plaintext": "dmd8Asv/3+fUQtVRWRZjTVyN56m8KuAwKV1W+/deeVE=",
    "KeyId": "arn:aws:kms:ap-northeast-1:999999999999:key/deadbeef-deaf-beef-deaf-beefbeadbeef"
}
```

BASE64エンコードされていますが、 `Plaintext` は32バイト、つまり256ビットの乱数列ですので、これは**AES 256ビットで手元のデータを暗号化するときのキー**として使えます[^3]。
[^3]: もちろんそれ以外の用途に使っても構いません。32バイト以内のデータにXORをかけるとか、これ自体を何かのパスワードにするとか

また、 `CiphertextBlob` はこの乱数列をKMSの対称鍵で暗号化したものです。つまり `CiphertextBlob` さえ持っていれば[Decrypt API](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html)で元に戻せるはずです。試してみましょう。

### 復号
```sh
aws kms decrypt \
  --key-id deadbeef-deaf-beef-deaf-beefbeadbeef \
  --ciphertext-blob "AQIDAHgjnCLqIzJ0vSDVeFv0F6upPLnpikd00qPCK4mCNcl3tAFsloiSofee6YIoUd96AyU4AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMMvqesGTcFkW5VwCnAgEQgDu+0DtmeJ8UDH4F+RswNj1Tzb+kGSsDlN2n9u80b9vl2OKDUsBFfbbm4Q4oMQR8AstGqylw+b7aTL6uQg=="
```

こうなりました。一致しています。

```json
{
    "KeyId": "arn:aws:kms:ap-northeast-1:999999999999:key/deadbeef-deaf-beef-deaf-beefbeadbeef",
    "Plaintext": "dmd8Asv/3+fUQtVRWRZjTVyN56m8KuAwKV1W+/deeVE=",
    "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}
```

### で、どうするのか
こんな手順になります。

1. KMSに「256ビットの乱数列をくれ」とお願いする
2. 256ビットの乱数列(A)が降ってくる。ついでにそれをKMSで暗号化したデータ(B)ももらえる
3. (A)を使って手元のデータを暗号化する(X)
4. (A)は捨ててしまい、(X)と(B)だけをどこかに保存する
5. KMSでキーへのアクセス権限があれば(B)→(A)が得られ、(X)から元のデータが得られる

簡単ですね！

#### KMSとS3との関係
SSE-KMSを有効にしたS3バケットにAWS SDK経由でファイルをアップロードすると、この暗号化処理がクライアントサイドで行われ、通信経路には平文のデータは一切[流れません](https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/services-s3.html)。そうでない場合は同等の処理をS3が実行します。したがって、「SSE-S3はサーバーサイド暗号化」と見るのは間違いである（ケースもある）ということになります。

## 例2: RSA 4096ビットの鍵をもらう
次はRSAの秘密鍵と公開鍵を作ってみます。

### キー生成
[GenerateDataKeyPair API](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKeyPair.html)でRSAのキーを生成できます。

```sh
aws kms generate-data-key-pair \
  --key-id deadbeef-deaf-beef-deaf-beefbeadbeef \
  --key-pair-spec RSA_4096
```

見慣れたRSAの秘密鍵、公開鍵、そして秘密鍵をKMSで暗号化したやつのセットが得られます。

```json
{
    "PrivateKeyCiphertextBlob": "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",
    "PrivateKeyPlaintext": "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",
    "PublicKey": "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx2J5WuuQFq8jYTLNU9G4xhzxPMC4lMn2ZjMOBu37RyI+01kJ1E1Hf32wAjBprqdvxyz8VvEmp2XEUOZoMcrJpYY3SHyIJBW6UOdJXeSaIyWKhtelqxHUIpkLe5MnJrk8GIpNIvPMK9p91gLPA8euS8zHhPFLkjsiSNSVZZvMulzrVQNh0/rbCgRtcSvXRTB1y0GpoeSYao7UI10SQ4G2z+H4on2pR0c+JA4hGRdAVHEBLgApELNBRNlYViNHkhDRi6tPJkdG1KajOfIFJ5IE16L55Mi8x1YE3ezY9GWNG3fJqEbOTM8/ozh67sYMUmOcbPT1FJej0CSxViO5gu2EwllnCBZIJittLnoLOUnkRo5tCekpNXOHll2QCUs4vQbWq2egb4AZKBs9qqBv6WZGSW/e2uSCKvKt7/zlqasjS9j1msYS34xUiW1m7aCwQu8vvHeuoOCLu+GhehejqN+Z+ypzkc/K7T67xwadI30RH2mSUz4tzeZIXb39cmtSYjv9FhtLsnl9ZPiLk/g6gR8NKgJRx4P1+vkeM2LfHKMZq+8Rv3LspwpBJ6q2YKsMuRxvL4ZWIvKnqlqN79lfwew9H3pHcgZ3kRLKjTkmr/JSJSi4xtFmsLTpadOcYZbTHC9PICBlcyxIY1NkuwnsNSMlM1XQLiRAQR0lB8MmV8WicgsCAwEAAQ==",
    "KeyId": "arn:aws:kms:ap-northeast-1:999999999999:key/deadbeef-deaf-beef-deaf-beefbeadbeef",
    "KeyPairSpec": "RSA_4096"
}
```

### 復号
念のため、こちらも `PrivateKeyCiphertextBlob` を復号して `PrivateKeyPlaintext` になるか試してみます。

```sh
aws kms decrypt \
>   --key-id deadbeef-deaf-beef-deaf-beefbeadbeef \
>   --ciphertext-blob "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"
```

一致しました。つまり、こちらも `PrivateKeyCiphertextBlob` さえあれば `PrivateKeyPlaintext` は破棄してしまって構わないわけです。

```json
{
    "KeyId": "arn:aws:kms:ap-northeast-1:999999999999:key/deadbeef-deaf-beef-deaf-beefbeadbeef",
    "Plaintext": "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",
    "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}
```

というわけで、RSAのキーが欲しいときもKMSの対称キーとKMS APIを使うことができます。

ただし、ここで作ったRSAのキーとKMS Key IDとの間には何の紐付きもありません。単に手元の ssh-keygen コマンドを叩いたのと同じです。「秘密鍵は1個でいい、直接参照できる必要もない、とにかくどこか安全な場所に保管しておきたい」という用途のときには対称キーではなく非対称キーを使えばいいわけです。

## 例3: 自分が作ったパスワードを直接KMSで暗号化する
KMSは小さなサイズのデータなら暗号化してくれるのでした。実際に `MySpecialSecretPassword*&!` というパスワードをKMSの[Encrypt API](https://docs.aws.amazon.com/kms/latest/APIReference/API_Encrypt.html)で暗号化してみます。

```sh
password="MySpecialSecretPassword*&!"
base64=`echo $password | base64`
aws kms encrypt \
  --key-id deadbeef-deaf-beef-deaf-beefbeadbeef \
  --plaintext $base64
```

暗号化した結果はこちらです。

```json
{
    "CiphertextBlob": "AQICAHgjnCLqIzJ0vSDVeFv0F6upPLnpikd00qPCK4mCNcl3tAHsstmbVra4ww+3aPQTOmEYAAAAeTB3BgkqhkiG9w0BBwagajBoAgEAMGMGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMvBlvazm0sh90G+zeAgEQgDZfDwTWk4cpKwGwtEneFzGdlFpe/aQY7utO56ocdfpMDvTwg5Efhdvk3+7+jEYBd2vIAMvTbrg=",
    "KeyId": "arn:aws:kms:ap-northeast-1:999999999999:key/deadbeef-deaf-beef-deaf-beefbeadbeef",
    "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}
```

もちろん[Decrypt API](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html)を使えばちゃんと元に戻せます。

```sh
aws kms decrypt \
  --key-id deadbeef-deaf-beef-deaf-beefbeadbeef \
  --ciphertext-blob "AQICAHgjnCLqIzJ0vSDVeFv0F6upPLnpikd00qPCK4mCNcl3tAHsstmbVra4ww+3aPQTOmEYAAAAeTB3BgkqhkiG9w0BBwagajBoAgEAMGMGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMvBlvazm0sh90G+zeAgEQgDZfDwTWk4cpKwGwtEneFzGdlFpe/aQY7utO56ocdfpMDvTwg5Efhdvk3+7+jEYBd2vIAMvTbrg="
```

```json
{
    "KeyId": "arn:aws:kms:ap-northeast-1:999999999999:key/deadbeef-deaf-beef-deaf-beefbeadbeef",
    "Plaintext": "TXlTcGVjaWFsU2VjcmV0UGFzc3dvcmQqJiEK",
    "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}
```

```sh
$ echo "TXlTcGVjaWFsU2VjcmV0UGFzc3dvcmQqJiEK" | base64 -d
MySpecialSecretPassword*&!
```

# おわりに
「暗号化キーと……暗号化された暗号化キー？？」と混乱していた頭がAWS CLIを叩くことですっきりしました。手を動かすのは大切ですねというありがちな結論で締めさせていただきます。