如何使用 public IP 和 SSL 将 ESP32 连接到 MQTT 服务器?
How to connect an ESP32 to MQTT server with public IP and SSL?
我正在使用 ESP32 和 MQTT 服务器创建气象站。我设法将 esp32 连接到服务器没有任何问题,即使在本地网络之外,我也可以 post 并收到消息。所以我决定用 SSL 和它的证书增加一些安全性,突然间,什么都不管用了。
搜索了一下后,我缩小了问题范围:如果我的 esp32 在本地网络中,SSL 和它的证书很有用。所以这显然意味着我的证书没问题,我的服务器配置也可能没问题。但是当我从网络外部尝试时,是证书引发了错误。更具体地说,它引发了:
sslv3 alert bad certificate mqtt
这至少意味着我可以访问我的服务器,但我无法连接到它。为了给问题添加更多令人费解的元素,我尝试使用 MQTT 资源管理器、SSL 和完全相同的证书通过网络外部的计算机连接到服务器,并且它可以正常工作。
我在服务器上使用 mosquitto。我的配置文件给出:
listener 8883
require_certificate true
allow_anonymous true ## this one is here for debug purpose. Taking it out doesn't change anything.
certfile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\broker\broker.crt
keyfile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\broker\broker.key
cafile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\ca\ca.crt
对于我的 ESP32,我可以使用这个简化的代码重现我的问题。它部分来自 link 中给出的代码:https://iotdesignpro.com/projects/how-to-connect-esp32-mqtt-broker .
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
const char* ssid = "mySsid";
const char* password = "myPassword";
const char* mqtt_server = "publicIp";
#define mqtt_port 8883
// I don t really need those two next lines for now, but I intend to use them later
#define MQTT_USER "eapcfltj"
#define MQTT_PASSWORD "3EjMIy89qzVn"
#define MQTT_SERIAL_PUBLISH_CH "/icircuit/ESP32/serialdata/tx"
#define MQTT_SERIAL_RECEIVER_CH "/icircuit/ESP32/serialdata/rx"
const char AWS_PUBLIC_CERT[] = ("-----BEGIN CERTIFICATE-----\n\
...
-----END CERTIFICATE-----\n");
//client.crt
const char AWS_DEVICE_CERT[] = ("-----BEGIN CERTIFICATE-----\n\
...
-----END CERTIFICATE-----\n");
//client.key
const char AWS_PRIVATE_KEY[] = ("-----BEGIN RSA PRIVATE KEY-----\n\
...
-----END RSA PRIVATE KEY-----\n");
WiFiClientSecure wifiClient;
PubSubClient client(wifiClient);
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte *payload, unsigned int length) {
Serial.println("-------new message from broker-----");
Serial.print("channel:");
Serial.println(topic);
Serial.print("data:");
Serial.write(payload, length);
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
wifiClient.setCACert(AWS_PUBLIC_CERT);
wifiClient.setCertificate(AWS_DEVICE_CERT);
wifiClient.setPrivateKey(AWS_PRIVATE_KEY);
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(),MQTT_USER,MQTT_PASSWORD)) {
Serial.println("connected");
client.subscribe(MQTT_SERIAL_RECEIVER_CH);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
Serial.setTimeout(500);// Set time out for
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
reconnect();
}
void loop() {
client.loop();
if (Serial.available() > 0) {
char mun[501];
memset(mun,0, 501);
Serial.readBytesUntil( '\n',mun,500);
if (!client.connected()) {
reconnect();
}
client.publish(MQTT_SERIAL_PUBLISH_CH, mun);
}
}
所以。由于要从本地网络外部访问我的服务器,我需要在我的盒子上添加一些端口转发,所以说盒子是我的主要嫌疑人。我不完全知道发生了什么,但我怀疑数据丢失,或者证书是硬编码的。比如,也许“\n”被我的 Internet 框或其他元素错误地解释了?我不知道。我找不到任何可以帮助我的东西,所以我真的可以在这个问题上使用任何帮助。我如何设法将我的 esp32 连接到我的服务器?
对于任何糟糕的英语表示歉意,并提前感谢您的帮助!
SSL 证书颁发给特定名称或 IP。连接时,证书的字段(CN 或 SAN)必须与目标主机的名称或 IP 匹配。也许您将证书颁发给内部 LAN 中的地址,例如“192.168.0.2”或“mqtt.local”。由于证书和主机名匹配,因此它们适用于 LAN 内的 SSL 连接。如果您尝试从 Internet 连接到您的 public IP,或者,例如“myhome.dyndns.org”它与证书的字段不匹配。 SSL 连接被拒绝。您的证书必须包含那些 public 地址。 MQTT 资源管理器可能会忽略这些错误,而 ESP 不会。
我正在使用 ESP32 和 MQTT 服务器创建气象站。我设法将 esp32 连接到服务器没有任何问题,即使在本地网络之外,我也可以 post 并收到消息。所以我决定用 SSL 和它的证书增加一些安全性,突然间,什么都不管用了。
搜索了一下后,我缩小了问题范围:如果我的 esp32 在本地网络中,SSL 和它的证书很有用。所以这显然意味着我的证书没问题,我的服务器配置也可能没问题。但是当我从网络外部尝试时,是证书引发了错误。更具体地说,它引发了:
sslv3 alert bad certificate mqtt
这至少意味着我可以访问我的服务器,但我无法连接到它。为了给问题添加更多令人费解的元素,我尝试使用 MQTT 资源管理器、SSL 和完全相同的证书通过网络外部的计算机连接到服务器,并且它可以正常工作。
我在服务器上使用 mosquitto。我的配置文件给出:
listener 8883
require_certificate true
allow_anonymous true ## this one is here for debug purpose. Taking it out doesn't change anything.
certfile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\broker\broker.crt
keyfile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\broker\broker.key
cafile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\ca\ca.crt
对于我的 ESP32,我可以使用这个简化的代码重现我的问题。它部分来自 link 中给出的代码:https://iotdesignpro.com/projects/how-to-connect-esp32-mqtt-broker .
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
const char* ssid = "mySsid";
const char* password = "myPassword";
const char* mqtt_server = "publicIp";
#define mqtt_port 8883
// I don t really need those two next lines for now, but I intend to use them later
#define MQTT_USER "eapcfltj"
#define MQTT_PASSWORD "3EjMIy89qzVn"
#define MQTT_SERIAL_PUBLISH_CH "/icircuit/ESP32/serialdata/tx"
#define MQTT_SERIAL_RECEIVER_CH "/icircuit/ESP32/serialdata/rx"
const char AWS_PUBLIC_CERT[] = ("-----BEGIN CERTIFICATE-----\n\
...
-----END CERTIFICATE-----\n");
//client.crt
const char AWS_DEVICE_CERT[] = ("-----BEGIN CERTIFICATE-----\n\
...
-----END CERTIFICATE-----\n");
//client.key
const char AWS_PRIVATE_KEY[] = ("-----BEGIN RSA PRIVATE KEY-----\n\
...
-----END RSA PRIVATE KEY-----\n");
WiFiClientSecure wifiClient;
PubSubClient client(wifiClient);
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte *payload, unsigned int length) {
Serial.println("-------new message from broker-----");
Serial.print("channel:");
Serial.println(topic);
Serial.print("data:");
Serial.write(payload, length);
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
wifiClient.setCACert(AWS_PUBLIC_CERT);
wifiClient.setCertificate(AWS_DEVICE_CERT);
wifiClient.setPrivateKey(AWS_PRIVATE_KEY);
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(),MQTT_USER,MQTT_PASSWORD)) {
Serial.println("connected");
client.subscribe(MQTT_SERIAL_RECEIVER_CH);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
Serial.setTimeout(500);// Set time out for
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
reconnect();
}
void loop() {
client.loop();
if (Serial.available() > 0) {
char mun[501];
memset(mun,0, 501);
Serial.readBytesUntil( '\n',mun,500);
if (!client.connected()) {
reconnect();
}
client.publish(MQTT_SERIAL_PUBLISH_CH, mun);
}
}
所以。由于要从本地网络外部访问我的服务器,我需要在我的盒子上添加一些端口转发,所以说盒子是我的主要嫌疑人。我不完全知道发生了什么,但我怀疑数据丢失,或者证书是硬编码的。比如,也许“\n”被我的 Internet 框或其他元素错误地解释了?我不知道。我找不到任何可以帮助我的东西,所以我真的可以在这个问题上使用任何帮助。我如何设法将我的 esp32 连接到我的服务器?
对于任何糟糕的英语表示歉意,并提前感谢您的帮助!
SSL 证书颁发给特定名称或 IP。连接时,证书的字段(CN 或 SAN)必须与目标主机的名称或 IP 匹配。也许您将证书颁发给内部 LAN 中的地址,例如“192.168.0.2”或“mqtt.local”。由于证书和主机名匹配,因此它们适用于 LAN 内的 SSL 连接。如果您尝试从 Internet 连接到您的 public IP,或者,例如“myhome.dyndns.org”它与证书的字段不匹配。 SSL 连接被拒绝。您的证书必须包含那些 public 地址。 MQTT 资源管理器可能会忽略这些错误,而 ESP 不会。