OPEnSLab-OSU/SSLClient

ESP32 W5500 MQTT SSL

jhnwmr opened this issue · 7 comments

Hello,
I can easily connect to my MQTT server from my pc with the ca.cert file and on the port 8883. But the ESP32 says:

My IP address: 192.168.170.151.
Connecting to MQTT...
(SSLClient)(SSL_WARN)(m_run_until): Terminating because the ssl engine closed
(SSLClient)(SSL_ERROR)(m_start_ssl): Failed to initlalize the SSL layer
(SSLClient)(SSL_ERROR)(m_print_br_error): Expected server name was not found in the chain.
failed with state -2Connecting to MQTT...
(SSLClient)(SSL_ERROR)(connect): Cannot have two connections at the same time! Please create another SSLClient instance.
failed with state -2Connecting to MQTT...
(SSLClient)(SSL_ERROR)(connect): Cannot have two connections at the same time! Please create another SSLClient instance.
failed with state -2Connecting to MQTT...

The Server shows me an new connection with the IP 192.168.170.151 without any reaction.

My actually code:

#include <SPI.h>
#include <Ethernet2.h>
#include <SSLClient.h>
#include "certificates.h" // This file must be regenerated
#include <PubSubClient.h>


const char my_cert[] = 
"-----BEGIN CERTIFICATE-----\n" 
................
"-----END CERTIFICATE-----";

const char my_key[] = 
"-----BEGIN RSA PRIVATE KEY-----\n"
...............
"-----END RSA PRIVATE KEY-----\n";

SSLClientParameters mTLS = SSLClientParameters::fromPEM(my_cert, sizeof my_cert, my_key, sizeof my_key);

byte mac[] = {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xEF };
const char* mqttServer = "192.168.170.143";
const int mqttPort = 8883;

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

EthernetClient ethClient;
SSLClient ethClientSSL(ethClient, TAs, (size_t)TAs_NUM, A5);
PubSubClient client(ethClientSSL);

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");

    if (client.connect("ESP32") {

      Serial.println("connected");
      client.subscribe("#");
    } else {

      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);

    }
  }
}

void setup(){
  Serial.begin(115200);
  while(!Serial);

  ethClientSSL.setMutualAuthParams(mTLS);
  Ethernet.init(27);  // Most Arduino shields
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for (;;)
      ;
  }
  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println();

  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
 
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
 
    if (client.connect("ESP32_Garage") {
 
      Serial.println("connected");   
    } else {
 
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
 
    }
  }
}

void loop(){
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

Hello!

It looks like SSLClient is verifying the domain name even though you are attempting to connect with only an IP address (e.g. there is no domain name to verify).

To clarify: when connecting with SSLClient::connect(const char* host, uint16_t port) SSLClient will use the domain name as part of the identity verification process, ensuring the server certificate matches the expected domain name (more info on that here). When connecting with just an IPAddress (SSLClient::connect(IPAddress ip, uint16_t port)), this verification should be disabled, as there is no domain name to verify against. For some reason, your SSLClient is attempting to verify the domain name even though you only specified an IP address. This verification fails (it was invalid to begin with), closing the connection and outputting an error.

I suspect this issue is caused by your incorrect use of PubSubClient::setServer. In your code you pass the desired IP address to the PubSubClient as a string:

const char* mqttServer = "192.168.170.143";
const int mqttPort = 8883;
PubSubClient client(ethClientSSL);
...
void setup() {
  ...
  client.setServer(mqttServer, mqttPort);
  ...
}

The definitions of PubSubClient::setServer, however, are as follows:

PubSubClient& setServer(IPAddress ip, uint16_t port);
PubSubClient& setServer(uint8_t * ip, uint16_t port);
PubSubClient& setServer(const char * domain, uint16_t port);

Your sketch invokes the third version by passing a string as the first parameter. This version expects a domain name, however, and eventually calls SSLClient::connect(const char* host, uint16_t port) with the IP address string. Since the IP address string is not a domain name, SSLClient throws an error.

You should be able to fix this issue by changing your IP address from a string to the IPAddress class:

IPAddress mqttServer(192, 168, 170, 143);

This will switch your setServer call to the first version from above, allowing PubSubClient to use SSLClient::connect(IPAddress ip, uint16_t port) and bypassing the verification.

Good luck, and let me know if that works!

Many thanks for your efforts!!

Here my new Code, but the ESP32 make the same output without changes:

................
IPAddress mqttServer(192, 168, 170, 143);
const int mqttPort = 8883;

EthernetClient ethClient;
SSLClient ethClientSSL(ethClient, TAs, (size_t)TAs_NUM, A5);
PubSubClient client(ethClientSSL);

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");

    if (client.connect("ESP32") {

      Serial.println("connected");
      client.subscribe("#");
    } else {

      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);

    }
  }
}

void setup(){
  // Start Serial
  Serial.begin(115200);
  while(!Serial);
  // Enable mutual TLS with SSLClient
  ethClientSSL.setMutualAuthParams(mTLS);
  // You can use Ethernet.init(pin) to configure the CS pin
  Ethernet.init(27);  // Most Arduino shields
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    for (;;)
      ;
  }
  // print your local IP address:
  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println();

  client.setServer(mqttServer, mqttPort);

  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
 
    if (client.connect("ESP32") {
 
      Serial.println("connected");   
    } else {
 
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
 
    }
  }
}
................

Your sketch is missing a closing parenthesis:

if (client.connect("ESP32") {

Is this a typo?

I'm not sure what else the issue could be, but I can do some testing and get back to you.

Oh, yes that's right, but there is normal an username and password at the end. I deleted too much, but in the original code is it right.


Do you have another idea how I can use mqtt encrypted?

Unfortunately not over Ethernet, though the ESP32 core developers are working on implementing TLS for all internet interfaces (I think here: esp8266/Arduino#6680).

After some investigation, I have a few more questions:

  • Are you using the latest version of both SSLClient and PubSubClient?
  • If SSLClient is attempting to connect using connect(IPAddress addr, uint16_t port) you should see the following line at least once in your output. Does your output contain this line?
(SSLClient)(SSL_WARN)(connect): Using a raw IP Address for an SSL connection bypasses some important verification steps. You should use a domain name (www.google.com) whenever possible.
  • How complicated is your certificate chain? I would be happy to inspect your certificates if you would be comfortable emailing them to me; you can use the email on my GitHub profile.

I did not create the certification.h correctly, what to do with the pycert_bearssl.
With the library update it also works perfectly.

I did not create the certification.h correctly, what to do with the pycert_bearssl. With the library update it also works perfectly.

hi there, I cannot connect to mqtt ssl using eps32 w5500, I would like to know how to create certification.h correctly? because maybe mine also not coreected