亚马逊AWS官方博客

如何把设备安全的接入 AWS IoT(三)

1. 简介

本系列文章前面两篇介绍了 AWS IoT 支持的所有协议,认证和授权方式的原理和细节,以及其分别所适应的场景。并介绍了设备使用 IAM 身份认证和 Amazon Cognito 身份接入 AWS IoT 的参考步骤和参考代码。接下来本文介绍设备使用 X.509 证书身份认证方式接入 AWS IoT 的参考步骤和参考代码。

在进行下一步之前,请确保已执行完本系列文档第一篇的第4章,准备工作。

2. 设备使用 X.509 证书认证方式接入

您可以使用 X.509 证书来认证设备。认证通过后,再通过 X.509 证书绑定的 IoT Policy 对请求进行鉴权。X.509 证书是设备默认的认证方式,如果没有特殊需求,一般建议使用这种方式。

设备使用 X.509 证书认证方式的流程示意图如下:

2.1 设备使用 HTTP 协议接入

为设备创建证书。

device_x509_http_crt_arn=`aws iot create-keys-and-certificate \
--set-as-active --certificate-pem-outfile device_x509_http.crt \
--public-key-outfile device_x509_http.pem --private-key-outfile device_x509_http.key \
| jq .certificateArn | sed 's/"//g'`

为设备创建 IoT Policy。

aws iot create-policy --policy-name IoTPolicyForDeviceX509Http \
--policy-document "{
\"Version\": \"2012-10-17\",
\"Statement\": [
{
\"Effect\": \"Allow\",
\"Action\": \"iot:Publish\",
\"Resource\": [
\"arn:aws-cn:iot:cn-north-1:${account_id}:topic/IoTDemo/device_x509_http\"
]
}
]
}"

把 IoT Policy  绑定到设备证书。

aws iot attach-policy --policy-name IoTPolicyForDeviceX509Http \
--target ${device_x509_http_crt_arn}

生成设备模拟程序。

cat <<EOF > ~/awsIoTAccessDemo/device_x509_http.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import argparse
import os

#获取参数
parser = argparse.ArgumentParser(description='Send data to IoT Core')
parser.add_argument('--data', default="data from device x509 http",
help='data to IoT')
parser.add_argument('--endpoint_prefix', required=True,
help='your iot endpoint prefix.')
parser.add_argument('--client_cert', required=True,
help='your client cert file.')
parser.add_argument('--client_key', required=True,
help='your client key file.')

args = parser.parse_args()
private_data = args.data
endpoint_prefix = args.endpoint_prefix
client_cert = args.client_cert
client_key = args.client_key
device_name = 'device_x509_http'
region = 'cn-north-1'
private_topic = "IoTDemo/"+device_name
server_root_ca_file = "./AmazonRootCA1.pem"
client_cert_file = "./%s" % client_cert
client_key_file = "./%s" % client_key

#port 8444. 443需要支持ALPN
iot_endpoint = "https://%s.ats.iot.cn-north-1.amazonaws.com.cn:8443/topics/" % endpoint_prefix
data = "{\\\\\"source\\\\\":\\\\\"%s\\\\\", \\\\\"data\\\\\":\\\\\"%s\\\\\"}" % (device_name, private_data)
endpoint = iot_endpoint + private_topic
command = "curl --tlsv1.2 --cacert %s --cert %s --key %s -X POST -d \"%s\" %s" % (server_root_ca_file,client_cert_file,client_key_file,data,endpoint)
os.system(command)
EOF

运行设备模拟程序。

python device_x509_http.py --data "data from device x509 http." \
--endpoint_prefix ${endpoint_prefix} \
--client_cert ./device_x509_http.crt \
--client_key ./device_x509_http.key

在本系列文章第一篇的第4.3章节打开的控制台中查看AWS IoT收到的消息。

2.2 设备使用 MQTT 协议接入

为设备创建证书。

device_x509_mqtt_crt_arn=`aws iot create-keys-and-certificate \
--set-as-active --certificate-pem-outfile device_x509_mqtt.crt \
--public-key-outfile device_x509_mqtt.pem --private-key-outfile device_x509_mqtt.key \
| jq .certificateArn | sed 's/"//g'`

为设备创建 IoT Policy。

aws iot create-policy --policy-name IoTPolicyForDeviceX509Mqtt \
--policy-document "{
\"Version\": \"2012-10-17\",
\"Statement\": [
{
\"Sid\": \"VisualEditor0\",
\"Effect\": \"Allow\",
\"Action\": [
\"iot:Publish\",
\"iot:Receive\"
],
\"Resource\": \"arn:aws-cn:iot:cn-north-1:${account_id}:topic/IoTDemo/device_x509_mqtt\"
},
{
\"Sid\": \"VisualEditor1\",
\"Effect\": \"Allow\",
\"Action\": \"iot:Connect\",
\"Resource\": \"arn:aws-cn:iot:cn-north-1:${account_id}:client/device_x509_mqtt\"
},
{
\"Sid\": \"VisualEditor2\",
\"Effect\": \"Allow\",
\"Action\": \"iot:Subscribe\",
\"Resource\": \"arn:aws-cn:iot:cn-north-1:${account_id}:topicfilter/IoTDemo/device_x509_mqtt\"
}
]
}"

把 IoT Policy  绑定到设备证书。

aws iot attach-policy --policy-name IoTPolicyForDeviceX509Mqtt \
--target ${device_x509_mqtt_crt_arn}

生成设备模拟程序。

cat <<EOF > ~/awsIoTAccessDemo/device_x509_mqtt.py
#coding:utf-8

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import json
import time
import sys
import commands
import boto3
import argparse

parser = argparse.ArgumentParser(description='Send data to IoT Core')
parser.add_argument('--endpoint_prefix', required=True,
help='your iot endpoint prefix.')
parser.add_argument('--client_cert', required=True,
help='your client cert file.')
parser.add_argument('--client_key', required=True,
help='your client key file.')

args = parser.parse_args()
endpoint_prefix = args.endpoint_prefix
client_cert = args.client_cert
client_key = args.client_key
device_name = 'device_x509_mqtt'
region = 'cn-north-1'
private_topic = "IoTDemo/"+device_name
base_path = "./"
server_root_ca_file = base_path + "AmazonRootCA1.pem"
client_cert_file = base_path + client_cert
client_key_file = base_path + client_key
endpoint = "%s.ats.iot.cn-north-1.amazonaws.com.cn" % endpoint_prefix
port = 8883
# Custom MQTT message callback
def customCallback(client, userdata, message):
print("Received a new message: ")
print(message.payload)
print("from topic: ")
print(message.topic)
print("--------------\n\n")

def pub_msg():
try:
pri_loopCount = 0
while True:
print 'please input:',
msg = raw_input()
private_data = msg
message = {}
message['message'] = json.dumps({"source":device_name, "data":private_data})
message['sequence'] = pri_loopCount
messageJson = json.dumps(message)
myAWSIoTMQTTClient.publish(private_topic, messageJson, 1)
pri_loopCount += 1
time.sleep(2)
except:
sys.exit()

if __name__ == '__main__':
# Init AWSIoTMQTTClient
myAWSIoTMQTTClient = None
myAWSIoTMQTTClient = AWSIoTMQTTClient(device_name)
myAWSIoTMQTTClient.configureEndpoint(endpoint, port)
myAWSIoTMQTTClient.configureCredentials(server_root_ca_file, client_key_file, client_cert_file)
# AWSIoTMQTTClient connection configuration
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)  # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureDrainingFrequency(2)  # Draining: 2 Hz
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)  # 5 sec
# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
myAWSIoTMQTTClient.subscribe(private_topic, 1, customCallback)
time.sleep(2)
pub_msg()
EOF

运行设备模拟程序。

python device_x509_mqtt.py \
--endpoint_prefix ${endpoint_prefix} \
--client_cert ./device_x509_mqtt.crt \
--client_key ./device_x509_mqtt.key

此设备模拟程序会一直运行,接受输入的数据,发送到AWS IoT Core,同时也订阅自己发送消息的topic。

输入要发送到 AWS IoT 的消息,如 “data from device X.509 MQTT.”,设备会接收到自己发送的这个消息。同时,在本系列文章第一篇的 4.3章节中打开的控制台中也可以看到此消息。

执行 Ctrl+C 停止程序。

3. 资源清理(可选)

target3=`aws iot list-targets-for-policy --policy-name IoTPolicyForDeviceX509Http | jq .targets[0] | sed 's/"//g'`
aws iot detach-policy --policy-name IoTPolicyForDeviceX509Http --target $target3
aws iot delete-policy --policy-name IoTPolicyForDeviceX509Http
cert_id3=`echo $target3| awk -F '/' '{print $2}'`
aws iot update-certificate --certificate-id $cert_id3 --new-status INACTIVE
aws iot delete-certificate --certificate-id $cert_id3
target4=`aws iot list-targets-for-policy --policy-name IoTPolicyForDeviceX509Mqtt | jq .targets[0] | sed 's/"//g'`
aws iot detach-policy --policy-name IoTPolicyForDeviceX509Mqtt --target $target4
aws iot delete-policy --policy-name IoTPolicyForDeviceX509Mqtt
cert_id4=`echo $target4| awk -F '/' '{print $2}'`
aws iot update-certificate --certificate-id $cert_id4 --new-status INACTIVE
aws iot delete-certificate --certificate-id $cert_id4

本篇作者

陈雷

AWS 高级解决方案架构师,负责基于 AWS 的云计算方案的咨询与架构设计,同时致力于物联网方面的研究和推广。在加入 AWS 之前,有超过10年的架构设计和软件开发的经验,在微服务,devops,物联网和大数据领域有丰富的经验。