本文介绍如何基于Paho-MQTT集成IoT设备认证(ID²),通过身份认证、数据加密等功能,实现物联网数据安全上云。
1. 基于阿里云物联网平台使用:
本节说明在物联网设备(Linux)中通过Paho-MQTT集成IoT设备身份认证设备端SDK并接入阿里云物联网平台,通过身份认证、通道加密等功能,实现物联网数据安全上云。
1.1 创建产品:
只有认证方式为ID²的产品才可以使用IoT设备身份认证的服务,接下来我们需要创建一个新的产品并指定认证方式为ID²。
前提条件:
- 登录产品控制台,在左侧导航栏,选择常规>集成与部署 > IoT设备身份认证,单击组合使用(与阿里云物联网平台)卡片的开始接入按钮。
- 在配置产品页面创建新产品&分配ID²授权,然后单击下一步按钮。
- 请选择新增产品,并输入产品名称。
- ID²有效期:免费试用、或者付费授权。
- 分配ID²授权数量:请输入ID²授权数量。
1.2 选择设备认证算法:
IoT设备身份认证支持国际算法(AES-128、AES-192、AES-256)和国密算法(SM1-128、SM2-256、SM4-128),能满足企业不同安全等级的需求。
说明:
国密算法SM1需要配合ID²安全芯片使用,SM2用于业务数据完整性校验和加密。
ID²新增产品中默认选择的设备认证算法是AES-128;如需选择其他设备认证算法,请按如下步骤操作:
- 在产品控制台,选择常规>集成与部署 > IoT设备身份认证,单击独立使用(三方物联网平台)卡片的开始接入按钮。
- 在配置产品页面获取配置信息,然后单击下一步按钮。
- 选择产品:请选择现有产品,在下拉框中勾选产品名称。
- 在获取配置信息页面,单击设备认证算法后的更换按钮,查看支持的密钥类型(如SM4-128),并确认。
- 在产品详情页面,记录下实例ID、ProductKey、ProductSecret。
- 实例ID:阿里云物联网平台的实例标识。企业实例ID是字符串,公共实例ID是符号“-”。
- ProductKey:设备所属产品的ProductKey。
- ProductSecret:由IoT设备身份认证颁发的产品密钥,与 ProductKey 成对出现。
1.3 集成设备端SDK:
1.3.1 下载设备端SDK:
- 执行wget命令获取设备端SDK:
wget https://id2-schip-online.oss-cn-shanghai.aliyuncs.com/static_resources/id2_client_sdk/ID2_Client_SDK.tar
- 执行命令tar -xvf ID2_Client_SDK.tar完成解压,执行命令cd <解压SDK后的目录>。设备端SDK的目录说明见表格内容:
目录/文件 |
说明 |
demos |
ID²设备端的示例代码:
|
external |
用于存放外部的组件:
|
include |
ID²的头文件目录 |
libs |
ID²的静态库 |
make.rules |
编译规则文件,可配置编译工具链和编译参数 |
make.settings |
编译配置文件,可配置ID²的密钥类型(如AES、SM4) |
makefile |
编译脚本 |
src |
ID²的源码目录 |
tests |
ID²的测试用例,包括HAL和ID²的测试。 |
tools |
ID²的本地工具/脚本:
|
1.3.2 集成设备端SDK:
- 设备硬件及系统层集成。适配接口说明请参考ID²设备端SDK适配接口,包括:
- OSA接口适配:实现src/osa/ls_osa.c中的接口。
- HAL接口适配:实现src/hal/km/demo/ls_hal_km.c中的接口,通过hal_test测试用例验证(成功日志: “HAL KM Test Pass”)。
- 设备连接通道层集成。
- 使用默认的Paho SDK,请跳过此步骤。
- 下载Paho Embedded-C SDK,并且拷贝其中的源码到external/mqtt/paho/paho_embedded_c目录
git clone https://github.com/eclipse/paho.mqtt.embedded-c
- 设备应用层集成。
- 设备接入域名(mqtt_host)、MQTT服务器端口的配置:
设备接入域名:${YourProductKey}.itls.cn-shanghai.aliyuncs.com,其中${YourProductKey}为IoT设备身份认证产品的ProductKey MQTT服务器端口:1883
- 生成阿里云物联网平台MQTT Connect的参数:
- MQTT客户端的标识 - ClientId:
deviceId+"|securemode=8,signmethod=hmacsha1,timestamp=2524608000000,authtype=id2[,instanceId=xxx]|"
- deviceId:{deviceName} + "&" + {productName}
- instanceId:填写物联网平台的企业实例ID,如使用公共实例(短横线‘"-"),此字段忽略掉
- MQTT客户端的用户名 - UserName:
${deviceName} +“&”+${productKey}
- MQTT客户端的密码 - Password:
"0000000000..000"
- 适配Paho的网络抽象接口:
- 更新Network的结构体,且重新实现Network的接口,参考实现如下:
typedefstructNetwork { char*product_key; char*product_secret; uintptr_thandle; intmy_socket; int (*mqttread) (structNetwork*, unsignedchar*, int, int); int (*mqttwrite) (structNetwork*, unsignedchar*, int, int); } Network; intmqtt_itls_read(Network*n, unsignedchar*buffer, intlen, inttimeout_ms) { returnhal_itls_read(n->handle, buffer, len, timeout_ms); } intmqtt_itls_write(Network*n, unsignedchar*buffer, intlen, inttimeout_ms) { returnhal_itls_write(n->handle, buffer, len, timeout_ms); } voidNetworkSetConfig(Network*n, char*product_key, char*product_secret) { n->product_key=product_key; n->product_secret=product_secret; } voidNetworkInit(Network*n) { n->handle=0; n->mqttread=mqtt_itls_read; n->mqttwrite=mqtt_itls_write; } intNetworkConnect(Network*n, char*addr, intport) { n->handle=hal_itls_establish(addr, port, n->product_key, n->product_secret); if (n->handle==0) { return-1; } return0; } voidNetworkDisconnect(Network*n) { hal_itls_destroy(n->handle); }
- 设备端SDK的编译。
- 在ID² SDK的根目录,执行命令vi ./make.settings打开文件,修改如下参数:
- 配置CONFIG_LS_ID2_KEY_TYPE,选择同ID²产品选择的认证算法保持相同
- 配置CONFIG_LS_ID2_MQTT_NAME,选择使用Paho
- 执行命令vi demos/lpdemo/paho/mqtt_id2_demo.c打开文件,配置mqtt_id2_demo.c示例中的参数:
- 如选择使用Link SDK时,执行命令vi demos/mqtt/lk/mqtt_id2_demo.c打开文件:
- ProductKey:ID²产品的ProductKey。
- ProductSecret:ID²产品的ProductSecret。
- DeviceName:自定义,同一个产品下唯一。
- LP_Instance_ID:填写在ID²产品详情中获得的企业实例ID字符串;如果是公共实例,填写NULL
- 执行命令“make clean & make plat=xxx”进行编译
- Linux x86_64默认使用系统中的GCC作为编译工具,且默认指定“plat=x86_64”参数
- 如要编译其他架构,如armhf,在make.rules中配置编译工具,运行编译命令“make clean & make plat=armhf”
1.4 安全接入:
在完成设备端集成的基础上,运行设备应用, 或者演示示例接入阿里云物联网平台。
- 使用演示示例:
- 在Linux x86_64设备上下载ID² SDK,执行命令vi ./make.settings打开文件。
- 配置CONFIG_LS_ID2_KEY_TYPE,选择同ID²产品选择的认证算法保持相同
- 修改CONFIG_LS_ID2_MQTT_NAME的值为Paho
- 执行命令make clean & make进行编译。
- 编辑vi tools/ls_lp_demo.sh填写如下参数:
- ProductKey:ID²产品的ProductKey。
- ProductSecret:ID²产品的ProductSecret。
- DeviceName:自定义,同一个产品下唯一。
- InstanceId:填写在ID²产品详情中获得的企业实例ID字符串,如公共实例ID:“-”。
- 执行命令./tools/ls_lp_demo.sh运行设备端演示示例,查看设备端打印的日志。
- 在日志中,可以看到此设备上的ID² ID(“ 000FF...”)
- 基于ID²进行设备和LP平台之间的双向认证(“Verify iTLS Server AuthCode OK”),并建立设备和LP平台的TLS安全传输通道(ID²-iTLS);MQTT消息开始基于TLS通道进行加密传输(“Start to use itls to encrypt message buffer”)
- 查看设备状态:
- 登录产品控制台,在左侧导航栏,选择资产>设备,查看设备状态为正常,安全状态为安全。
2. 基于自建的物联网平台使用:
本节说明在物联网应用中集成ID²云端SDK、在物联网设备(Linux)中通过Paho-MQTT集成ID²设备端SDK,通过身份认证、业务数据加密等功能,实现物联网数据安全上云。
2.1 创建产品:
本章节描述了如何在IoT设备身份认证控制台创建产品并分配授权额度,包括:
- 创建产品
- 分配授权额度
前提条件:
- 登录产品控制台,在左侧导航栏,选择常规>集成与部署 > IoT设备身份认证,单击独立使用(三方物联网平台)卡片的开始接入按钮。
- 在配置产品页面创建新产品&分配ID²授权,然后单击下一步按钮。
- 选择产品:请选择创建新产品,并输入产品名称。
- ID²有效期:免费试用、或者付费授权。
- 分配ID²授权数量:请输入ID²授权数量。
2.2 选择设备认证算法:
IoT设备身份认证支持国际算法(AES-128、AES-192、AES-256)和国密算法(SM1-128、SM2-256、SM4-128),能满足企业不同安全等级的需求。
说明:
国密算法SM1需要配合ID²安全芯片使用,SM2用于业务数据完整性校验和加密。
ID²新增产品中默认选择的设备认证算法是AES-128;如需选择其他设备认证算法,请按如下步骤操作:
- 在查看配置信息页面,选择设备认证算法为SM4-128,单击提交按钮。
- 在查看配置信息页面,您需要记录下ProductKey、ProductSecret。
- ProductKey:设备所属产品的ProductKey。
- ProductSecret:由IoT设备身份认证颁发的产品密钥,与 ProductKey 成对出现。
- 单击下一步按钮进入集成云端SDK页面。
2.3 集成云端SDK:
2.3.1 阿里云账号的AccessKey:
- AccessKey是调用阿里云API的身份凭证,请参见获取AccessKey。
- 需要注意的是AccessKey的归属账号必须与创建产品时的账号保持一致。
2.3.2 下载云端SDK:
- 执行wget命令获取云端SDK:
wget https://id2-schip-online.oss-cn-shanghai.aliyuncs.com/static_resources/id2_server_sdk/ID2_Server_SDK.tar
- 在云端环境执行命令tar -xvf ID2_Server_SDK.tar 完成解压,执行命令cd <解压SDK后的目录>。设备端SDK的目录说明见表格内容:
目录/文件 |
说明 |
demos |
ID²云端的示例代码 |
lib |
ID²云端的Jar包 |
2.3.3 集成云端SDK
- 添加Maven项目依赖,引入阿里云Java SDK公共包。
<dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.5.6</version> </dependency>
- 导入云端SDK中的lib/aliyun-java-sdk-id2-1.1.4.jar到项目工程中,初始化云端SDK。
public static String ACCESS_KEY = null; public static String ACCESS_SECRET = null; public static String REGION_ID = "cn-shanghai"; public static String END_POINT = "id2." + REGION_ID + ".aliyuncs.com"; // Load ACCESS_KEY and ACCESS_SECRET From Config File loadConfigProperties("xxx.conf"); IClientProfile profile = DefaultProfile.getProfile(REGION_ID, ACCESS_KEY, ACCESS_SECRET); DefaultProfile.addEndpoint(REGION_ID, PRODUCT_CODE, END_POINT); client = new DefaultAcsClient(profile);
- 发起调用,ID²云端SDK为每个API封装了一个类,命名为${API名称}+"Request",如VerifyRequest,用于API的调用请求,ID²云端API列表请参考ID²云端API手册。
- ID²空发
OtpGetId2Request request = new OtpGetId2Request(); request.setDeviceAuthCode(authCode); request.setApiVersion(1.1.2); OtpGetId2Response response = client.getAcsResponse(request);
- 获取ID²认证挑战字
GetServerRandomRequest request = new GetServerRandomRequest(); request.setId2(id2Id); request.setApiVersion(1.1.2); GetServerRandomResponse response = client.getAcsResponse(request); System.out.println("GetServerRadom requestId:" + response.getRequestId());
- ID²设备认证和业务密钥加密
VerifyAndEncryptRequest request = new VerifyAndEncryptRequest(); request.setApiVersion(1.1.2); request.setProductKey(productKey); request.setId2(id2Id); request.setAuthCode(authCode); request.setData(keyInfo); VerifyAndEncryptResponse response = client.getAcsResponse(request); System.out.println("VerifyAndEncrypt requestId:" + response.getRequestId());
- 应用集成ID²云端SDK的示例代码,请参考demos/ID2SPDemo
2.4 集成设备端SDK:
2.4.1 下载设备端SDK:
- 执行wget命令获取设备端SDK:
wget https://id2-schip-daily.oss-cn-shanghai.aliyuncs.com/static_resources/ID2_Client_SDK.tar
- 执行命令tar -xvf ID2_Client_SDK.tar完成解压,执行命令cd <解压SDK后的目录>。设备端SDK的目录说明见表格内容:
目录/文件 |
说明 |
demos |
ID²设备端的示例代码:
|
external |
用于存放外部的组件:
|
include |
ID²的头文件目录 |
libs |
ID²的静态库 |
make.rules |
编译规则文件,可配置编译工具链和编译参数 |
make.settings |
编译配置文件,可配置ID²的密钥类型(如AES、SM4) |
makefile |
编译脚本 |
src |
ID²的源码目录 |
tests |
ID²的测试用例,包括HAL和ID²的测试。 |
tools |
ID²的本地工具/脚本:
|
2.4.2 集成设备端SDK:
- 设备硬件及系统层集成,详情请参考ID²设备端SDK适配接口
- OSA接口适配:实现src/osa/ls_osa.c中的接口。
- HAL接口适配:实现src/hal/km/demo/ls_hal_km.c中的接口,通过hal_test测试用例验证(成功日志: “HAL KM Test Pass”)。
- 设备应用层集成,详情请参考ID²设备端API手册
- 设备应用首先调用ID²的初始化函数,完成设备端SDK的初始化。
{ intret; ret=id2_client_init(); if (ret!=IROT_SUCCESS) { ls_osa_print("id2 client init fail, %d\n", ret); return-1; } }
- 获取ID²设备端的烧录状态:
- 如ID²已烧录(is_prov == true),退出ID²空发流程
{ intret=0; boolis_prov=false; ret=id2_client_get_prov_stat(&is_prov); if (ret!=IROT_SUCCESS) { ls_osa_print("id2 client get prov stat fail, %d\n", ret); return-1; } }
- 生成ID²设备端的空发认证码。
{ intret=0; uint8_tauth_code[ID2_MAX_AUTH_CODE_LEN] = {0}; uint32_tauth_code_len=ID2_MAX_AUTH_CODE_LEN; ret=id2_client_get_otp_auth_code( (uint8_t*)product_secret, (int)strlen(product_secret), auth_code, &auth_code_len); if (ret!=IROT_SUCCESS) { ls_osa_print("id2_client_get_otp_auth_code fail, %d\n", ret); return-1; } }
- ID²空发的网络请求和响应。
- authCode:ID²空发认证码的Base64编码
- otpData:ID²云端下发的空发数据(Base64格式)
# ID²空发请求:{commandId:xxx, authCode: xxx} # ID2空发响应:{commandId:xxx, otpData: xxx}
- 存储ID²密钥到设备安全存储区。
{ intret=0; ret=id2_client_load_otp_data(otp_data, otp_len); if (ret!=IROT_SUCCESS) { ls_osa_print("id2 load otp data fail, %d\n", ret); return-1; } }
- 获取设备端的ID² ID。
{ intret=0; uint8_tid2_id[ID2_ID_MAX_LEN+1] = {0}; uint32_tid2_id_len=ID2_ID_MAX_LEN; ret=id2_client_get_id(id2_id, &id2_id_len); if (ret!=IROT_SUCCESS) { ls_osa_print("id2 client get id fail, %d\n", ret); return-1; } }
- ID²认证挑战字的网络请求和响应。
# ID²认证挑战字的请求: {commandId:xxx, Id2Id: xxx} # ID2认证挑战字的响应: {commandId:xxx, challenge: xxx}
- 获取设备端的ID²认证码。
{ int ret = 0; uint8_t auth_code[ID2_MAX_AUTH_CODE_LEN] = {0}; uint32_t auth_code_len = ID2_MAX_AUTH_CODE_LEN; ret = id2_client_get_challenge_auth_code( challenge, NULL, 0, auth_code, &auth_code_len); if (ret != IROT_SUCCESS) { ls_osa_print("id2 client get challenge auth code fail, %d\n", ret); return -1; } }
- 设备端认证和数据加密的网络请求和响应。
- cipherData:ID²云端下发的加密数据
# ID²设备认证和数据加密的请求:{commandId:xxx, productKey:xxx, Id2Id:xxx, authCode:xxx} # ID2设备认证和数据加密的响应:{commandId:xxx, cipherData: xxx}
- 解密ID²云端加密下发的数据。
{ intret=0; ret=id2_client_decrypt(cipher_data, cipher_len, cipher_data, &cipher_len); if (ret!=IROT_SUCCESS) { ls_osa_print("id2 client decrypt fail\n"); return-1; } }
- 应用集成ID²设备SDK的示例代码,请参考demos/spdemo
- 设备端SDK的编译:
- 在ID² SDK的根目录,执行命令vi ./make.settings打开文件, 修改CONFIG_LS_ID2_KEY_TYPE的值
- 同ID²产品选择的认证算法保持相同
- 执行命令“make clean & make plat=xxx”进行编译
- Linux x86_64默认使用系统中的GCC作为编译工具,且默认指定“plat=x86_64”参数
- 如要编译其他架构,如armhf,在make.rules中配置编译工具,运行编译命令“make clean & make plat=armhf”
2.5 业务数据加密:
在完成设备端集成的基础上,运行设备应用, 或者使用演示示例,进行ID²认证和加密,以及业务数据加密的调试。
- 使用演示示例:
- 部署EMQX物联网平台,详情参见EMQX安装和部署
- 本节描述在Ubuntu 22.04上通过Docker部署EMQX物联网平台的示例
- 安装和启动Docker工具:
# Install Dockercurl-fsSLget.docker.com -o get-docker.sh sudosh get-docker.sh --mirror Aliyun # Start Dockersudo systemctl enable docker sudo systemctl start docker # Get Docker Versiondocker -v
- 使用Docker安装EMQX物联网平台:
# Get emqx-5.1.3 docker imagedocker pull emqx/emqx:5.1.3 # Start docker emqx instancedocker run -d--name emqx -p1883:1883 emqx/emqx:5.1.3 # Get docker instance infodocker ps
- 部署云端演示示例,在ID²云端SDK根目录,配置vi demos/ID2SPMessage/Id2SpDemo.conf文件中的AccessKey和AccessSecret信息。
- 执行命令cd demos/ID2SPMessage,执行命令java -jar Id2SpDemo.jar启动云端演示示例。
- 在ID²设备端SDK目录,执行命令vi tools/ls_sp_demo.sh填写如下参数:
- HostAddr:云端演示示例的IP地址,localhost代表本机网络。
- ProductKey:ID²产品的ProductKey。
- ProductSecret:ID²产品的ProductSecret。
- PublishData:设备端应用上报的业务数据,建议不超过1024字节。
- 执行命令./tools/ls_sp_demo.sh运行设备端演示示例,查看设备端打印的日志。
- 在设备端日志中,可以看到完成ID²的设备认证,以及通过ID²加密下发SP Key Info;设备端通过ID²设备端接口解密后,获得明文的Key ID和Data
- 在设备端日志中,可看到由SPKey加密下发的业务数据(“SP Server Subscribed Cipher Data”);通过在调用SPKey解密后,可得到明文的下行业务数据(“SP_Server_Hello”)
- 在云端查看打印的日志。
- 可看到由SPKey加密的业务数据("Device Published Cipher Data")
- 使用SPKey解密后,可得到正确的上行业务数据(“SP_Client_Hello”)
- 查看设备状态:
- 登录产品控制台,在左侧导航栏,选择资产>设备,查看设备状态为正常,安全状态为安全。
3. 延申阅读:
- IoT设备身份认证产品试用,请查看IoT设备身份认证免费试用
- 常见问题以及处理方案,请查看常见问题。
- 使用虚拟机模拟物联网设备,请查看最佳实践。
- 在实际业务环境中使用ID²的最佳实践,请查看最佳实践。
- 在Android、RTOS设备中使用IoT设备身份认证,请查看最佳实践。
- IoT设备身份认证的计量计费规则说明,请查看计费说明。
如有任何问题和疑问,欢迎在下面留言