sslarduinomqttesp32

How to connect an ESP32 to MQTT server with public IP and SSL?


I'm working with an ESP32 and an MQTT Server to create a meteo station. I managed to connect the esp32 to the server without any problem, even outside the local network, and I could post and received messages. So I decide to add some security with SSL and it's certificate, and suddenly, nothing work anymore.

After searching for a bit, I narrowed the problem down : if my esp32 is in the local network, SSL and it's certificate work like a charm. So that obviously means that my certificates are ok, and that my server configuration is probably ok too. But when I try from outside the network, it's the certificates that raises the error. More specifically, it raises :

sslv3 alert bad certificate mqtt

Which at least mean that I can reach my server, I just can't connect to it. To add even more puzzling element to the problem, I try to connect to the server with a computer outside of the network using MQTT explorer, with SSL and the exact same certificates, and it work without a itch.

I'm using mosquitto for the server. My configuration file give :

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

And for my ESP32, I can reproduce my problem with this simplified code. It come in part from the code given in that 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);
   }
 }

So. Since to reach my server from outside the local network I needed to add some port forwarding on my box, said box is my main suspect. I don't exactly know what happen, but I suspect either a loss of data, or something that come from the fact that the certificates are hardcoded. Like, maybe the "\n" are badly interpreted by my Internet box or by another element ? I don't know. I couldn't found anything to help me more, so really I could use any help on that problem. How could I manage to connect my esp32 to my server ?

Apologies for any bad english, and thank you in advance for your help !


Solution

  • SSL certificates are issued to a specific name or IP. When connecting, the certificate's fields (CN or SAN) have to match the name or IP of the target host. Maybe you issued the certificate to an address in your internal LAN like "192.168.0.2" or "mqtt.local". Those work fine for SSL connections inside the LAN since the cert and host name match. If you try to connect from Internet to your public IP or, e.g. "myhome.dyndns.org" it doesn't match the cert's fields. SSL connection is denied. Your cert would have to include those public addresses. MQTT explorer may ignore those errors and ESP not.