AWS IoTのコード署名を作成してみた

はじめに

サーバーレス開発部@大阪の岩田です。 前回のブログでAWS IoTのジョブ機能を試してみました。

AWS IoTのジョブ機能とモノの動的グループを利用してデバイスに配布するソフトウェアを管理する

この手順でもソフトウェアの配布自体は可能ですが、本来はソフトウェアが改竄されていないことを保証するためにコードファイルに署名することが推奨されます。 AWS IoTの開発者ガイドでも下記のように紹介されています。

コードをデバイスに送信する場合、ベストプラクティスはコードファイルに署名することです。これにより、デバイスでコードが転送中に変更されているかどうかを検出できます。

このブログでは実際にコード署名を作成する手順についてご紹介します。

やってみる

順を追って進めていきます。

CA証明書の作成

後ほど作成する証明書に署名するためオレオレCA証明書を作成します。 AWS IoTのコード署名では署名アルゴリズムにSHA256WITHECDSAを使う必要があるため、各オプションを適切に設定します。

$ openssl ecparam -genkey -name prime256v1 -out ca.key
$ openssl req -x509 -sha256 -new  -nodes -key ca.key -days 3650 -out ca.crt


You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:JP
State or Province Name (full name) []:Osaka
Locality Name (eg, city) []:Osaka
Organization Name (eg, company) []:Classmethod
Organizational Unit Name (eg, section) []:Serverless
Common Name (eg, fully qualified host name) []:CM-Iwata CA
Email Address []:

証明書の作成

作成したオレオレCA証明書を使ってオレオレ証明書を作成します。

$ openssl ecparam -genkey -name prime256v1 -noout -out server.key
$ openssl req -new -sha256 -key server.key > server.csr


You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:JP
State or Province Name (full name) []:Osaka
Locality Name (eg, city) []:Osaka
Organization Name (eg, company) []:Classmethod
Organizational Unit Name (eg, section) []:Serverless
Common Name (eg, fully qualified host name) []:iwata.classmethod.local
Email Address []:

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

$ openssl x509 -req -sha256 -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

ACMに証明書をインポート

作成した証明書をACMにインポートします。

$ aws acm import-certificate --certificate file://server.crt --private-key file://server.key --certificate-chain file://ca.crt

インポートが成功したら署名アルゴリズムがSHA256WITHECDSAとなっていることを確認しARNを控えておきます。

署名用プロファイルの作成

次に署名用のプロファイルを作成します。 まず下記の要領でJSONファイルを用意します。

{
    "profileName": "<適当なプロファイル名>",
    "signingMaterial": {
        "certificateArn": "<先ほどACMにインポートした証明書のARN>"
    },
    "platformId": "AWSIoTDeviceManagement-SHA256-ECDSA"
}

作成したJSONファイルを使って署名用プロファイルを作成します。

$ aws signer put-signing-profile --cli-input-json file://profile.json
{
    "arn": "arn:aws:signer::xxxxxxxxxxxx:/signing-profiles/test_profile"
}

バージョニングを有効にしたS3バケットの作成

コード署名にはバージョニングを有効にしたS3バケットが必要になります。 下記のコマンドで作成します。

$ aws s3 mb s3://<適当なバケット名>
$ aws s3api put-bucket-versioning --versioning-configuration Status=Enabled --bucket <作成したバケット名>

バケットが作成できたら署名対象となるコードをS3にアップしてバージョンを確認します

$ aws s3 cp <署名対象のファイル> s3://<作成したバケット名>
$ aws s3api list-object-versions   --bucket <作成したバケット名> --query 'Versions[?Key==`<署名対象のファイル>`]'

コード署名

準備ができたのでコード署名のジョブを実行します。 下記のようなJSONファイルを準備します。

{
    "source": {
        "s3": {
            "bucketName": "<作成したS3バケット>",
            "key": "<署名対象のコード>",
            "version": "<先ほど確認した署名対象コードのバージョンID>"
        }
    },
    "destination": {
        "s3": {
            "bucketName": "<作成したS3バケット>",
            "prefix": "<署名結果のS3オブジェクトに付与する適当なプレフィックス>"
        }
    },
    "profileName": "<作成した署名用プロファイルの名前>"
}

作成したJSONファイルを使ってコード署名のジョブを開始します。

$ aws signer start-signing-job --cli-input-json file://sign.json
{
    "jobId": "xxxxxxxxxxxxxxxxxx"
}

ジョブIDが出力されるので、ジョブの実行状況を確認します。

$ aws signer describe-signing-job --job-id <出力されたジョブID>
{
    "jobId": "xxxxxxxxxxxxxxxxxx",
    "source": {
        "s3": {
            "bucketName": "<作成したS3バケット>",
            "key": "<署名対象のコード>",
            "version": "<署名対象コードのバージョンID>"
        }
    },
    "signingMaterial": {
        "certificateArn": "<ACMにインポートした証明書のARN>"
    },
    "platformId": "AWSIoTDeviceManagement-SHA256-ECDSA",
    "profileName": "署名に使用したプロファイル名",
    "createdAt": 1549621529,
    "completedAt": 1549621530,
    "requestedBy": "ジョブを開始したIAMユーザー",
    "status": "Succeeded",
    "statusReason": "Signing Succeeded",
    "signedObject": {
        "s3": {
            "bucketName": "<作成したS3バケット>",
            "key": "<署名結果のオブジェクトキー>"
        }
    }
}

statusがSucceededになっていれば成功です。 署名結果のオブジェクトキーが出力されているので、適当にDLして中身を確認してみましょう。

$ aws s3 cp s3://<作成したS3バケット>/<署名結果のオブジェクトキー> sign_result.json
$ jq . < sign_result.json
{
  "rawPayloadSize": 6610,
  "signature": "署名",
  "signatureAlgorithm": "SHA256withECDSA",
  "payloadLocation": {
    "s3": {
      "bucketName": "<作成したS3バケット>",
      "key": "<署名対象のコード>",
      "version": "<署名対象コードのバージョンID>"
    }
  }
}

無事に署名成功です!!

まとめ

初めてAWS IoTのコード署名を作成してみたのですが、コード署名周りはかなり情報が少ない印象でした。 誰かのお役にたてば幸いです。