ESP32 SSL/TLS 双向认证实践

本文详细介绍了如何在ESP32上实现HTTPS的双向认证,包括客户端和服务器端证书的生成、服务器端和客户端代码的编写,以及测试验证过程。通过ESP-IDF示例,展示了如何在代码中加载和使用自签名证书,确保安全连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SSL/TLS 双向认证指的是相互校验,服务器需要校验每个client,client也需要校验服务器。此篇文章使用两个 ESP32 分别做 HTTPS server 和 HTTPS client 来尝试了解 ESP32 HTTPS 双向认证的实现流程。分为以下四部分:

  • 客户端以及服务器端的证书生成
  • 服务器端代码编写
  • 客户端代码编写
  • 测试验证
  • 附录

1 客户端以及服务器端的证书生成

在双向认证前,需要生成客户端和服务器端的以下证书:

  • server 需要 server.key 、server.crt 、ca.crt(client 端 CA 证书)
  • client 需要 client.key 、client.crt 、ca.crt(server 端 CA 证书)
    上述证书可以通过 openssl 指令生成,在此也可以使用总结好的脚本 gen_crt.sh,内容如下:
#!/bin/bash

#
# Generate the certificates and keys for testing.
#

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

ROOT_SUBJECT="/C=C1/ST=JS1/L=WX1/O=ESP1/OU=ESP1/CN=Server1 CA/emailAddress=ESP1"
LEVEL2_SUBJECT="/C=C2/ST=JS22/L=WX22/O=ESP22/OU=ESP22/CN=Server22 CA/emailAddress=ESP22"
LEVEL3_SUBJECT="/C=C3/ST=JS333/L=WX333/O=ESP333/OU=ESP333/CN=Server333 CA/emailAddress=ESP333"

# private key generation
openssl genrsa -out ca.key 2048
openssl genrsa -out server.key 2048
openssl genrsa -out client.key 2048

# cert requests
openssl req -new -key ca.key -out ca.csr -text -subj $ROOT_SUBJECT
openssl req -new -key server.key -out server.csr -text -subj $LEVEL2_SUBJECT
openssl req -new -key client.key -out client.csr -text -subj $LEVEL3_SUBJECT

# generate the actual certs.
openssl x509 -req -in ca.csr -out ca.pem -sha256 -days 5000 -signkey ca.key -text -extensions v3_ca
openssl x509 -req -in server.csr -out server.pem -sha256 -CAcreateserial -days 5000 -CA ca.pem -CAkey ca.key -text -extensions v3_ca
openssl x509 -req -in client.csr -out client.pem -sha256 -CAcreateserial -days 5000 -CA ca.pem -CAkey ca.key -text -extensions v3_ca

rm *.csr
rm *.srl

mv ca.* ./main
mv server.* ./main
mv client.* ./main

具体的使用方式为:将上述脚本 gen_crt.sh 放入您需要生成 ESP HTTPS 客户端或服务器证书的 ESP-IDF 工程下,打开工程对应的终端,输入

. ./gen_crt.sh

就可以看到以下 log 信息:

[11:54:03] [~/github/esp-idf/rel4.4/esp-idf/examples/protocols/esp_http_client] git(1329b19fe4) 🔥 ❱❱❱ . ./gen_crt.sh
Generating RSA private key, 2048 bit long modulus (2 primes)
...........................................................+++++
.................+++++
e is 65537 (0x010001)
Generating RSA private key, 2048 bit long modulus (2 primes)
....+++++
...........................................................................................+++++
e is 65537 (0x010001)
Generating RSA private key, 2048 bit long modulus (2 primes)
.............................................+++++
......+++++
e is 65537 (0x010001)
Can't load /home/zhengzhong/.rnd into RNG
140139168797120:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/zhengzhong/.rnd
Can't load /home/zhengzhong/.rnd into RNG
140396157870528:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/zhengzhong/.rnd
Can't load /home/zhengzhong/.rnd into RNG
140703026512320:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/zhengzhong/.rnd
Signature ok
subject=C = C1, ST = JS1, L = WX1, O = ESP1, OU = ESP1, CN = Server1 CA, emailAddress = ESP1
Getting Private key
Signature ok
subject=C = C2, ST = JS22, L = WX22, O = ESP22, OU = ESP22, CN = Server22 CA, emailAddress = ESP22
Getting CA Private Key
Signature ok
subject=C = C3, ST = JS333, L = WX333, O = ESP333, OU = ESP333, CN = Server333 CA, emailAddress = ESP333
Getting CA Private Key

脚本运行完毕后,即可看到 ESP-IDF 对应工程下的 main 文件夹里新增了一些证书,如下:
请添加图片描述

到此,证书生成的步骤已经完成。此时:

  • 客户端需要的证书为 ca.pem, client.pem, client.key
  • 服务器端需要的证书为 ca.pem, server.pem, server.key

2 服务器端代码编写

这部分以 ESP-IDF v4.4.1 里的 https_server/simple example 为例,首先需要确保第一步生成的对应 ca.pem, server.pem, server.key 证书放在了 main 文件夹下:
请添加图片描述
然后需要让代码成功加载这些证书并使用,首先修改项目 main 文件夹下 CMakeLists.txt 为如下:

idf_component_register(SRCS "main.c"
                    INCLUDE_DIRS "."
                    EMBED_TXTFILES "ca.pem"
                                   "server.pem"
                                   "server.key")

然后替换掉 main.c 里原有的证书,修改后如下:

   extern const unsigned char cacert_pem_start[] asm("_binary_server_pem_start");
    extern const unsigned char cacert_pem_end[]   asm("_binary_server_pem_end");
    conf.cacert_pem = cacert_pem_start;
    conf.cacert_len = cacert_pem_end - cacert_pem_start;

    extern const unsigned char prvtkey_pem_start[] asm("_binary_server_key_start");
    extern const unsigned char prvtkey_pem_end[]   asm("_binary_server_key_end");
    conf.prvtkey_pem = prvtkey_pem_start;
    conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start;

    extern const unsigned char client_pem_start[] asm("_binary_ca_pem_start");
    extern const unsigned char client_pem_end[]   asm("_binary_ca_pem_end");
    conf.client_verify_cert_pem = client_pem_start;
    conf.client_verify_cert_len = client_pem_end - client_pem_start;

具体修改的代码地点请参考下图。
请添加图片描述
然后在 menuconfig 里配置 Wi-Fi SSID 和 Wi-Fi Password 后,烧录程序进 ESP32 即可。不过此时需要从 HTTPS server 运行的 log 里找到 server 的 IP 地址,方便 HTTPS client 后续进行连接。

在这次实践中,HTTPS server 的 IP 为 192.168.1.109。如下:
请添加图片描述
到这里,HTTPS server 端的代码编写修改工作全部结束。

3 客户端代码编写

这部分以 ESP-IDF v4.4.1 里的 esp_http_client example 为例,首先需要确保第一步生成的对应 ca.pem, client.pem, client.key 证书放在了 main 文件夹下:
请添加图片描述
然后需要让代码成功加载这些证书并使用,首先修改项目 main 文件夹下 CMakeLists.txt 为如下:

# Embed the server root certificate into the final binary
#
# (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.)
idf_component_register(SRCS "esp_http_client_example.c"
                    INCLUDE_DIRS "."
                    EMBED_TXTFILES howsmyssl_com_root_cert.pem
                    postman_root_cert.pem
                    ca.pem
                    client.pem
                    client.key)

然后在 main.c 里新增对应的证书,如下:

extern const char server_root_cert_pem_start[] asm("_binary_ca_pem_start");
extern const char server_root_cert_pem_end[]   asm("_binary_ca_pem_end");

extern const char client_cacert_pem_start[] asm("_binary_client_pem_start");
extern const char client_cacert_pem_pem_end[]   asm("_binary_client_pem_end");

extern const char client_prvtkey_pem_start[] asm("_binary_client_key_start");
extern const char client_prvtkey_pem_end[]   asm("_binary_client_key_end");

具体修改的代码地点请参考下图。
请添加图片描述
然后需要在 esp_http_client_config_t 结构体里配置使用上述证书,修改如下:

    esp_http_client_config_t config = {
        .url = "https://192.168.1.109:443",
        .event_handler = _http_event_handler,
//        .crt_bundle_attach = esp_crt_bundle_attach,
        .cert_pem = server_root_cert_pem_start,
        .client_cert_pem = client_cacert_pem_start,
        .client_key_pem = client_prvtkey_pem_start,
        .skip_cert_common_name_check = true,
    };

具体修改的代码地点请参考下图。
请添加图片描述
同时也修改了上图里的 log 显示等级为 warning。方便后续观测测试结果。

注:上述 esp_http_client_config_t 结构体里的 url 成员变量里的 IP 应为实际观测到的 HTTPS server IP,在这里使用的是第二步观测到的 192.168.1.109。

最后在 menuconfig 里配置 Wi-Fi SSID 和 Wi-Fi Password 后,烧录程序进 ESP32 即可。到这里,HTTPS client 端的代码编写修改工作全部结束。

4 测试验证

同时烧录上述两个工程到两个 ESP32 并观测 log,可以看到 HTTPS 链接成功建立,如下:
请添加图片描述

5 附录

5.1 相关参考

5.2 Q & A

  1. 证书生成脚本里的 ROOT_SUBJECT 是什么?CN 检查 为什么要关掉
    [ESP]ROOT_SUBJECT 里为用于生成 CA 证书的一些信息, CN 检查关闭的原因是客户端此时是按照 IP 访问的服务器端,如果设置 CN 检查, CN 字段为 IP 值,若服务器证书 CN 生成的时候设置成这个 IP,应该就可以访问了。总结就是 CN 字段客户端需和服务器证书的 CN 字段匹配。

  2. 这种情况下的 server 端的 CA 证书为啥和 client 是共用的?
    [ESP]这个 CA 同时签发了客户端和服务器的证书,就可以用这个 CA 去校验服务器或者客户端证书。

  3. 为什么需要屏蔽掉 crt_bundle_attach = esp_crt_bundle_attach?
    [ESP]crt_bundle_attach 里保存的校验服务器的 CA 证书都是受信任的 CA 证书,而自行使用 openssl 生成的 CA 证书是不受信任的,自然签发的证书也都是不受信任的,用受信任的 CA 证书链去校验不受信任的服务器证书肯定会校验失败。

  4. skip_cert_common_name_check = true 是因为此时 esp32 server 没有类似于域名的 common name 吗?
    [ESP]是的,对于自签证书,可以尝试将 server 端证书 CN 设置成 server 的 IP 地址,然后客户端的 URL 里面也是直接传入 IP 地址结构的 URL,此时打开 CN 检查即可校验通过。

<think>好的,用户让我介绍一下ESP-IDF华为云物联网MQTT。首先,我需要确认自己对这几个关键概念的理解是否正确。 ESP-IDF是乐鑫官方的开发框架,用于他们的ESP32系列芯片。华为云物联网平台应该是指华为云的IoT服务,而MQTT是一种轻量级的通信协议,常用于物联网设备与云端的消息传递。所以用户可能是在问如何将基于ESP-IDF开发的ESP32设备连接到华为云物联网平台,使用MQTT协议进行通信。 接下来,我需要分解这个问题。首先,可能需要简要介绍ESP-IDF、华为云物联网平台和MQTT各自的作用,然后说明它们如何结合使用。用户可能需要知道整个流程的步骤,比如如何配置设备、建立连接、发布和订阅消息等。 需要确保回答结构清晰,分步骤说明。同时,根据用户提供的系统级指令,所有数学公式要用正确的LaTeX格式,不过这个问题可能不涉及数学公式,但需要检查是否有变量或代码块需要处理。用户要求使用中文,所以需要保持语言准确。 可能需要注意的几点:ESP-IDF的环境搭建,华为云物联网平台的设备注册,MQTT客户端在ESP-IDF中的实现,以及具体的代码示例或配置步骤。此外,可能需要提到一些常见问题,比如安全连接(TLS/SSL)、认证方式(如设备密钥)、主题(Topic)的配置等。 还需要考虑用户可能的实际需求,比如他们是否在尝试实际连接,所以步骤需要具体,比如如何获取设备的三元组信息,如何在代码中配置MQTT参数,如何处理消息回调等。如果有代码示例,要确保用正确的格式,但根据用户指令,可能不需要展示完整代码,而是说明关键部分。 需要验证一些细节,比如华为云物联网平台支持的MQTT协议版本,ESP-IDF中是否有现成的MQTT库(比如esp_mqtt),以及相关的文档资源。可能还需要提及华为云物联网平台的特定要求,比如Topic结构、设备鉴权方式等。 另外,可能需要对比其他物联网平台,突出华为云的特点,比如设备管理、规则引擎、数据可视化等,但用户的问题更偏向技术实现,所以重点放在连接过程和MQTT通信上。 总结回答结构:先分别介绍三个组成部分,再讲解集成的步骤,可能包括准备工作、代码配置、连接流程、发布订阅消息,最后可能提到调试和常见问题。需要确保术语准确,比如设备证书、ClientId、Username、Password的生成方式,以及MQTT Broker的地址和端口。 需要检查是否有过时的信息,比如华为云物联网平台的接口是否有更新,ESP-IDF的MQTT库是否有变化。可能需要参考最新的官方文档来确保信息准确。 最后,用简洁明了的语言组织内容,分点说明,帮助用户逐步理解如何实现ESP-IDF与华为云物联网平台的MQTT连接。</think>### ESP-IDF 与华为云物联网平台(MQTT)集成指南 #### 1. **核心概念简介** - **ESP-IDF**:乐鑫(Espressif)官方为ESP32系列芯片提供的开发框架,支持Wi-Fi、蓝牙、MQTT等协议。 - **华为云物联网平台**:提供设备接入、数据管理、规则引擎等服务的云端平台,支持MQTT协议通信。 - **MQTT协议**:轻量级发布/订阅模型协议,适用于低带宽、高延迟的物联网场景。 #### 2. **集成步骤** ##### 2.1 准备工作 1. **华为云侧配置**: - 注册华为云账号,进入物联网平台。 - 创建产品(如“智能温控器”),定义设备能力(数据格式)。 - 注册设备,获取设备唯一标识(`device_id`)、设备密钥(`secret`)及平台地址(如 `iot-mqtts.cn-north-4.myhuaweicloud.com:1883`)。 2. **ESP32开发环境**: - 安装ESP-IDF(建议v4.4+)。 - 配置项目依赖,启用`esp_mqtt`库。 ##### 2.2 设备认证与连接 华为云MQTT支持两种认证方式: - **一机一密**:设备使用预注册的`device_id`和`secret`动态生成连接参数。 - **一型一密**:同一产品下设备共享产品级密钥。 **关键参数生成公式**: - **Client ID**:`${device_id}${timestamp}`(需唯一) - **Username**:`${device_id}${suffix}`(如`${device_id}@${product_id}`) - **Password**:HMAC-SHA256加密生成,例如: ```c password = hmac_sha256(device_secret, client_id + device_id + timestamp) ``` ##### 2.3 代码实现要点(ESP-IDF) ```c #include "esp_mqtt.h" // 配置MQTT客户端参数 esp_mqtt_client_config_t mqtt_cfg = { .uri = "mqtts://iot-mqtts.cn-north-4.myhuaweicloud.com:8883", .client_id = "your_client_id", .username = "your_username", .password = "your_password", .cert_pem = (const char *)huawei_cloud_root_ca_pem, // 平台CA证书 }; // 初始化并启动MQTT客户端 esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); esp_mqtt_client_start(client); ``` ##### 2.4 主题(Topic)定义 华为云主题格式示例: - **上行数据**:`$oc/devices/{device_id}/sys/properties/report` - **下行命令**:`$oc/devices/{device_id}/sys/commands/#` ##### 2.5 消息交互 - **发布数据**(JSON格式): ```json { "services": [{ "service_id": "Battery", "properties": {"level": 95} }] } ``` - **订阅命令**:通过回调函数处理云端下发的指令。 #### 3. **安全与调试** - **TLS加密**:必须使用MQTTS(端口8883)并配置华为云CA证书。 - **日志监控**:通过ESP-IDF的日志系统及华为云平台“设备监控”功能排查连接问题。 - **断线重连**:实现`MQTT_EVENT_DISCONNECTED`事件处理逻辑。 #### 4. **扩展功能** - **OTA升级**:通过华为云推送固件包,ESP32触发`esp_https_ota`组件完成升级。 - **规则引擎**:将设备数据转发到其他云服务(如华为云OCR、大数据分析)。 #### 5. **参考资源** - 华为云物联网平台文档:[设备接入指南](https://support.huaweicloud.com/devg-iothub/iot_02_0089.html) - ESP-IDF MQTT示例:[esp-mqtt示例代码](https://github.com/espressif/esp-idf/tree/master/examples/protocols/mqtt) --- 通过上述步骤,ESP32设备可安全接入华为云物联网平台,实现双向数据通信。实际开发中需结合具体业务逻辑调整数据格式与主题订阅策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值