Problem with CA certificate for SSL

Hi
I am using a NodeMCU Esp8266 to send some sensor data to my Server using MQTT. I have my own CA certificate generated by my server for the SSL connection. This is an extension of https://forum.arduino.cc/t/secure-mqtt-in-arduino-uno-esp-8266/1140681 topic. A separate topic was created because the type and scope of the problem changed significantly.

Initially, I was unable to connect to my server over SSL but I could connect to my server after I included the following line in my code.

 wifiClient.setInsecure()

But I think this makes my CA Certificate useless as I tried to replace it with some garbage string and was still able to connect to the server.

I think this makes my communication insecure. Is there any way to make my data sending secure?

I am still able to perfectly use my Python code in the first post on earlier topic to send data to my server properly.

Can anyone please help?

Thanks

Here's how my present code looks like

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <WiFiClientSecure.h>

// Wi-Fi settings
const char* ssid = "ssid";
const char* password = "pass";

// MQTT broker settings
const char* mqttServer = "mqtt.myserver.com";
const int mqttPort = 8883;  // MQTT over SSL/TLS default port

// MQTT authentication
const char* mqttUsername = "guest";
const char* mqttPassword = "guest";

// Sensor pin
const int sensorPin = A0;

// Create an instance of WiFiClientSecure
WiFiClientSecure wifiClient;

// Create an instance of PubSubClient
PubSubClient mqttClient(wifiClient);

// Root CA certificate
const char* rootCACertificate = R"(
-----BEGIN CERTIFICATE-----
My certificate goes here
-----END CERTIFICATE-----
)";

void setup() {
  Serial.begin(9600);
  delay(10);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi!");

  // Configure MQTT server and set SSL/TLS options
  mqttClient.setServer(mqttServer, mqttPort);
  mqttClient.setCallback(callback);

  // Load root CA certificate into WiFiClientSecure object
  wifiClient.setTrustAnchors(new BearSSL::X509List(rootCACertificate));
  wifiClient.setInsecure();

  // Connect to MQTT broker
  connectToMqtt();
}

void loop() {
  if (!mqttClient.connected()) {
    // If MQTT connection is lost, try to reconnect
    reconnect();
  }

  // Read sensor data
  int sensorValue = 22;

  // Convert sensor value to a string
  String payload = String(sensorValue);

  // Publish the sensor data to the MQTT topic
  mqttClient.publish("sensor_data_topic", payload.c_str());

  // Wait for some time before publishing the next sensor data
  delay(5000);
}

void connectToMqtt() {
  // Loop until connected to MQTT broker
  while (!mqttClient.connected()) {
    Serial.println("Connecting to MQTT server...");
    if (mqttClient.connect("ESP8266Client", mqttUsername, mqttPassword)) {
      Serial.println("Connected to MQTT server!");
      // Subscribe to MQTT topics, if needed
      // mqttClient.subscribe("topic_name");
    } else {
      Serial.print("Failed to connect to MQTT server. Retrying in 5 seconds...");
      delay(5000);
    }
  }
}

void reconnect() {
  // Disconnect if already connected
  if (mqttClient.connected()) {
    mqttClient.disconnect();
  }

  // Attempt to reconnect
  connectToMqtt();
}

void callback(char* topic, byte* payload, unsigned int length) {
  // Handle MQTT subscription messages, if needed
  // ...
}

Please help me understand why isn't my CA certificate working properly?
Thanks

1 Like

In your code, the line wifiClient.setInsecure(); is basically telling the ESP8266 to ignore the certificate validation and connect insecurely. This is why you are able to connect even when using an incorrect certificate.

What you should do instead is to use wifiClient.setTrustAnchors(new BearSSL::X509List(rootCACertificate));. This is the correct way to load your CA certificate into the ESP8266 for it to validate the server's certificate during the SSL handshake.

However, the ESP8266 also needs to validate the date of the certificate, and it needs an accurate system time to do this. It has no Real-Time Clock (RTC), so it won't know the correct time on its own. This means you will also need to set the time on the ESP8266.

Here is how you could modify your setup() function:

void setup() {
  Serial.begin(9600);
  delay(10);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi!");

  // Configure NTP and get current time
  configTime(0, 0, "pool.ntp.org");  // UTC
  setClock();  // Wait for time synchronization

  // Configure MQTT server and set SSL/TLS options
  mqttClient.setServer(mqttServer, mqttPort);
  mqttClient.setCallback(callback);

  // Load root CA certificate into WiFiClientSecure object
  wifiClient.setTrustAnchors(new BearSSL::X509List(rootCACertificate));

  // Connect to MQTT broker
  connectToMqtt();
}

void setClock() {
  time_t now = time(nullptr);
  while (now < 8 * 3600 * 2) {  // Wait for the NTP time to be set
    delay(500);
    Serial.print(".");
    now = time(nullptr);
  }
  struct tm timeinfo;
  gmtime_r(&now, &timeinfo);
  Serial.println(&timeinfo, "%Y/%m/%d %H:%M:%S");  // Print the time
}

In this code, configTime configures the ESP8266 to use the Network Time Protocol (NTP) to get the current time from an NTP server ("pool.ntp.org" in this case). setClock then waits for the time to be set before continuing with the rest of the setup.

Now, with the correct time set and the correct CA certificate loaded, the ESP8266 should be able to securely connect to your MQTT server using SSL/TLS. If it still cannot connect, double-check that your CA certificate is correct and that the server is properly configured for SSL/TLS. Also, keep in mind that the ESP8266 has limited resources, and handling SSL/TLS can be resource-intensive. If your application is complex, you may run into issues due to these resource constraints.

2 Likes

Thank You so Much for this. It is now working and connecting to the MQTT server properly. however, I got an error compiling the following line

Serial.println(&timeinfo, "%Y/%m/%d %H:%M:%S");  // Print the time
Compilation error: no matching function for call to 'println(tm*, const char [18])'

I commented out that print line to upload it and I was able to connect to my server properly.

Also, just out of curiosity, why does the wifiClient.setInsecure(); function enable the esp8266 to connect to a SSL server on port 8883 even without a valid certificate?

Thanks for help.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.