背景介绍
为了保证通信的安全性,Amazon IoT设备与Amazon IoT Core的MQTT通信使用基于证书的TLS 1.2双向认证体系。所谓的双向认证,即意味着Amazon IoT设备端需安装Amazon IoT设备证书,并且,签发该证书所使用的CA证书需要被Amazon IoT Core授信,从而完成Amazon IoT Core对Amazon IoT设备端的认证。并且,Amazon IoT设备也会验证Amazon IoT Core的身份。
为了保证Amazon IoT设备和Amazon IoT Core的双向安全对接,对于Amazon IoT设备端,需要安装两类证书:
-
Amazon IoT设备证书
-
Amazon IoT平台的CA证书
-
Amazon IoT
https://aws.amazon.com/cn/iot/
何时使用设备证书的即时注册
当用户希望使用从第三方机构购买或者自签发的CA证书,并将由CA证书签发的设备证书的设备连接到Amazon IoT Core时,可以利用即时注册功能来实现。如果希望直接利用Amazon IoT CA证书签发的设备证书对设备进行注册激活,可以参考Amazon Certificate Vending Machine方案。
-
Amazon Certificate Vending Machine
https://aws.amazon.com/cn/blogs/china/certification-vending-machine-intelligent-device-access-aws-iot-platform-solution/
实现步骤如下:
-
创建CA证书并在Amazon IoT Core上注册和激活。
-
使用该CA证书签发设备证书并安装在Amazon IoT设备上。
-
创建Amazon Lambda函数实现设备证书在Amazon IoT Core上的自动注册。
-
Amazon IoT设备与Amazon IoT Core的第一次连接。
准备工作
本文中的Amazon IoT设备会使用一台Amazon Linux EC2实例模拟,并使用MQTT Mosquitto Client工具来模拟MQTT消息交互过程。实际应用中的Amazon IoT设备需要集成Amazon IoT SDK以实现和Amazon IoT Core的交互。此外,接下来的所有操作都是以亚马逊云科技北京区为示例。
Amazon Linux EC2实例上默认安装了亚马逊云科技命令行工具Amazon AWSCLI,如读者使用其他实例或者自己的电脑,请参考此链接来安装Amazon AWSCLI。
-
Amazon Linux EC2
https://aws.amazon.com/cn/ec2/
-
此链接:
https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/installing.html
第一步:创建CA证书并在Amazon IoT Core上注册和激活
在真实场景中,用户的设备证书经常是由中间CA证书 (Intermediate CA Certificate) 签发而来,而不是由根CA签发(Root CA Certificates)。为了方便起见,本步骤会跳过中间CA证书,直接用根证书签发设备证书。并且由于实际购买CA证书会让笔者付出N个月的薪水,这里我们会使用OpenSSL来自签发证书,用户可以根据自己的实际情况选择不同的签发方式。
登陆Amazon EC2实例并执行如下命令创建私钥和对应的CA证书:
$ mkdir cert
$ cd cert
$ openssl genrsa -out CA_Private.key 2048
$ openssl req -x509 -new -nodes -key CA_Private.key -sha256 -days 365 -out CA_Certificate.pem
左右滑动查看更多
我们需要将这个CA证书注册到Amazon IOT Core。为了安全,Amazon IOT Core提供了相应的审核流程确保你同时持有CA证书和对应的私钥。因此,在最终注册CA证书之前,我们还需要按照流程生成一份用于验证CA证书和私钥持有者身份的中间证书(请注意这份证书并不是上面创建的CA证书)。下面这个Amazon AWSCLI命令会返回一个随机生成的认证码,这个认证码会和你的账户绑定。记录下这个认证码,很快我们就会用到。
$ aws iot get-registration-code
左右滑动查看更多
再次使用OpenSSL生成用于验证身份的私钥和证书请求文件(Amazon CSR – Amazon Certificate Signing Request)。
$ openssl genrsa -out Verification_Private.key 2048
$ openssl req -new -key Verification_Private.key -out Verification.csr
左右滑动查看更多
在创建Amazon CSR的过程中,你会被提示输入如下一些内容,将前一步记录下的认证码填入到Common Name中:
…Organization Name (eg, company) []:Organizational Unit Name (eg, section)Common Name (e.g. server FQDN or YOUR name) []: XXXXXREGISTRATIONCODEXXXXX…
接下来,使用CA证书和私钥,以及上面创建的Amazon CSR来生成一份用于验证身份的中间证书。
$ openssl x509 -req -in Verification.csr -CA CA_Certificate.pem -CAkey
CA_Private.key -CAcreateserial -out Verification.crt -days 365 -sha256
左右滑动查看更多
最后,通过如下命令导入CA证书和中间证书,Amazon IoT Core会完成CA证书的注册和激活。同时,通过设置–allow-auto-registration的方式开启设备连接Amazon IoT Core时设备证书的自动注册。这个命令的输出会返回对应CA证书在Amazon IoT Core上的ID(caCertificateId)。
$ aws iot register-ca-certificate --ca-certificate
file://CA_Certificate.pem --verification-certificate
file://Verification.crt --set-as-active --allow-auto-registration
左右滑动查看更多
第二步:使用CA证书签发设备证书
当我们创建并注册好CA证书之后,就可以开始用这个CA证书来签发设备证书了,步骤如下:
创建一个设备证书的私钥Device.key和对应的证书请求文件Device_Certificate.csr。
$ openssl genrsa -out Device.key 2048
$ openssl req -new -key Device.key -out Device_Certificate.csr
左右滑动查看更多
使用CA证书,CA证书私钥和证书请求文件签发设备证书Device_Certificate.crt。
$ openssl x509 -req -in Device_Certificate.csr -CA CA_Certificate.pem -CAkey
CA_Private.key -CAcreateserial -out Device_Certificate.crt -days 365 -sha256
左右滑动查看更多
在创建好设备证书并在设备上安装完成后,你可能要问,那我如何注册并使用设备证书呢?你当然可以通过Amazon AWSCLI命令行甚至图形界面在Amazon IoT Core上完成注册,但是面对着千千万万的Amazon IoT设备,应该没有人想这样手动去做。接下来我们就会介绍利用Amazon Lambda函数,在设备第一次连接Amazon IoT Core时,自动完成设备证书的注册过程。
第三步:创建Amazon Lambda函数和Amazon IoT规则实现设备证书在Amazon IoT Core上的激活和权限附加
当Amazon IoT设备第一次连接Amazon IoT Core时,如果它集成的设备证书是由已在Core上注册的CA证书签发而来,那么相应的设备证书会实现自动注册,注册后的默认状态为“PENDING_ACTIVATION”,意味着虽然设备证书已经成功注册,但是还处于等待激活的状态。同时,这个连接动作默认会发一条消息到Amazon IoT Core的MQTT Topic “$aws/events/certificates/registered/”上,这条消息事件会是如下的格式:
{
"certificateId": "<certificateID>",
"caCertificateId": "<caCertificateId>",
"timestamp": "<timestamp>",
"certificateStatus": "PENDING_ACTIVATION",
"awsAccountId": "<awsAccountId>",
"certificateRegistrationTimestamp": "<certificateRegistrationTimestamp>"
}
左右滑动查看更多
大家都知道Amazon Lambda函数的执行可以由事件来触发,那么接下来我们会做两件事:
-
创建一个Amazon Lambda函数,接收传入的事件,执行代码逻辑去激活设备证书并附加上一条Policy去给予这个设备相应的权限。
-
创建一Amazon IoT规则,订阅MQTT Topic “$aws/events/certificates/registered/”,当有消息发到这个Topic上时,将消息转发给Amazon Lambda函数处理(激活证书并附加 Policy)。
首先,我们来创建这个Amazon Lambda函数:
1. 登陆到Amazon Console并进入Amazon Lambda页面。
-
Amazon Lambda
https://console.amazonaws.cn/lambda/home?region=cn-north-1#/
2. 点击 “创建函数”后选择“从头开始创作”。
3. 填入“名称”,运行语言选择 “Node.js 6.10”,角色选择“创建自定义角色”。
4. 在新弹出的窗口中,Amazon IAM角色选择“创建新的Amazon IAM角色”,填入角色名称,点击“查看策略文档”,点击“编辑”。
5. 点击“编辑”会弹出一个窗口,提示在编辑之前务必读一下相关文档,这里我们直接点击确定按钮。当然如果有时间的话,还是建议先读一下文档。
6. 用如下的策略替换现有策略,可以看到这里我们为Amazon Lambda函数添加了更新证书和附加Policy的权限,点击“允许”完成角色和策略的配置。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws-cn:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"iot:UpdateCertificate",
"iot:CreatePolicy",
"iot:AttachPrincipalPolicy"
],
"Resource": "*"
}
]
}
左右滑动查看更多
7.点击创建函数进入函数配置界面,替换函数当前的代码为:
/**
This node.js Lambda function code creates and attaches an IoT policy to the
just-in-time registered certificate. It also activates the certificate. The Lambda
function is attached as a rule engine action to the registration topic
$aws/events/certificates/registered/<caCertificateID>
**/
var AWS = require('aws-sdk');
exports.handler = function (event, context, callback) {
// 根据实际部署区域写入,在certificateARN一处也是。
var region = "cn-north-1";
var accountId = event.awsAccountId.toString().trim();
var iot = new AWS.Iot({
'region': region,
apiVersion: '2015-05-28'
});
var certificateId = event.certificateId.toString().trim();
//这里你可以替换成你想要的topic名称
var topicName = `JITR/test`;
var certificateARN = `arn:aws-cn:iot:${region}:${accountId}:cert/${certificateId}`;
var policyName = `Policy_${certificateId}`;
//定义Policy并赋予权限,允许IoT设备连接,发布,订阅和接受消息
var policy = {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"iot:Publish",
"iot:Subscribe",
"iot:Connect",
"iot:Receive"
],
"Effect": "Allow",
"Resource": [
"*"
]
}
]
};
/*
创建Policy
*/
iot.createPolicy({
policyDocument: JSON.stringify(policy),
policyName: policyName
}, (err, data) => {
//Ignore if the policy already exists
if (err && (!err.code || err.code !== 'ResourceAlreadyExistsException')) {
console.log(err);
callback(err, data);
return;
}
console.log(data);
/*
附加Policy到设备证书上
*/
iot.attachPrincipalPolicy({
policyName: policyName,
principal: certificateARN
}, (err, data) => {
//Ignore if the policy is already attached
if (err && (!err.code || err.code !== 'ResourceAlreadyExistsException')) {
console.log(err);
callback(err, data);
return;
}
console.log(data);
/*
激活证书
*/
iot.updateCertificate({
certificateId: certificateId,
newStatus: 'ACTIVE'
}, (err, data) => {
if (err) {
console.log(err, err.stack);
callback(err, data);
} else {
console.log(data);
callback(null, "Success, created, attached policy and activated the certificate " + certificateId);
}
});
});
});
}
左右滑动查看更多
8.点击页面右上角的“保存”来完成Amazon Lambda函数的创建。
在Amazon Lambda函数创建完成后,我们继续创建Amazon IoT规则:
1. 进入Amazon IoT界面。
2. 点击页面左侧的“行动”,然后点击右上角的“创建”。
3. 在创建规则页面中填入规则的“名称”和“描述”,在“属性”一栏填入一个星号“*”。
4. 在创建规则页面中的“主题筛选条件”里填入“$aws/events/certificates/registered/”,注意这里面的要替换成之前用OpenSSL签发的CA证书的ID,这个ID可以通过在Amazon IoT界面的左侧点击“安全”->“CA”来获得。
5. 在设置一个或多个操作中点击“添加操作”, 选择“调用Amazon Lambda函数,传递消息数据”,然后点击“配置操作”。
6. 函数名称选择之前创建的Amazon Lambda函数名称,然后点击“添加操作”。
7. 点击“创建规则”完成Amazon IoT规则的配置。
到此为止,Amazon Lambda函数和Amazon IoT规则创建完成。接下来,我们来尝试连接设备到Amazon IoT Core上。
第四步:Amazon IoT设备与Amazon IoT Core的第一次连接
为了模拟一台设备,你可以安装Amazon IoT SDK,通过自己的代码调用亚马逊云科技来实现所有的功能。这里我们为了简化步骤和节约时间,选择直接在之前创建的Amazon EC2上安装MQTT Mosquitto Client工具。
-
MQTT Mosquitto Client:
https://mosquitto.org/
登陆到Amazon EC2实例并执行如下命令:
$ sudo wget http://download.opensuse.org/repositories/home:/oojah:/mqtt/CentOS_CentOS-7/home:oojah:mqtt.repo -O /etc/yum.repos.d/mqtt.repo
$ sudo yum install mosquitto mosquitto-clients -y
# 如果上面的命令执行时报依赖缺少的错误,可以加上--skip-broken再执行一遍即可
$ sudo yum install mosquitto mosquitto-clients -y --skip-broken
左右滑动查看更多
进入到之前创建的cert目录。
$ cd cert
合并CA证书和设备证书到一个新的证书形成有效的证书链。
$ cat Device_Certificate.crt CA_Certificate.pem > Device_CA_Certificate.crt
左右滑动查看更多
执行mosquitto_pub命令去发布一个消息到对应的topic上面,这也是设备与Amazon IoT Core的第一次连接。如果你回想一下之前的步骤,到目前为止我们的设备证书还只存在于设备上面,并没有在Amazon IoT Core上注册,那么接下来见证奇迹的时刻就要到啦!
$ mosquitto_pub --cafile root-CA.crt --cert Device_CA_Certificate.crt --key Device.key -h xxxxxxxxxxxxxx.iot.cn-north-1.amazonaws.com.cn -p 8883 -q 1 -t JITR/test -i anyclientID --tls-version tlsv1.2 -m "Hello" -d
左右滑动查看更多
-
命令中的-cafile是Amazon IoT Core的CA证书,用于设备去验证Amazon IoT Core的身份,这个文件可以通过此链接获得
-
命令中的-cert是合并CA证书和设备证书后的证书链
-
命令中的-key是设备的私钥
-
命令中的-h是Amazon IoT Core的接入点,可以通过在Amazon IoT界面中左下角点击“设置”获得
-
命令中的-t是你要发布消息到哪一个topic 上,这里我是发布到JITR/test,你可以选择自己想要发布的topic
-
命令中的-i可以按照你希望的名字命名
在第一次执行这个命令后,你会看到如下的报错,那么这是为什么呢?
Client anyclientID sending CONNECT
Error: The connection was lost.
实际上设备在第一次连接Amazon IoT Core的时候,设备证书还没有注册,所以TLS认证会失败。这个连接动作会发布一条注册消息到 “$aws/events/certificates/registered/”上面,接下来Amazon Lambda函数接收到这个消息后,会完成设备证书的注册,附加Policy,那么我们再次执行这个命令就可以成功了。
$ mosquitto_pub --cafile root-CA.crt --cert Device_CA_Certificate.crt --key Device.key -h xxxxxxxxxxxxxx.iot.cn-north-1.amazonaws.com.cn -p 8883 -q 1 -t JITR/test -i anyclientID --tls-version tlsv1.2 -m "Hello" -d
左右滑动查看更多
输出如下:
Client anyclientID sending CONNECT
Client anyclientID received CONNACK
Client anyclientID sending PUBLISH (d0, q1, r0, m1, 'JITR/test', ... (5 bytes))
Client anyclientID received PUBACK (Mid: 1)
Client anyclientID sending DISCONNECT
左右滑动查看更多
这里要注意的是,在实际环境中,用户的代码逻辑里要负责处理这一个过程,也就是说在第一次连接失败后需要自动重连一次或多次来完成证书的注册与设备的激活。
这时进入Amazon IoT界面,点击左侧的安全后,在证书页面中可以看到我们的设备证书已经注册完成并激活了。
参考链接
-
Just-in-Time Registration of Device Certificates on Amazon IoT:
https://aws.amazon.com/blogs/iot/just-in-time-registration-of-device-certificates-on-aws-iot/
本篇作者
郭松
亚马逊云科技解决方案架构师
负责企业级客户的架构咨询及设计优化,同时致力于Amazon IoT和存储服务在国内和全球企业客户的应用和推广。加入亚马逊云科技之前在EMC研发中心担任系统工程师,对企业级存储应用的高可用架构,方案及性能调优有深入研究。