基于AI-WB2实现MQTTS(MQTT-SSL)单向+双向加密传输
前言
你将学到什么
通过本教程,你将掌握以下核心技能:
- ✅ 理解 MQTT over SSL/TLS(MQTTS)的安全传输原理
- ✅ 掌握单向认证和双向认证的区别及应用场景
- ✅ 使用 OpenSSL (Git内置)生成 SSL/TLS 证书
- ✅ 配置 EMQX 云服务端和 MQTTX 客户端
- ✅ 在 Ai-WB2 开发板上实现 MQTTS 双向加密通信
- ✅ 通过 MQTT 消息控制开发板 RGB LED 点亮
MQTTS(MQTT over SSL/TLS)加密传输
在物联网应用中,设备传输的数据往往涉及敏感信息(如控制指令、传感器数据),因此 安全性 至关重要。MQTT 本身不提供加密功能,需要依赖传输层安全协议。
1️⃣ SSL/TLS 基础
SSL(Secure Sockets Layer) 和 TLS(Transport Layer Security) 是为网络通信提供安全及数据完整性的加密协议。TLS 是 SSL 的继任者,目前广泛使用的是 TLS 1.2 和 TLS 1.3。
TLS 提供的安全保障:
- 加密:防止数据被窃听(对称加密传输数据)
- 身份认证:验证通信双方身份(非对称加密交换密钥)
- 完整性:防止数据被篡改(消息认证码 MAC)
MQTT over TLS(MQTTS)协议栈:
应用层:MQTT 协议
安全层:SSL/TLS(加密/解密、身份认证)
传输层:TCP 协议
网络层:IP 协议
MQTTS 默认端口为 8883(MQTT 为 1883)。
2️⃣ 单向认证(One-way Authentication)
单向认证 是指 客户端验证服务器身份,服务器不验证客户端身份。这是最常见的 TLS 配置方式。
工作原理:
- 客户端发起 TLS 连接请求
- 服务器返回自己的证书(包含公钥)
- 客户端验证服务器证书的合法性(检查 CA 签名、有效期、域名匹配等)
- 验证通过后,客户端生成会话密钥,用服务器公钥加密后发送
- 服务器用私钥解密获取会话密钥
- 双方使用会话密钥进行对称加密通信
证书需求:
| 角色 |
需要的证书 |
| 服务器 |
服务器证书(含公钥)+ 私钥 |
| 客户端 |
CA 根证书(用于验证服务器证书) |
适用场景:
- 公共物联网平台(设备只需确认连接到正确的服务器)
- 设备数量庞大,难以管理每个设备的证书
- 对安全性要求不是极端严格的场景
优点:配置简单,客户端只需存储 CA 证书
缺点:服务器无法验证客户端身份,存在被未授权设备连接的风险
3️⃣ 双向认证(Mutual Authentication / mTLS)
双向认证 是指 客户端和服务器互相验证对方身份,提供最高级别的安全性。
工作原理:
- 客户端发起 TLS 连接请求
- 服务器返回自己的证书
- 客户端验证服务器证书
- 服务器请求客户端证书
- 客户端发送自己的证书
- 服务器验证客户端证书(检查 CA 签名、有效期、是否吊销等)
- 双方互相验证通过后,交换会话密钥
- 使用会话密钥进行加密通信
证书需求:
| 角色 |
需要的证书 |
| 服务器 |
服务器证书 + 私钥 + CA 证书(验证客户端) |
| 客户端 |
客户端证书 + 私钥 + CA 证书(验证服务器) |
适用场景:
- 金融、医疗等高安全要求的行业
- 工业控制系统(防止未授权设备接入)
- 需要精确控制设备接入权限的场景
优点:最高安全性,服务器可精确识别每个客户端
缺点:证书管理复杂,每个设备需要独立证书,证书分发和更新成本高
4️⃣ 证书类型与选型
| 类型 |
说明 |
适用场景 |
| DV SSL |
域名验证证书,仅验证域名所有权 |
测试环境、非核心应用 |
| OV SSL |
组织验证证书,验证组织身份 |
企业应用、工业物联网 |
| EV SSL |
扩展验证证书,最高级别验证 |
金融、关键基础设施 |
| 自签名证书 |
自己生成的证书,无第三方 CA |
内网测试、开发环境 |
密钥算法选择:
- RSA:兼容性好,应用广泛,但密钥较长、计算开销大
- ECC(椭圆曲线):密钥短、计算快、功耗低,适合资源受限的物联网设备
一、准备工作
📦 硬件与软件清单
| 分类 |
项目 |
数量/版本 |
说明 |
| 硬件 |
Ai-WB2-12F 开发板 |
1 |
核心控制 MCU |
|
Type-C 数据线 |
1 |
烧录程序与调试 |
| 软件 |
安信可 Ai-WB2 开源仓库 |
最新版 |
Gitee |
|
MQTTX 客户端 |
最新版 |
官网下载 |
|
EMQX 服务端 |
最新版 |
点击跳转 |
|
Git(内置OpenSSL) |
最新版 |
官网下载 |
⏱️ 预计完成时间:约 2-3 小时(包含证书生成、配置、编译和测试)
📋 前置要求
- 熟悉 GPIO 控制和 FreeRTOS
- 对 Linux、make、Git 指令有一定的了解
- 对回调函数、JSON、MQTT 协议有一定了解
二、克隆完整仓库
1️⃣ 克隆仓库
以下指令可直接一键克隆完整 Git 仓库和子模块,也可点击链接跳转直接下载 ZIP 包:(⚠️务必克隆最新仓库!)
# Gitee
git clone --recursive https://gitee.com/Ai-Thinker-Open/Ai-Thinker-WB2.git
🔴 提示:该仓库包含子模块内容较大(约几百兆),需要耐心等待克隆完成。
2️⃣ 检查仓库完整性
克隆下来的完整文件如图,重点关注图中红框部分的子模块包含内容是否完整无缺失:

图:Ai-WB2 项目仓库目录结构,红框标注的 toolchain/riscv 子模块应包含 Darwin、Linux、MSYS 三个文件夹且文件夹内应包含有内容。
三、注册配置EMQX服务端
3️⃣ 注册 EMQX 服务端
- 打开 EMQX 官网注册登录后创建一个新的 MQTT 连接实例,选择
专有版 的云服务区域和配置(免费套餐即可),完成实例创建点击进入后记下连接地址(例如 xx.aliyun.emqxcloud.cn)和端口(默认 8883)。

图:EMQX 服务端连接信息界面,显示服务器地址、端口信息及 CA 证书下载按钮。
4️⃣ 配置客户端认证参数
- 在左侧栏中点击
访问控制 → 客户端认证,点击 添加 按钮,创建两个用户与对应密码信息用于后续设备连接:

图:EMQX 客户端认证配置界面,红框和箭头标注了从进入「客户端认证」页面到点击「+添加」按钮,再到输入账号密码的完整操作流程。
四、认证证书生成与配置
该步骤为双向认证所需配置,如果你电脑里没有 Git,请去上述官网下载并安装(一路默认 Next 即可)。它内置了 OpenSSL,不需要繁琐的 Windows 环境变量配置。
1️⃣ 生成 CA 根证书
- 首先生成 CA 根证书和私钥,建议创建一个专门用于存放生成证书的文件夹
ssl_certs 在该文件夹右键 → 选择 "Open Git Bash here" 打开,复制以下命令到 Git Bash 中执行:
# 生成一个 2048 位的 RSA 私钥,命名为 ca.key
# 这里的 -out 表示输出文件名
openssl genrsa -out ca.key 2048
- 生成 CA 根证书,命令如下:
# 使用刚才的私钥 (ca.key) 生成一张自签名的证书 (ca.pem)
# -x509: 生成自签名证书的标准格式
# -days 3650: 证书有效期 10 年,避免频繁过期
# -subj: 填写证书信息,CN=MyRootCA 表示发证机构的名字
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem -subj "//CN=MyRootCA"
产出文件:ca.key(严加保管),ca.pem(发证机关的公开执照)。
2️⃣ 生成服务器证书
这套证书是给你的 MQTT 服务器(EMQX Cloud)用的,证明它是合法的服务器。
- 生成服务器私钥:
# 生成服务端的私钥,命名为 server.key
openssl genrsa -out server.key 2048
- 生成服务端签名请求(CSR):
⚠️ 注意:为了稳妥,Common Name(CN)最好填写你创建的EMQX连接地址(例如 xx.aliyun.emqxcloud.cn),或者是你的自定义域名。
# 生成请求文件 server.csr
# CN=xxx 替换为 3.1 中的连接地址
openssl req -new -key server.key -out server.csr -subj "//CN=XXX.cn"
- 使用 CA 给服务端颁发证书:
# CA 拿出它的私钥 (ca.key) 和证书 (ca.pem) 给服务端的请求 (server.csr) 盖章
# 生成最终的服务端公钥证书: server.pem
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.pem -days 3650 -sha256
产出文件:server.key,server.pem
3️⃣ 生成客户端证书
双向认证的核心在于:设备也要出示证件。
- 生成客户端私钥:
# 生成客户端私钥,命名为 client.key
openssl genrsa -out client.key 2048
- 生成客户端签名请求:
# 生成请求文件 client.csr
# CN=client1 这里可以代表设备的 ID
openssl req -new -key client.key -out client.csr -subj "//CN=client1"
- 使用 CA 给客户端颁发证书:
# CA 再次出马,给客户端盖章
# 生成最终的客户端公钥证书: client.pem
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem -days 3650 -sha256
产出文件:client.key,client.pem
此时你的 ssl_certs 文件夹将有以下文件,对应不同角色的证书和私钥:

图:生成的证书文件列表,包含 CA 根证书、服务器证书和客户端证书及其对应的私钥文件。
五、证书上传与 EMQX 配置
该步骤为双向认证所需步骤。
1️⃣ 上传服务器证书到 EMQX

图:EMQX 双向 TLS/SSL 证书配置界面,红框标注选择"双向"认证类型,红色箭头指引依次上传服务器端 .pem 文件、服务器端 .key 文件和 CA 证书文件。
2️⃣ 配置 MQTTX 使用客户端证书
单向认证配置如下:

图:MQTTX 单向认证连接配置界面。
双向认证配置如下:

图:MQTTX 双向认证连接配置界面,红框和箭头标注用户名密码输入、证书配置区域的提示文字以及右上角"连接"按钮。
3️⃣ 配置 AI-WB2 使用客户端证书
- 需要将客户端证书中的内容复制替换到例程中的证书宏定义中,期间需要涉及到大量的修改格式和转义字符的工作,以下推荐一种一键修改的方法:
在 Git Bash 中我们可以利用 Linux 经典的 sed 工具,一行指令直接生成你想要的宏定义格式。在你的证书目录下右击打开 Git Bash 并运行:
# 将 ca.pem 处理并输出到 ca_config.h 文件中
# s/$/\\r\\n\\/ : 在每行末尾加上 \r\n\
# s/^/\"/ : 在每行开头加上 "
# s/$/\"/ : 在每行末尾(刚才的斜杠前)加上 "
sed -e 's/^/"/' -e 's/$/\\r\\n\\"/' ca.pem > ca_config.h
此时打开 ca_config.h 文件,可以看到内容已经转换为宏定义格式。
⚠️ 注意:最后一行末尾的 \ 需要手动删掉,然后在前后加上 #define 以及换号转义 \,将修改好格式的证书内容分别复制替换到例程中 Ai-Thinker-WB2/applications/protocols/mqtt/ssl/ssl/demo.c 的宏定义:EMQX_CA_CRT、CLIENT_CRT、CLIENT_KEY 即可。
六、点灯代码配置与编译
1️⃣ 配置 WiFi 路由器信息
在 Ai-Thinker-WB2/applications/protocols/mqtt/ssl/ssl/main.c 文件下,修改两个宏定义:ROUTER_SSID 和 ROUTER_PWD,填入你的 WiFi 名称和密码:
// 路由器配置信息(需要用户根据实际情况修改)
#define ROUTER_SSID "WIFI" // 填入你的 WiFi 名称
#define ROUTER_PWD "1234567890" // 填入你的 WiFi 密码
2️⃣ 添加 RGB LED 控制代码
📌 关键配置点:引入头文件与定义引脚
在 Ai-Thinker-WB2/applications/protocols/mqtt/ssl/ssl/demo.c 文件下,增加以下代码。在 #include <task.h> 以下添加以下头文件和宏定义:
#include "cJSON.h" // JSON 解析库
#include <bl_gpio.h> // GPIO 控制库
// RGB LED 引脚定义(根据实际硬件连接修改)
#define GPIO_LED_RED 14
#define GPIO_LED_GREEN 17
#define GPIO_LED_BLUE 3
📌 关键配置点:LED 状态枚举与任务实现
紧接着在 #define EMQX_CA_CRT 函数上添加以下代码:
/* ================= LED 指示灯业务逻辑 ================= */
typedef enum {
LED_OFF = 0, // 闲置(全灭)
LED_BLUE, // 蓝灯常亮
LED_GREEN, // 绿灯常亮
LED_RED // 红灯常亮
} led_indicator_state_t;
static volatile led_indicator_state_t g_led_state = LED_OFF;
// 设置 RGB LED 的亮灭状态
static void led_set_level(uint8_t r, uint8_t g, uint8_t b) {
bl_gpio_output_set(GPIO_LED_RED, r);
bl_gpio_output_set(GPIO_LED_GREEN, g);
bl_gpio_output_set(GPIO_LED_BLUE, b);
}
// LED 指示灯任务(FreeRTOS 任务)
static void led_indicator_task(void *pvParameters) {
// 初始化 GPIO 为输出模式
bl_gpio_enable_output(GPIO_LED_RED, 0, 0);
bl_gpio_enable_output(GPIO_LED_GREEN, 0, 0);
bl_gpio_enable_output(GPIO_LED_BLUE, 0, 0);
led_set_level(0, 0, 0); // 初始状态:全灭
while (1) {
switch (g_led_state) {
case LED_BLUE: // 蓝灯常亮
led_set_level(0, 0, 1);
vTaskDelay(pdMS_TO_TICKS(500));
break;
case LED_GREEN: // 绿灯常亮
led_set_level(0, 1, 0);
vTaskDelay(pdMS_TO_TICKS(500));
break;
case LED_RED: // 红灯常亮
led_set_level(1, 0, 0);
vTaskDelay(pdMS_TO_TICKS(500));
break;
case LED_OFF: // 全灭
default:
led_set_level(0, 0, 0);
vTaskDelay(pdMS_TO_TICKS(500));
break;
}
}
}
📌 关键配置点:MQTT 客户端参数配置
在 mqtt_start(void) 函数中配置 MQTT 客户端参数:
/*
* 配置 MQTT 客户端参数
* uri: MQTT 代理服务器地址,使用 mqtts:// 协议(加密)
* event_handle: 设置事件回调函数,用于接收连接状态和数据
*/
void mqtt_start(void)
{
axk_mqtt_client_config_t mqtt_cfg = {
.uri = "mqtts://xx.aliyun.emqxcloud.cn:8883", // Broker 服务器地址与端口
.cert_pem = EMQX_CA_CRT, // CA 根证书内容(单向认证时需要,双向认证时也需要)
.cert_len = sizeof(EMQX_CA_CRT), // 证书长度
.client_cert_pem = CLIENT_CRT, // 客户端证书内容(双向认证时需要,单证时注释)
.client_key_pem = CLIENT_KEY, // 客户端私钥内容(双向认证时需要,单证时注释)
.username = "wb2-12f", // 用户名(必须与服务器端设置保持一致)
.password = "12345678", // 密码(必须与服务器端设置保持一致)
.client_id = "Ai-Thinker-WB2", // ID 用于区分不同设备,必须唯一
.event_handle = event_cb, // 事件回调函数
/* 注意:启动是异步的,连接成功/失败会通过 event_cb 回调通知 */
};
// 初始化 MQTT 客户端并启动连接
axk_mqtt_client_handle_t client = axk_mqtt_client_init(&mqtt_cfg);
axk_mqtt_client_start(client);
// LED 指示灯任务创建
static TaskHandle_t led_task_handle;
xTaskCreate(led_indicator_task, "led", 512, NULL, 10, &led_task_handle);
}
📌 关键配置点:MQTT 事件回调函数实现
修改回调函数 event_cb(axk_mqtt_event_handle_t event),实现以下三个关键事件:
/* ==================== MQTT 连接成功事件 ==================== */
case MQTT_EVENT_CONNECTED:
blog_info("MQTT_EVENT_CONNECTED");
// 订阅主题 "/topic/qos0",QoS 等级 0(最多送达一次,不保证送达)
msg_id = axk_mqtt_client_subscribe(client, "/topic/qos0", 0);
blog_info("sent subscribe successful, msg_id=%d", msg_id);
break;
/* ==================== 订阅成功确认事件 ==================== */
case MQTT_EVENT_SUBSCRIBED:
blog_info("MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
// 订阅成功后,立即向该主题发布一条 QoS 0 的消息 "hello,MQTT!"
msg_id = axk_mqtt_client_publish(client, "/topic/qos", "hello,MQTT!", 0, 0, 0);
blog_info("sent publish successful, msg_id=%d", msg_id);
break;
/* ==================== 收到 MQTT 消息数据事件 ==================== */
case MQTT_EVENT_DATA:
blog_info("MQTT_EVENT_DATA");
// 1. 解析 JSON 字符串为 cJSON 对象
cJSON *root = cJSON_Parse(event->data);
if (root == NULL) {
printf("Error parsing JSON!\n");
break;
}
// 2. 获取 LED 字段
cJSON *LED = cJSON_GetObjectItem(root, "LED");
// 3. 访问值并进行类型检查
if (LED && LED->type == cJSON_String) {
if (strcmp(LED->valuestring, "LED_RED") == 0) {
g_led_state = LED_RED;
printf("LED_RED_ON");
}
else if (strcmp(LED->valuestring, "LED_GREEN") == 0) {
g_led_state = LED_GREEN;
printf("LED_GREEN_ON");
}
else if (strcmp(LED->valuestring, "LED_BLUE") == 0) {
g_led_state = LED_BLUE;
printf("LED_BLUE_ON");
}
else if (strcmp(LED->valuestring, "LED_OFF") == 0) {
g_led_state = LED_OFF;
printf("LED_OFF");
}
else {
printf("LED ERROR");
}
}
// 4. 核心:必须手动释放 cJSON 内存!
cJSON_Delete(root);
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
3️⃣ 编译与烧录
保存代码后,执行以下命令进行编译与烧录:
make flash p=/dev/ttyUSB0
编译过程如下:

图:编译成功提示,生成 tcp.bin 二进制文件。
烧录过程如下:

图:烧录成功日志,红色标注显示 "[All Success]"。
按下复位键重启开发板,打开串口助手,波特率设置为 115200,查看串口打印信息:

图:串口日志显示 MQTT 连接成功、订阅成功、发布成功的消息。
七、测试验证
1️⃣ 添加主题订阅
点击左侧 "+ 添加订阅"按钮,填写订阅信息:

图:添加订阅对话框。
| 配置项 |
值 |
说明 |
| Topic |
/topic/qos0 |
订阅的主题名称 |
| QoS |
0(最多一次) |
消息服务质量等级 |
2️⃣ 发布消息测试
向主题 /topic/qos0 发布 JSON 格式消息,控制 LED 亮灭:
发布红色 LED:
{
"LED": "LED_RED"
}
发布绿色 LED:
{
"LED": "LED_GREEN"
}
发布蓝色 LED:
{
"LED": "LED_BLUE"
}
关闭 LED:
{
"LED": "LED_OFF"
}
发布消息后,可在串口助手中看到相应的打印信息,开发板 RGB 灯也会对应亮起:

图:MQTTX 消息收发界面,显示订阅主题、已发布和已接收的消息。

图:Ai-WB2 开发板 RGB LED 点亮效果(绿色)。
3️⃣ 常见错误排查
⚠️ 问题 1:编译报错 "command not found: make"
原因:系统未安装 make 工具或未正确配置环境变量。
解决方法:
- Windows 用户:确保安装了 Git Bash 并在其中运行
make 命令
- 检查是否在项目根目录下执行命令
⚠️ 问题 2:MQTT 连接失败,串口打印连接超时
可能原因:
- WiFi 路由器名称或密码错误
- EMQX 服务器地址或端口配置错误
- 证书内容格式不正确
解决方法:
- 检查
ROUTER_SSID 和 ROUTER_PWD 宏定义是否正确
- 确认 EMQX 服务器地址和端口是否与控制台一致
- 重新使用
sed 命令生成证书宏定义,确保转义字符正确
⚠️ 问题 3:双向认证连接失败,提示证书验证错误
可能原因:
- 服务器证书的 Common Name 与连接地址不匹配
- CA 证书未正确上传到 EMQX
- 客户端证书与私钥不匹配
解决方法:
- 重新生成服务器证书,确保 CN 与连接地址一致
- 检查 EMQX 的 TLS/SSL 配置,确认证书已正确上传
- 确保客户端证书和私钥是同一对生成的证书文件
⚠️ 问题 4:LED 不响应 MQTT 消息
可能原因:
- JSON 格式错误
- 主题名称不匹配
- 引脚定义与实际硬件连接不一致
解决方法:
- 检查 MQTTX 发布的 JSON 消息格式是否正确(注意双引号和逗号)
- 确认订阅主题与发布主题一致
- 检查
GPIO_LED_RED、GPIO_LED_GREEN、GPIO_LED_BLUE 的引脚定义
总结
至此,基于 Ai-WB2 使用 MQTT 协议实现订阅、发布及点灯功能的全部内容已完成!
本教程完整覆盖了:
- ✅ 单向认证:客户端验证服务器,适用于公共物联网平台
- ✅ 双向认证:客户端和服务器互相验证,适用于高安全要求的场景
- ✅ 证书生成:使用 OpenSSL (Gitn内置)生成完整的证书链
- ✅ EMQX 配置:服务端和客户端认证信息配置
- ✅ MQTTX 测试:模拟设备连接和消息收发
- ✅ RGB LED 控制:通过 MQTT 消息控制硬件
如有问题或建议,欢迎在评论区留言讨论!