この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
サーバーレス開発部@大阪の岩田です。 前回のブログで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のコード署名を作成してみたのですが、コード署名周りはかなり情報が少ない印象でした。 誰かのお役にたてば幸いです。