跳转到内容

ESP32 证书集成

ESP32 证书集成

本节介绍如何在 ESP32 中集成 TLS 证书,实现 MQTT 通信的加密传输。证书集成是 ESP32 安全通信的核心步骤。学习完成后,您将能够:

  • 将 CA 证书嵌入 ESP32 固件
  • 配置 ESP32 验证 MQTT Broker 证书
  • 区分证书验证的不同模式
  • 评估证书管理对固件开发的影响

在开始本节之前,请确保:

  • MQTT TLS Broker 已配置
  • 了解 WiFiClientSecure 库的基本用法
  • 了解证书文件格式(PEM)
模式验证级别安全性证书配置适用场景
不验证无验证❌ 低无需证书开发测试
CA 验证服务器验证✅ 中嵌入 CA 证书生产环境
双向验证双向验证✅ ✅ 高CA + 客户端证书高安全场景
Terminal window
# 下载 Let's Encrypt ISRG Root X1 证书
curl -O https://letsencrypt.org/certs/isrgrootx1.pem
# 查看证书内容
cat isrgrootx1.pem
# 输出:
# -----BEGIN CERTIFICATE-----
# MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
# TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
# cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
# ...
# -----END CERTIFICATE-----
ca_cert.h
#ifndef CA_CERT_H
#define CA_CERT_H
// Let's Encrypt ISRG Root X1 根证书
static const char* rootCACertificate =
"-----BEGIN CERTIFICATE-----\n"
"MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
// ... 完整证书内容
"-----END CERTIFICATE-----\n";
#endif

证书转换脚本

#!/bin/bash
# convert-cert.sh - 将 PEM 证书转换为 C 字符串
CERT_FILE="$1"
VAR_NAME="${2:-rootCACertificate}"
echo "// 自动生成: $(date)"
echo "// 来源: $CERT_FILE"
echo "#ifndef TLS_CERT_H"
echo "#define TLS_CERT_H"
echo ""
echo "static const char* ${VAR_NAME} = "
while IFS= read -r line; do
# 转义双引号和反斜杠
escaped=$(echo "$line" | sed 's/"/\\"/g')
echo " \"${escaped}\\n\""
done < "$CERT_FILE"
echo " \"\";"
echo ""
echo "#endif // TLS_CERT_H"
Terminal window
# 使用示例
chmod +x convert-cert.sh
./convert-cert.sh isrgrootx1.pem > ca_cert.h

方法 2:使用 PROGMEM 存储(节省 RAM)

Section titled “方法 2:使用 PROGMEM 存储(节省 RAM)”
// 将证书存储在 Flash 而非 RAM
#include <pgmspace.h>
static const char rootCA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
...
-----END CERTIFICATE-----
)EOF";
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include "ca_cert.h" // 根证书
// WiFi 配置
const char* ssid = "YourSSID";
const char* password = "YourWiFiPassword";
// MQTT TLS 配置
const char* mqtt_server = "mqtt.example.com"; // DynDNS 域名
const int mqtt_port = 8883; // MQTTS 端口
const char* mqtt_user = "esp32_device";
const char* mqtt_pass = "device_password";
const char* client_id = "esp32_factory_001";
WiFiClientSecure espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(115200);
// 连接 WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("\nWiFi 已连接");
// 配置 TLS 证书验证
// 方法 1:使用 CA 证书验证(推荐)
espClient.setCACert(rootCACertificate);
// 方法 2:不验证证书(仅开发测试)
// espClient.setInsecure();
// 配置 MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
// 连接 MQTT Broker
connectToBroker();
}
void connectToBroker() {
while (!client.connected()) {
Serial.print("连接到 MQTT Broker (TLS)...");
if (client.connect(client_id, mqtt_user, mqtt_pass)) {
Serial.println("已连接 ✅");
// 发布在线消息
client.publish("esp32/status", "{\"status\": \"online\", \"tls\": true}");
// 订阅控制 Topic
client.subscribe("esp32/control");
} else {
Serial.print("连接失败, 状态码: ");
Serial.print(client.state());
Serial.println(" 5 秒后重试");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
// 处理收到的消息
Serial.printf("收到消息 [%s]: ", topic);
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
void loop() {
if (!client.connected()) {
connectToBroker();
}
client.loop();
}
WiFiClientSecure client;
// 模式 1: 不验证(开发测试用)
client.setInsecure();
// 特点: 所有证书都接受,无安全保障
// 风险: 🔴 容易受中间人攻击
// 模式 2: CA 证书验证(生产推荐)
client.setCACert(rootCA);
// 特点: 验证服务器证书由可信 CA 签发
// 安全: ✅ 防止中间人攻击
// 模式 3: 双向验证(最高安全)
client.setCACert(rootCA);
client.setCertificate(clientCert); // 设备证书
client.setPrivateKey(privKey); // 设备私钥
// 特点: 服务器也验证设备身份
// 安全: ✅ ✅ 防止设备伪造
场景推荐模式理由
开发原型setInsecure()快速迭代
内网生产setCACert()验证服务器身份
互联网生产setCACert()防止中间人攻击
高安全双向验证设备和服务器互验证
证书类型典型大小RAM 占用存储位置
Let’s Encrypt Root~2KB代码段Flash (PROGMEM)
服务器证书~1.5KB运行时Flash
私钥(双向)~1.5KB运行时Flash
设备证书(双向)~1KB运行时Flash

优化建议

  • 使用 PROGMEM 存储证书,减少 RAM 占用
  • 仅包含需要的根证书
  • 使用 ESP.getFreeHeap() 监控 TLS 连接的内存使用
要点说明对买家的沟通
代码嵌入证书作为字符串编译进固件”安全证书已内置在固件中”
验证模式支持 CA 验证和双向验证”设备会自动验证服务器身份”
内存影响证书占 ~2KB Flash”对固件大小影响很小”
存储安全证书固件中只读”证书不可篡改,OTA 更新时更新”

本节介绍了 ESP32 证书集成:

  1. 证书获取:从 Let’s Encrypt 获取根证书
  2. 代码嵌入:证书转换为 C 字符串嵌入固件
  3. 三种模式:不验证(开发)、CA 验证(生产)、双向验证(高安全)
  4. 代码实现:espClient.setCACert() → 连接 8883 端口
  5. 内存影响:证书 ~2KB,可使用 PROGMEM 优化