MQTT协议云网关设备接入示例(阿里云FC认证)

设备通过MQTT协议云网关接入物联网平台时,设备身份可以由自定义证书认证,设备可以使用证书中CN信息进行注册。本文以MQTT协议云网关三方认证的阿里云FC认证为例,介绍如何将设备接入物联网平台。

使用前必读

本文操作步骤以普通用户权限为例。如果您在操作过程中涉及到管理员权限才能执行的操作,可尝试使用sudo命令执行。

前提条件

已购买尊享型企业版实例。本示例购买华东2(上海)地域的尊享型实例。具体操作,请参见购买企业版实例

背景信息

物联网平台提供MQTT云网关功能,支持设备通过函数计算提供的三方认证、自定义证书、OCSP、自定义通信Topic等能力认证并接入物联网平台进行通信,实现多种物联网业务场景。

MQTT协议云网关设备认证和通信说明,请参见MQTT协议云网关概述

准备工作

本文示例使用开发环境:Ubuntu 22.04版本操作系统。

步骤一:生成自定义证书

  1. 登录Ubuntu操作系统。

  2. 执行以下命令生成设备端和服务端的根证书文件root-ca.crt

    openssl req \
        -new \
        -newkey rsa:2048 \
        -days 365 \
        -nodes \
        -x509 \
        -subj "/C=CN/O=Aliyun IOT/CN=IoT CA" \
        -keyout root-ca.key \
        -out root-ca.crt
  3. 根据根证书文件root-ca.crt,自定义服务端证书。

    1. 执行以下命令生成服务端密钥文件server.key

      openssl genrsa -out server.key 2048
    2. 执行命令touch openssl.cnf新建文件openssl.cnf

    3. 执行命令vi openssl.cnf进入文件,填入以下内容后按Esc键,输入:wq保存。

      [policy_match]
      countryName             = cn
      stateOrProvinceName     = optional
      organizationName        = optional
      organizationalUnitName  = optional
      commonName              = supplied
      emailAddress            = optional
      
      [req]
      default_bits       = 2048
      distinguished_name = req_distinguished_name
      req_extensions     = req_ext
      x509_extensions    = v3_req
      prompt             = no
      
      [req_distinguished_name]
      commonName          = Server
      
      [req_ext]
      subjectAltName = @alt_names
      
      [v3_req]
      subjectAltName = @alt_names
      
      [alt_names]
      DNS.1 = *.mqtt.iothub.aliyuncs.com
      DNS.2 = *.igw.iothub.aliyuncs.com
    4. 执行以下命令生成服务端请求文件server.csr

      openssl req -new -key server.key -config openssl.cnf -out server.csr
    5. 执行以下命令生成服务端证书文件server.crt

      openssl x509 -req -days 365 -sha256 -in server.csr -CA root-ca.crt -CAkey root-ca.key -CAcreateserial -out server.crt -extensions v3_req -extfile openssl.cnf
    6. 执行以下命令验证服务端证书。

      openssl verify -CAfile root-ca.crt server.crt
  4. 根据根证书文件root-ca.crt,自定义设备端证书。

    1. 执行以下命令生成设备端密钥文件client.key

      openssl genrsa -out client.key 2048
    2. 执行以下命令生成设备端证书请求文件client.csr,设置CNClient_123

      openssl req -new -key client.key -out client.csr -subj "/CN=Client_123"
    3. 执行以下命令生成设备端证书文件client.crt

      openssl x509 -req -days 365 -sha256 -in client.csr -CA root-ca.crt -CAkey root-ca.key -CAcreateserial -out client.crt
    4. 执行以下命令验证设备端证书。

      openssl verify -CAfile root-ca.crt client.crt

自定义证书文件如下:

image

步骤二:创建设备认证的FC函数

  1. 登录函数计算控制台,在左侧导航栏,单击服务及函数

  2. 在顶部菜单栏,选择华东2(上海)地域,然后在服务列表页面,单击创建服务

  3. 创建服务面板,填写服务名称,例如:IoT_Service,然后单击确定。

  4. 函数管理页面,单击创建函数

  5. 创建函数页面,配置以下配置项,然后单击创建

    image
  6. 函数详情页面,将示例代码替换为以下代码,单击部署代码

    认证函数将设备证书的CN作为deviceName返回。

    # -*- coding: utf-8 -*-
    import logging
    import json
    import time
    import enum
    import random
    import string
    
    class Request:
        def __init__(self, json_str):
            self.clientId = None
            self.username = None
            self.password = None
            self.certificateCommonName = None
    
            for key, value in json.loads(json_str).items():
                setattr(self, key, value)
    
    class Response:
        def __init__(self):
            self.deviceName = None
            self.result = 'true'
            self.message = 'success'
    
        def handler(self, request):
            # 将设备证书的CN作为deviceName返回
            self.deviceName = request.certificateCommonName
            
            return json.dumps(self.__dict__)
    
    def handler(event, context):
        request = Request(event)
        return Response().handler(request)

步骤三:创建云网关产品

  1. 登录物联网平台控制台,在页面左上方选择华东2(上海)地域。

  2. 实例概览页面,单击目标尊享型实例。

  3. 在左侧导航栏,选择设备管理 > 云网关,单击添加云网关

  4. 配置如下信息,单击确定

    服务端证书server.crt服务端证书私钥server.key设备根证书root-ca.crt。参数详细说明,请参见添加云网关

    image
  5. 在云网关列表,复制网关URL保存。

    image

步骤四:开发设备端程序

  1. 返回Ubuntu操作系统。

  2. 执行以下命令,安装依赖库。

    sudo apt-get install build-essential gcc make cmake cmake-gui cmake-curses-gui
    sudo apt-get install libssl-dev
  3. 执行以下命令,安装MQTT开源库Paho。

    git clone https://github.com/eclipse/paho.mqtt.c.git
    mkdir build && cd build
    cmake ../paho.mqtt.c -DPAHO_WITH_SSL=TRUE -DCMAKE_INSTALL_PREFIX="/usr/lib"
    make -j
    sudo make install && cd ..
  4. 执行命令touch aiot_mqtt_demo.c新建设备模拟程序文件aiot_mqtt_demo.c

  5. 执行命令vi aiot_mqtt_demo.c进入文件,填入以下内容。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include "MQTTClient.h"
    
    int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
    {
        printf("message recv < topic [%s], payload [%s]\n", topicName, (char *)message->payload);
        MQTTClient_freeMessage(&message);
        MQTTClient_free(topicName);
        return 1;
    }
    
    int main(int argc, char* argv[])
    {
        MQTTClient client;
        MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
        MQTTClient_SSLOptions ssl_opts = MQTTClient_SSLOptions_initializer;
        int rc;
    
        /* 创建一个mqtt_client, 待替换 */
        const char *host = "ssl://iot-0****.igw.iothub.aliyuncs.com:1883";
        const char *client_id = "12345";
        MQTTClient_create(&client, host, client_id, MQTTCLIENT_PERSISTENCE_NONE, NULL);
    
        MQTTClient_setCallbacks(client, NULL, NULL, msgarrvd, NULL);
        /* 配置建连参数:证书、用户名、密码,待替换*/
        ssl_opts.trustStore = "root-ca.crt";
        ssl_opts.privateKey = "client.key";
        ssl_opts.keyStore = "client.crt";
    
        conn_opts.ssl = &ssl_opts;
        conn_opts.username = "sdk_test01";
        conn_opts.password = "hello123";
    
        /* mqtt完成建连 */
        if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
            printf("Failed to connect, return code %d\n", rc);
            exit(EXIT_FAILURE);
        }
        printf("connect success username %s, password %s\n", conn_opts.username, conn_opts.password);
    
        /* 订阅消息 */
        MQTTClient_subscribe(client, "/user/aiot_mqtt_demo_downraw", 1);
    
        /* 生成消息并循环发布消息 */
        MQTTClient_message pubmsg = MQTTClient_message_initializer;
        const char *topic = "/user/aiot_mqtt_demo_upraw";
        pubmsg.payload = "Hello Service!";
        pubmsg.payloadlen = (int)strlen(pubmsg.payload);
        pubmsg.qos = 1;
        for(int i = 0; i < 100; i++) {
            MQTTClient_publishMessage(client, topic, &pubmsg, NULL);
            printf("message send > topic [%s], payload [%s]\n", topic, (char *)pubmsg.payload);
            sleep(10);
        }
    
        /* 断开连接并删除client */
        MQTTClient_disconnect(client, 10000);
        MQTTClient_destroy(&client);
        return rc;
    }
  6. 在以上代码中修改实际设备相关参数,然后按Esc键,输入:wq保存文件aiot_mqtt_demo.c

    参数

    说明

    host

    MQTT云网关设备接入地址,格式为ssl://${网关接入地址}:${端口号}

    ${网关接入地址}${端口号}为本文“步骤三:创建云网关产品”中复制保存的网关URL中的域名和自定义端口号。

    client_id

    (可选)客户端ID,需自定义,长度不可超过64个字符。建议使用设备的MAC地址或SN码,方便您识别区分不同的客户端。

    ssl_opts.trustStore

    设备端根证书root-ca.crt的路径。

    ssl_opts.privateKey

    设备端密钥文件client.key的路径。

    ssl_opts.keyStore

    设备端证书文件client.crt的路径。

    conn_opts.username

    MQTT的userName。

    userName支持英文字母、数字、短划线(-)、下划线(_)、at(@)、英文句号(.)和英文冒号(:),长度限制为4~32个字符。userName在产品维度下不可重复。

    conn_opts.password

    MQTT的password。

    password支持英文字母、数字、短划线(-)、下划线(_)、at(@)、英文句号(.)和英文冒号(:),长度限制为1~32个字符。

本示例相关库的安装、证书的制作和设备程序的开发完成,如下所示:image

步骤五:编译运行

  1. 执行以下命令编译运行设备程序文件aiot_mqtt_demo.c

    gcc -o aiot_mqtt_demo aiot_mqtt_demo.c -lpaho-mqtt3cs
    ./aiot_mqtt_demo

    运行成功后,通过FC认证函数返回的deviceNameClient_123会作为物联平台设备的DeviceName,在物联网平台控制台实例下的设备管理 > 设备页面,自动创建设备Client_123,设备状态显示为在线image

  2. (可选)在物联网平台控制台实例下的监控运维 > 日志服务页面,查看设备上报数据。image