AWSから行うセキュア動画配信 ~KMSを利用したCSE~


## はじめに

ここ何ヶ月かで、個人情報が含まれる動画をAWS上のサービスを利用して配信するための構成を考える機会があったので、その主な構成などを紹介させていただきます！

## やりたいこと

以下の条件を満たす動画配信環境を構築します。

* 個人情報が含まれているので、AWS上に保管する動画は暗号化する。(Client Side Encryption – CSE)
* 動画は暗号化したまま配信できるようにする。
* 動画は、認証済みのユーザしか再生(復号)できないようにする。

この条件を満たすために、HLS+AESによる動画配信を採用しました。
よりセキュアな動画配信の方法にDRMがありますが、今回は採用しませんでした。

本記事は以下の３部作となる予定です。今回は１．について解説していきます。

1. **配信するための動画を、AWS Key Management System (KMS) から生成した共通鍵で暗号化し、S3へ格納する**
2. AWS Elemental MediaConvert を利用して、HLS+AES暗号化の形へ動画を変換する
3. Cloudfrontの署名付きCookieを利用して、アクセス可能なユーザを制限する


## 構築予定の環境

構築予定のシステム構成図は以下のようになります。
本記事では、赤字となっている部分を解説します。

![構成図(前編).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168722/41a72aa6-9e93-68d2-3b5a-35995b581817.png)



## 作業手順
以下の手順で暗号化を行っていきます。
1. KMSを使用して共通鍵を生成
2. 生成した共通鍵を16進数へ変換し、それをKMSを用いて暗号化
3. 動画をaes256-cbcで暗号化する（共通鍵と初期化ベクトルが必要）
4. s3へ暗号化した動画をアップロードする

本記事ではcentOS上でaws cliを利用します。
aws-cliのバージョンは 2.1.7 となります。

### 1. KMSを使用して共通鍵を生成
KMSとは、データの暗号化に使用される暗号化キー カスタマーマスターキー (CMK) の作成と管理を容易にするマネージド型サービスです。
詳しくは[こちら](https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/overview.html)を参照してみてください。(2021/3/4時点の情報)
aws cliを使用し、KMSから動画の暗号に使用する鍵(データキー)を生成します。

```
$ aws kms generate-data-key \
--key-id {kmd key or alias} \
--key-spec AES_256 \
--output json > cdk.json
```

※{kmd key or alias} には、KMSのキーのIDまたはaliasを指定します。事前に作成しておいてください。

cdk.jsonの中身は以下のような情報が格納されます。
* CiphertextBlob：KMSのCMKを使用して暗号化されたデータキー
* Plaintext：平文のデータキー
* KeyId：CMKのArn

### 2. 生成した共通鍵を16進数へ変換し、それをKMSを用いて暗号化

平文のデータキーを使用して動画ファイルを暗号化します。
暗号化には共通鍵と初期化ベクトルが必要になります。
* 共通鍵：KMSから生成した平文のデータキーをbase64デコードし、16進数としたもの

    KMSから取得した平文の共通鍵はbase64エンコード済みなので、base64デコードののち、16進数に変換します。
    
    ```
    $ jq -r '.Plaintext' cdk.json > key_base64
    $ cat key_base64 | base64 -d > key_binary    //base64デコードを実施
    $ cat key_binary | xxd -p -c 64 > key_16bit  //16進数へ変換
    ```
    
* 初期化ベクトル：16byteのデータを16進数としたもの

    今回採用する暗号化方式には初期化ベクトルが必須なため、用意します。
    初期化ベクトルには毎回ランダムなものを指定します。
    initvec123456789のように、16byteの文字列を16進数化します。

    ```
    $ echo -n {生成したランダムな16byteの文字列} | xxd -p > init_vector_16bit   //16byteの文字列を16進数へ変換
    $ echo -n {生成したランダムな16byteの文字列} | base64 > init_vector_base64  //16byteの文字列をbase64エンコード
    ```
    
これらの16進数化した共通鍵及び初期化ベクトルを動画を暗号化する際に使用します。
また、これらは動画をMediaConvert上で復号する際にも必要となり、S3上へ動画を格納する際にメタデータとして付与してあげる必要があります。
この際、平文のまま付与するのは危険なため、KMSで暗号化したものを付与します。
それぞれ以下の手順によって暗号化します。

* KMSによって暗号化した共通鍵

    共通鍵をKMSによって暗号化します。S3へアップロードする際にファイルへメタデータとしてこれを付与します。
    ファイルの暗号化に使用するkeyは16進数化したものを使用しますが、kmsを使用して暗号化するものはbase64エンコード済みのものを使用します。つまりはkms generatekeyで生成した平文をそのまま使用します。
    
    ```
    $ aws kms encrypt --key-id {kms key or alias} \
    --region '{指定したkms keyを生成したリージョン}' \
    --encryption-context "service=mediaconvert.amazonaws.com" \
    --plaintext 'file://key_base64' \
    | jq -r ".CiphertextBlob" > key_encrypted
    ```

    CiphertextBlobが暗号化した値です。
    ※MediaConvertで復号を行う場合、–encryption-contextへはservice=mediaconvert.amazonaws.comの指定が必須となります。

* KMSによって暗号化した初期化ベクトル

    初期化ベクトルをKMSによって暗号化します。共通鍵と同様にメタデータとしてこれを付与します。
    
    ```
    $ aws kms encrypt --key-id {kms key or alias} \
    --region '{指定したkms keyを生成したリージョン}' \
    --encryption-context "service=mediaconvert.amazonaws.com" \
    --plaintext 'file://init_vector_base64' \
    | jq -r ".CiphertextBlob" > init_vector_encrypted
    ```
    
    CiphertextBlobが暗号化した初期化ベクトルとなります。

### 3. 動画をaes256-cbcで暗号化する（共通鍵と初期化ベクトルが必要）

実際に動画を暗号化します。
16進数に変換した平文のデータキーと初期化ベクトルを使用します。

```
$ openssl enc -nosalt -aes-256-cbc \
-in {暗号化する動画ファイル名} \
-out {暗号化後の動画ファイル名} \
-K {key_16bitの中身} \
-iv {init_vector_16bitの中身}
```

### 4. s3へ暗号化した動画をアップロードする

暗号化した動画をS3上へアップロードします。

```
$aws s3 cp encrypted_sample.mp4 \
s3://{配置するバケットへのファイルパス} \
--metadata '{"x-amz-key":"{暗号化された共通鍵}", "x-amz-iv":"{暗号化された初期化ベクトル}"}'
```

{暗号化された共通鍵}、{暗号化された初期化ベクトル}は、KMSによって暗号化されたもの(key_encrypted, init_vector_encrypted)です。


## 終わりに
AWS KMSからデータキーを生成し、ファイルを暗号化してS3へ格納する手順をご紹介しました。
次回は、暗号化された動画をMediaConvertを使って配信する形へ変換する手順をご紹介する予定です。