AWS IoTの証明書をACMで発行し、ECDSA鍵でMQTT通信

# 実現したいことのまとめ

* MQTT TLS相互認証でECDSA鍵を使いたい
    * ECDSAは鍵サイズが小さいので、メモリフットプリントが少なく、低スループットなIoTとの相性が良い
* 鍵サーバーの運用は手間なのでマネージドサービスで完結したい
    * 証明書発行サーバの運用をなくしたい
* 会社のセキュリティ要件を満たしたい
    * IoT Coreの証明書だと要件を満たせない

# 使い所

まず、ACMについて、
> ACM は、AWS ベースのウェブサイトとアプリケーション用のパブリック SSL/TLS 証明書の複雑な作成と管理を処理します。
https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-overview.html

とありますがACMでは、パブリックな証明書の作成だけでなく、プライベートCAの作成（ルートCA、下位 CA）および管理が可能であり、使用する暗号アルゴリズムや証明書の有効期限管理等の設定が可能です。

AWS IoT単体でも、TLS相互認証に使用する証明書の発行は可能ですが、有効期限の設定が固定(2049年までなので、困らないことも多いと思われるが)、払い出される非対称鍵はRSAであったりと、会社やプロジェクトに依ってはセキュリティ要件を満たせない可能性があります。

ここでは、ACMを使って、プライベート CAを作成し、そのCAをAWS IoTに登録することで、マネージドサービスのみで柔軟な証明書の運用を行うことを検討してみます。

# ACM PCA の設定
マネージメントコンソール上で、Certificate Manager → 左側のメニューからプライベートCAを選択し、ウィザードに沿って作成します。認証機関 (CA) 名は特に指定は無いので適当に（本番では慎重に）入れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102681/11c7627b-2a31-0a70-377a-f188f4e0a434.png)

認証機関 (CA) のキーアルゴリズムの設定で、 `ECDSA P256` を選びます。以降の設定は既定値で進めます。
作成が完了したら、ステータスタブに記載されているこのプライベートCAのARNを控えておき、CA証明書タブに切り替えて、証明書をエクスポートします。 `Certificate.pem` というファイルがダウンロードできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102681/849fb76a-0f2a-998a-68ee-5a81e70ddd2a.png)


# CSRの作成
CSRのCommon Nameに、AWS IoTが提供する`registrationCode` の値を入れる必要があります。

```sh
openssl ecparam -list_curves | grep prime256
openssl ecparam -name prime256v1 -out prime256v1.pem
openssl ecparam -in prime256v1.pem -genkey -noout -out prime256v1-key.pem
aws iot get-registration-code
#registrationCodeの値を控えておく
openssl req -new -key prime256v1-key.pem -out prime256v1.csr 
# cnにregistrationCodeを入れる　それ以外は任意
```

# ACM PCAに証明書発行リクエスト

```sh
aws acm-pca issue-certificate --region ap-northeast-1 \ 
--certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:<account-id>:certificate-authority/34d16a3a-1d30-41c4-8f0b-79db67f9f3dc \
--csr file://prime256v1.csr \
--signing-algorithm SHA256WITHECDSA \
--validity Value=365,Type="DAYS"

aws acm-pca get-certificate --region ap-northeast-1 \
 --certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:<account-id>:certificate-authority/34d16a3a-1d30-41c4-8f0b-79db67f9f3dc \
 --certificate-arn <前のコマンドで出力されたCertificateArn>
```

これでコンソール出力に証明書が表示されるので、Certificateの方をコピペし、`prime256v1-verify.crt` というファイルに保存します。改行が文字列 `\n` になっているので実際の改行に直します。

```text:prime256v1-verify.crt
-----BEGIN CERTIFICATE-----
MIICHTCCAcSgAwIBAgIQKPqNaYNqoJDZ/BqRX//aGTAKBggqhkjOPQQDAjATMREw
DwYDVQQKDAh0YXRzaWlkYTAeFw0yMDAzMjYwODEyNDdaFw0yMTAzMjYwOTEyNDda
中略
GfcFjFTIXQndyhGjDdVe/QM=
-----END CERTIFICATE-----
```

余談ですが証明書の中身が気になったら、以下のサイトなどで簡単にデコード結果を確認できます。
https://www.sslshopper.com/certificate-decoder.html

CAの証明書とACM PCAから払い出された証明書を使って、CAを登録します。

```sh
aws iot register-ca-certificate --ca-certificate file://Certificate.pem --verification-certificate file://prime256v1-verify.crt --region ap-northeast-1 --set-as-active
```

以下の記事で書かれているようなJITRを行う場合は `--allow-auto-registration` のオプションを入れます。
https://qiita.com/TakashiKOYANAGAWA/items/b3b679e2a7d56f144a8e

# クライアント証明書を作る

上述の手順と同様に、CSRを作成し、ACM PCAに証明書発行を依頼します。

```sh

openssl ecparam -name prime256v1 -out device.pem
openssl ecparam -in device.pem -genkey -noout -out device-key.pem
openssl req -new -key device-key.pem -out device.csr 

aws acm-pca issue-certificate --region ap-northeast-1 \
--certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:<account-id>:certificate-authority/34d16a3a-1d30-41c4-8f0b-79db67f9f3dc \
--csr file://device.csr \
--signing-algorithm SHA256WITHECDSA \
--validity Value=365,Type="DAYS"

aws acm-pca get-certificate --region ap-northeast-1 \
 --certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:<account-id>:certificate-authority/34d16a3a-1d30-41c4-8f0b-79db67f9f3dc \
 --certificate-arn <前のコマンドで出力されたCertificateArn>
```

これでコンソール出力に証明書が表示されるので、Certificateの方をコピペし、`device.crt` というファイルに保存します。改行が文字列 `\n` になっているので実際の改行に直します。

```text:device.crt
-----BEGIN CERTIFICATE-----
MIICHTCCAcSgAwIBAgIQKPqNaYNqoJDZ/BqRX//aGTAKBggqhkjOPQQDAjATMREw
DwYDVQQKDAh0YXRzaWlkYTAeFw0yMDAzMjYwODEyNDdaFw0yMTAzMjYwOTEyNDda
中略
GfcFjFTIXQndyhGjDdVe/QM=
-----END CERTIFICATE-----
```

# AWS IoTに証明書を登録
```
aws iot register-certificate --certificate-pem file://device.crt --ca-certificate-pem file://Certificate.pem --set-as-active --region ap-northeast-1
```

# 動作確認

以前書いた[この記事](https://qiita.com/tatsuhiroiida/items/bb9e801702ed0e641fa1)の流れに沿って、確認環境をCloud9に作ります。
上記記事の `setup.sh`にて、 `aws iot create-keys-and-certificate` のコマンドをコメントアウトし、CERTIFICATE_ARNに、先程`register-certificate` で生成されたARNを指定します。これで、疎通に必要なThing, policyの作成およびそれらの関連付けがされます。

```sh:cloud9上から実行
python2 basicPubSub.py -e $ENDPOINT -r rootca.pem -id $THING_NAME -c device.crt -k device-key.pem 
```

これで無事にECDSA鍵を使って疎通ができました。ここで指定しているrootca.pemは、AmazonRootCA1の証明書です。ACMで作成したRootCAのものでは無いので注意です。
あと、ACM PCAは、無料期間がありますが、そこそこ費用がかかります（管理サーバ建てて運用するよりは安いとおもいますが）ので注意です。