Skip to content

Instantly share code, notes, and snippets.

@eLement87
Created December 10, 2017 13:12
Show Gist options
  • Save eLement87/133cddc5bd0472daf5cb35a20bfd811e to your computer and use it in GitHub Desktop.
Save eLement87/133cddc5bd0472daf5cb35a20bfd811e to your computer and use it in GitHub Desktop.
ESP8266 Secure MQTT Connection with Client Certificate Authentication
#include <FS.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>
// Insert your FQDN of your MQTT Broker
#define MQTT_SERVER "mqtt.srvx1.local"
const char* mqtt_server = MQTT_SERVER;
// WiFi Credentials
const char* ssid = "insert_coin";
const char* password = "supersecretwifipassword";
// Static IP configuration
#define IPSET_STATIC { 10, 10, 10, 13 }
#define IPSET_GATEWAY { 10, 10, 10, 254 }
#define IPSET_SUBNET { 255, 255, 255, 0 }
#define IPSET_DNS { 10, 10, 10, 254 }
byte ip_static[] = IPSET_STATIC;
byte ip_gateway[] = IPSET_GATEWAY;
byte ip_subnet[] = IPSET_SUBNET;
byte ip_dns[] = IPSET_DNS;
// Fingerprint of the broker CA
// openssl x509 -in mqttserver.crt -sha1 -noout -fingerprint
const char* fingerprint = "28:7C:0E:AC:E1:B9:00:9B:DB:4B:BA:19:0A:3D:4A:B5:F6:AA:4D:A6";
// Topic
char* topic = "test";
String clientName;
long lastReconnectAttempt = 0;
long lastMsg = 0;
int test_para = 2000;
unsigned long startMills;
WiFiClientSecure wifiClient;
PubSubClient client(mqtt_server, 8883, wifiClient);
void verifytls() {
// Use WiFiClientSecure class to create TLS connection
Serial.print("connecting to ");
Serial.println(mqtt_server);
if (!wifiClient.connect(mqtt_server, 8883)) {
Serial.println("connection failed");
return;
}
if (wifiClient.verify(fingerprint, mqtt_server)) {
Serial.println("certificate matches");
} else {
Serial.println("certificate doesn't match");
}
}
// Load Certificates
void loadcerts() {
if (!SPIFFS.begin()) {
Serial.println("Failed to mount file system");
return;
}
// Load client certificate file from SPIFFS
File cert = SPIFFS.open("/esp.der", "r"); //replace esp.der with your uploaded file name
if (!cert) {
Serial.println("Failed to open cert file");
}
else
Serial.println("Success to open cert file");
delay(1000);
// Set client certificate
if (wifiClient.loadCertificate(cert))
Serial.println("cert loaded");
else
Serial.println("cert not loaded");
// Load client private key file from SPIFFS
File private_key = SPIFFS.open("/espkey.der", "r"); //replace espkey.der with your uploaded file name
if (!private_key) {
Serial.println("Failed to open private cert file");
}
else
Serial.println("Success to open private cert file");
delay(1000);
// Set client private key
if (wifiClient.loadPrivateKey(private_key))
Serial.println("private key loaded");
else
Serial.println("private key not loaded");
// Load CA file from SPIFFS
File ca = SPIFFS.open("/ca.der", "r"); //replace ca.der with your uploaded file name
if (!ca) {
Serial.println("Failed to open ca ");
}
else
Serial.println("Success to open ca");
delay(1000);
// Set server CA file
if(wifiClient.loadCACert(ca))
Serial.println("ca loaded");
else
Serial.println("ca failed");
}
void getTime(){
// Synchronize time useing SNTP. This is necessary to verify that
// the TLS certificates offered by the server are currently valid.
Serial.print("Setting time using SNTP");
configTime(8 * 3600, 0, "de.pool.ntp.org");
time_t now = time(nullptr);
while (now < 1000) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println("");
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
}
boolean reconnect()
{
if (!client.connected()) {
if (client.connect((char*) clientName.c_str())) {
Serial.println("===> mqtt connected");
} else {
Serial.print("---> mqtt failed, rc=");
Serial.println(client.state());
}
}
return client.connected();
}
void wifi_connect()
{
if (WiFi.status() != WL_CONNECTED) {
// WIFI
Serial.println();
Serial.print("===> WIFI ---> Connecting to ");
Serial.println(ssid);
delay(10);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
WiFi.config(IPAddress(ip_static), IPAddress(ip_gateway), IPAddress(ip_subnet), IPAddress(ip_dns));
int Attempt = 0;
while (WiFi.status() != WL_CONNECTED) {
Serial.print(". ");
Serial.print(Attempt);
delay(100);
Attempt++;
if (Attempt == 150)
{
Serial.println();
Serial.println("-----> Could not connect to WIFI");
ESP.restart();
delay(200);
}
}
Serial.println();
Serial.print("===> WiFi connected");
Serial.print(" ------> IP address: ");
Serial.println(WiFi.localIP());
}
}
void setup()
{
Serial.begin(115200);
startMills = millis();
wifi_connect();
delay(500);
getTime();
delay(500);
loadcerts();
delay(200);
clientName += "esp8266-";
uint8_t mac[6];
WiFi.macAddress(mac);
clientName += macToStr(mac);
clientName += "-";
clientName += String(micros() & 0xff, 16);
}
void loop()
{
if (WiFi.status() == WL_CONNECTED) {
if (!client.connected()) {
long now = millis();
if (now - lastReconnectAttempt > 2000) {
lastReconnectAttempt = now;
if (reconnect()) {
lastReconnectAttempt = 0;
}
}
} else {
long now = millis();
if (now - lastMsg > test_para) {
lastMsg = now;
String payload = "{\"startMills\":";
payload += (millis() - startMills);
payload += "}";
sendmqttMsg(topic, payload);
}
client.loop();
}
} else {
wifi_connect();
}
}
void sendmqttMsg(char* topictosend, String payload)
{
if (client.connected()) {
Serial.print("Sending payload: ");
Serial.print(payload);
unsigned int msg_length = payload.length();
Serial.print(" length: ");
Serial.println(msg_length);
byte* p = (byte*)malloc(msg_length);
memcpy(p, (char*) payload.c_str(), msg_length);
if ( client.publish(topictosend, p, msg_length)) {
Serial.println("Publish ok");
free(p);
//return 1;
} else {
Serial.println("Publish failed");
free(p);
//return 0;
}
}
}
String macToStr(const uint8_t* mac)
{
String result;
for (int i = 0; i < 6; ++i) {
result += String(mac[i], 16);
if (i < 5)
result += ':';
}
return result;
}
@JosephCottingham
Copy link

Please send aid by uploading your entire directory, I am having problems pulling the files. My job and therefore my life depends on it!

@mrdc
Copy link

mrdc commented Jul 29, 2020

@eLement87

Do we need to use

.setCertificate(client_key);
.setPrivateKey(client_cert);

additionally to

.loadCACert()
.loadPrivateKey()

?

@atsspp
Copy link

atsspp commented Feb 9, 2021

Hi,
i have loaded all the cert file ( client.crt client.key ca.crt ), but i don't know where i can set username and password.
So my broker respond "mqtt failed, rc=5" for a lot of seconds, and after esp8266 restart...
thank you...

@sbluhm
Copy link

sbluhm commented Feb 20, 2021

@atsspp, the certificates replace the username and password. This is the whole idea of Client Certificate Authentication. The code is not only about setting up an encrypted connection.

@ghostmaster75
Copy link

Hi, i have the -2 rc too... i don't understand what's wrong. I've laoded client, cient ket and ca certificate (I used the same certificates to connect from node-red and it works) but this is the result:

20:34:19.288 -> Connecting to GhostNet
20:34:19.888 -> .......
20:34:23.727 -> WiFi connected
20:34:23.727 -> IP address: 
20:34:23.727 -> xxx.xxx.xxx.xxx
20:34:23.727 -> Success to open cert file
20:34:24.793 -> cert loaded
20:34:24.793 -> Success to open private cert file
20:34:25.856 -> private key loaded
20:34:25.856 -> Success to open ca
20:34:26.922 -> ca loaded
20:34:27.803 -> Attempting MQTT connection...failed, rc=-2 try again in 5 seconds
20:34:32.847 -> Attempting MQTT connection...failed, rc=-2 try again in 5 seconds
20:34:37.933 -> Attempting MQTT connection...failed, rc=-2 try again in 5 seconds

and this is my code:

#include <FS.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include <DHT_U.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>


//dht settings
#define DHTPIN  2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE); 
StaticJsonDocument<256> temphum;
float Humidity = 0.0;
float Temperature = 0.0;
bool isValid = true;


// wifi and mqtt settings
const char* ssid = "MySSID";
const char* password = "thebestpasswordintheworld";
const char* mqtt_server = "mqqtserver";
const char* mqtt_user = "mqqtuser";
const char* mqtt_pass = "awonderpass";

// Fingerprint of the broker CA
// openssl x509 -in  mqttserver.crt -sha1 -noout -fingerprint
const char* fingerprint = "FA:KE:FI:NG:ER:PR:IN:T0:12:34:56:78:90:A0:B1:C2:D3:E4:F5:AA";

WiFiClientSecure espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE  (256)
char msg[MSG_BUFFER_SIZE];
int value = 0;


void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  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 verifytls() {
  // Use WiFiClientSecure class to create TLS connection
  Serial.print("connecting to ");
  Serial.println(mqtt_server);
  if (!espClient.connect(mqtt_server, 8883)) {
    Serial.println("connection failed");
    return;
  }

  if (espClient.verify(fingerprint, mqtt_server)) {
    Serial.println("certificate matches");
  } else {
    Serial.println("certificate doesn't match");
  }
}

// Load Certificates
void loadcerts() {

  if (!SPIFFS.begin()) {
    Serial.println("Failed to mount file system");
    return;
  }

  // Load client certificate file from SPIFFS
  File cert = SPIFFS.open("/client.crt", "r"); //replace esp.der with your uploaded file name
  if (!cert) {
    Serial.println("Failed to open cert file");
  }
  else
    Serial.println("Success to open cert file");

  delay(1000);

  // Set client certificate
  if (espClient.loadCertificate(cert))
    Serial.println("cert loaded");
  else
    Serial.println("cert not loaded");

  // Load client private key file from SPIFFS
  File private_key = SPIFFS.open("/client.key", "r"); //replace espkey.der with your uploaded file name
  if (!private_key) {
    Serial.println("Failed to open private cert file");
  }
  else
    Serial.println("Success to open private cert file");

  delay(1000);

  // Set client private key
  if (espClient.loadPrivateKey(private_key))
    Serial.println("private key loaded");
  else
    Serial.println("private key not loaded");


  // Load CA file from SPIFFS
  File ca = SPIFFS.open("/ca.crt", "r"); //replace ca.der with your uploaded file name
  if (!ca) {
    Serial.println("Failed to open ca ");
  }
  else
    Serial.println("Success to open ca");
  delay(1000);

  // Set server CA file
  if (espClient.loadCACert(ca))
    Serial.println("ca loaded");
  else
    Serial.println("ca failed");

}


int digitalReadOutputPin(uint8_t pin)
{
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  if (port == NOT_A_PIN) 
    return LOW;

  return (*portOutputRegister(port) & bit) ? HIGH : LOW;
}

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();
  int b = 0;
  char out[256];
  switch ((char)payload[0]) {
    case '1':
       digitalWrite(BUILTIN_LED, LOW);
       b =serializeJson(temphum, out);
       snprintf (msg, MSG_BUFFER_SIZE, out);
       Serial.print("Publish message: ");
       Serial.println(msg);
       client.publish("my/mqqt/temperature", msg);
       break;       
    }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str(), mqtt_user, mqtt_pass)) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("my/mqqt/temperature", "29");
      // ... and resubscribe
      client.subscribe("my/sensors/commands");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  Serial.begin(115200);
  delay(500);
  setup_wifi();
  loadcerts();
  delay(500);
  client.setServer(mqtt_server, 8883);
  client.setCallback(callback);
  dht.begin(); 
}

void loop() {
  digitalWrite(BUILTIN_LED, HIGH);
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  unsigned long now = millis();
  if (now - lastMsg > 5000) {
 
  // Read humidity
    Humidity = (float(int(dht.readHumidity() * 10))/10);
  //Read temperature in degree Celsius
    Temperature = (float(int(dht.readTemperature() * 10))/10);
    isValid = true;
    if (isnan(Humidity) || isnan(Temperature)) {
      isValid = false;
    }
    temphum["temperature"] = Temperature;
    temphum["humidity"] = Humidity;
    temphum["isValid"] = isValid;
    lastMsg = now;
    char out[256];
    int b =serializeJson(temphum, out);
    snprintf (msg, MSG_BUFFER_SIZE, out);
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish("my/mqqt/temperature", msg);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment