AWS IoTのあまり知られていない機能TransferCertificateを試す

##はじめに

AWS IoTの機能でデバイス証明書を同一リージョン内の別アカウントに移動できる機能であるTransferCertificateを試してみます。

勉強がてら独自CA証明書をつかった構成で試してみます。以下のような構成になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/f5682b2a-d409-7bd8-c744-bc067861d5c3.png)

アカウントAのエンドポイントに対してPublish/Subscirbeを実施可能になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/6752e2ff-bcbe-5d98-2c99-aaf14316c5bf.png)

TransferCertificateを実行しアカウントBでPublish/Subscribeを実施します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/d9cfc2ae-e71c-b75f-312f-dea2f6882c4b.png)


##ユースケース

本番、ステージング、開発といった環境ごとにAWSアカウントを分けているような場合で、テスト対象のデバイスにデバイス証明書を焼き込んでいるようなプロダクトでテストフェーズごとに移動してテストしたいという場合にお使いいただける機能かと思います。

##やってみた

###テスト用に環境作成します

以下のマニュアルに沿って環境構築します。
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/device-certs-your-own.html

キーペアを生成します。

```
$ openssl genrsa -out rootCA.key 2048
```

キーペアのプライベートキーを使用して CA 証明書を生成します。値は何も入力しなくても試すことは可能です。

```
$ openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Meguro
Organization Name (eg, company) [Internet Widgits Pty Ltd]:company
Organizational Unit Name (eg, section) []:section
Common Name (e.g. server FQDN or YOUR name) []:server
Email Address []:
```

AWS IoT から登録コードを取得します。

```json
$ aws iot get-registration-code
{
    "registrationCode": "***********************************"
}
```

プライベートキー検証証明書のキーペアを生成します。

```
openssl genrsa -out verificationCert.key 2048
```

プライベートキー検証証明書の CSR を作成します。 Common NameにAWS IoTからの登録コードを指定する。

```
openssl req -new -key verificationCert.key -out verificationCert.csr
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Meguro
Organization Name (eg, company) [Internet Widgits Pty Ltd]:company
Organizational Unit Name (eg, section) []:section
Common Name (e.g. server FQDN or YOUR name) []:***********************************
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
```

CSR を使用して、プライベートキー検証証明書を作成します。

```
$ openssl x509 -req -in verificationCert.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out verificationCert.pem -days 500 -sha256
Signature ok
subject=/C=JP/ST=Tokyo/L=Meguro/O=company/OU=section/CN=***********************************
Getting CA Private Key
```

AWS IoT に CA 証明書を登録します。

```json
$ aws iot register-ca-certificate --ca-certificate file://rootCA.pem --verification-cert file://verificationCert.pem  
{
    "certificateArn": "arn:aws:iot:us-east-2:***********************************:cacert/***********************************", 
    "certificateId": "***********************************"
}
```

CA 証明書を有効化します。

```
aws iot update-ca-certificate --certificate-id *********************************** --new-status ACTIVE
```

###デバイス証明書を作成する

キーペアを生成します。

```
$ openssl genrsa -out deviceCert.key 2048
Generating RSA private key, 2048 bit long modulus
....................................+++
.............+++
e is 65537 (0x10001)
```

デバイス証明書の CSR を作成します。

```
$ openssl req -new -key deviceCert.key -out deviceCert.csr
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Meguro
Organization Name (eg, company) [Internet Widgits Pty Ltd]:company
Organizational Unit Name (eg, section) []:section
Common Name (e.g. server FQDN or YOUR name) []:device1
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
```

CSR からデバイス証明書を作成します。

```
$ openssl x509 -req -in deviceCert.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out deviceCert.pem -days 500 -sha256
Signature ok
subject=/C=JP/ST=Tokyo/L=Meguro/O=company/OU=section/CN=device1
Getting CA Private Key
```

デバイス証明書を登録する。

```json
$ aws iot register-certificate --certificate-pem file://deviceCert.pem --ca-certificate-pem file://rootCA.pem
{
    "certificateArn": "arn:aws:iot:us-east-2:***********************************:cert/***********************************", 
    "certificateId": "***********************************"
}
```

デバイス証明書を有効化します。

```
$ aws iot update-certificate --certificate-id *********************************** --new-status ACTIVE
```

IoT policyを作成します。

```json
$ cat policy.json 
{
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Action":["iot:*"],
        "Resource": ["*"]
    }]
}
```

```json
$ aws iot create-policy --policy-name "policy1" --policy-document file://policy.json
{
    "policyName": "policy1", 
    "policyArn": "arn:aws:iot:us-east-2:***********************************:policy/policy1", 
    "policyDocument": "{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n        \"Effect\": \"Allow\",\n        \"Action\":[\"iot:*\"],\n        \"Resource\": [\"*\"]\n    }]\n}", 
    "policyVersionId": "1"
}
```

IoT policyをデバイス証明書にアタッチする。

```
$ aws iot attach-policy --policy policy1 --target "arn:aws:iot:us-east-2:***********************************:cert/7***********************************"
```
AmazonのRootCA証明書をダウンロードする

```
$ curl -o AmazonRootCA.pem  https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1758  100  1758    0     0   3753      0 --:--:-- --:--:-- --:--:--  3748
```

###接続確認する

エンドポイントを確認する

```json
$ aws iot describe-endpoint
{
    "endpointAddress": "************.iot.us-east-2.amazonaws.com"
}
```

モスキートでPub/Subする

```
$ mosquitto_pub --cafile AmazonRootCA.pem --cert deviceCert.pem  --key deviceCert.key -h ************.iot.us-east-2.amazonaws.com -p 8883 -q 1 -d -t topic/test -i device1 -m "hello"
Client device1 sending CONNECT
Client device1 received CONNACK (0)
Client device1 sending PUBLISH (d0, q1, r0, m1, 'topic/test', ... (5 bytes))
Client device1 received PUBACK (Mid: 1)
Client device1 sending DISCONNECT
```

```
$ mosquitto_sub --cafile AmazonRootCA.pem --cert deviceCert.pem  --key deviceCert.key -h ************.iot.us-east-2.amazonaws.com -p 8883 -q 1 -d -t topic/test 
Client mosqsub|7490-f018981c62 sending CONNECT
Client mosqsub|7490-f018981c62 received CONNACK (0)
Client mosqsub|7490-f018981c62 sending SUBSCRIBE (Mid: 1, Topic: topic/test, QoS: 1)
Client mosqsub|7490-f018981c62 received SUBACK
Subscribed (mid: 1): 1
Client mosqsub|7490-f018981c62 received PUBLISH (d0, q1, r0, m1, 'topic/test', ... (5 bytes))
Client mosqsub|7490-f018981c62 sending PUBACK (Mid: 1)
hello
```

##トランスファーする　ここからが本番
手順の流れは以下になります。

1. Detach Policy
2. Update certificate status to inactive
3. Transfer Certificate
4. Accept Certificate, set as active
5. Attach Policy

Detach Policyする

```
$ aws iot detach-policy --policy policy1 --target "arn:aws:iot:us-east-2:*************:cert/************************************"
```

証明書をinactiveにする。

```
$ aws iot update-certificate --certificate-id ************************************" --new-status INACTIVE
```

アカウントBにデバイス証明書を移動する

```json
$ aws iot transfer-certificate --certificate-id ************************ --target-aws-account <アカウントBのID>
{
    "transferredCertificateArn": "arn:aws:iot:us-east-2:***********:cert/******************************"
}
```

###移行先のAWSアカウントで実施

確認

```json
$ aws iot list-certificates
{
    "certificates": [
        {
            "certificateArn": "arn:aws:iot:us-east-2:************:cert/**********************************", 
            "status": "PENDING_TRANSFER", 
            "creationDate": 1547988722.594, 
            "certificateId": "**********************************"
        }
    ]
}
```

移動をAcceptする。

```
$ aws iot accept-certificate-transfer --certificate-id **********************************
```

確認する。

```
$ aws iot list-certificates
{
    "certificates": [
        {
            "certificateArn": "arn:aws:iot:us-east-2:************:cert/*****************************", 
            "status": "INACTIVE", 
            "creationDate": 1547988722.594, 
            "certificateId": "*****************************"
        }
    ]
}
```

デバイス証明書を有効化する。

```
$ aws iot update-certificate --certificate-id ***************************** --new-status ACTIVE
```

Activeになっていることを確認する。

```
$ aws iot list-certificates
{
    "certificates": [
        {
            "certificateArn": "arn:aws:iot:us-east-2:************:cert/*****************************", 
            "status": "ACTIVE", 
            "creationDate": 1547988722.594, 
            "certificateId": "*****************************"
        }
    ]
}
```

エンドポイント確認をする

```
$ aws iot describe-endpoint
{
    "endpointAddress": "*************.iot.us-east-2.amazonaws.com"
}
```

IoT Policyを作成してアタッチする。

##モスキートでPub/Subできることを確認する。

```
$ mosquitto_pub --cafile AmazonRootCA.pem --cert deviceCert.pem  --key deviceCert.key -h *************.iot.us-east-2.amazonaws.com -p 8883 -q 1 -d -t topic/test -i device1 -m "hello"
Client device1 sending CONNECT
Client device1 received CONNACK (0)
Client device1 sending PUBLISH (d0, q1, r0, m1, 'topic/test', ... (5 bytes))
Client device1 received PUBACK (Mid: 1)
Client device1 sending DISCONNECT
```

```
$ mosquitto_sub --cafile AmazonRootCA.pem --cert deviceCert.pem  --key deviceCert.key -h *************.iot.us-east-2.amazonaws.com -p 8883 -q 1 -d -t topic/test 
Client mosqsub|9497-f018981c62 sending CONNECT
Client mosqsub|9497-f018981c62 received CONNACK (0)
Client mosqsub|9497-f018981c62 sending SUBSCRIBE (Mid: 1, Topic: topic/test, QoS: 1)
Client mosqsub|9497-f018981c62 received SUBACK
Subscribed (mid: 1): 1
Client mosqsub|9497-f018981c62 received PUBLISH (d0, q1, r0, m1, 'topic/test', ... (5 bytes))
Client mosqsub|9497-f018981c62 sending PUBACK (Mid: 1)
hello
```

##落ち穂拾い
多段で移行可能か確認してみます。
アカウントA→アカウントB→アカウントCといいった移動が可能か？
→可能です！

##お約束
投稿内容は私個人の意見であり、所属企業・部門見解を代表するものではありません。