I would like to connect ESP8266 with Azure IoT Hub Device via X509 certificate. Currently, I can connect using the symmetric key connection to my device, but I have devices that I want to authenticate with X509 certificate.
I found a solution, but is for Arduino Nano 33 IoT, which is probably using another chip and it has encryption slot.
I am trying to do this using the Azure IoT SDK C library, but without much success. Here is a code that is using Mqtt and WiFiClientSecure plus BearSSL in order to connect via certificate. Unfortunately, the only solution that I found was to generate ECCX09 certificate with a chip that have encryption and to just use the thumbprint. Here is my try to use the certificate:
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
// C99 libraries
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <cstdlib>
// Libraries for MQTT client, WiFi connection and SAS-token generation.
#include <ESP8266WiFi.h>
#include <ArduinoBearSSL.h>
#include <ArduinoMqttClient.h>
// Additional sample headers
#include "secrets.h"
// Utility macros and defines
#define LED_PIN 2
// Translate iot_configs.h defines into variables used by the sample
static const char* ssid = SECRET_WIFI_SSID;
static const char* password = SECRET_WIFI_PASS;
static const char* host = SECRET_BROKER;
static const String device_id = SECRET_DEVICE_ID;
// Memory allocated for the sample's variables and structures.
static WiFiClientSecure wifi_client;
static BearSSLClient ssl_client(wifi_client);
static MqttClient mqtt_client(ssl_client);
// Auxiliary functions
static void connectToWiFi()
{
Serial.begin(115200);
Serial.println();
Serial.print("Connecting to WIFI SSID ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.print("WiFi connected, IP address: ");
Serial.println(WiFi.localIP());
}
/*
* Establishses connection with the MQTT Broker (IoT Hub)
* Some errors you may receive:
* -- (-.2) Either a connectivity error or an error in the url of the broker
* -- (-.5) Check credentials - has the SAS Token expired? Do you have the right connection string copied into arduino_secrets?
*/
void connectToMQTT() {
Serial.print("Attempting to MQTT broker: ");
Serial.print(host);
Serial.println(" ");
while (!mqtt_client.connect(host, 8883)) {
// failed, retry
Serial.print(".");
Serial.println(mqtt_client.connectError());
delay(5000);
}
Serial.println();
Serial.println("You're connected to the MQTT broker");
Serial.println();
// subscribe to a topic
mqtt_client.subscribe("devices/" + device_id + "/messages/devicebound/#");
}
unsigned long getTime() {
// get the current time from the WiFi module
// return WiFi.getTime();
return time(NULL);
}
static void establishConnection()
{
// Set the username to "<broker>/<device id>/?api-version=2018-06-30"
String username;
// Set the client id used for MQTT as the device id
mqtt_client.setId(device_id);
username += host;
username += "/";
username += device_id;
username += "/api-version=2018-06-30";
mqtt_client.setUsernamePassword(username, "");
if(WiFi.status() != WL_CONNECTED) {
connectToWiFi();
}
if (!mqtt_client.connected()) {
// MQTT client is disconnected, connect
connectToMQTT();
}
}
// Arduino setup and loop main functions.
void setup()
{
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
Serial.begin(9600);
// Set the X.509 certificate
// ssl_client.setEccCert(CLIENT_CERT);
ssl_client.setEccSlot(0, CLIENT_CERT);
// Set a callback to get the current time
// used to validate the servers certificate
ArduinoBearSSL.onGetTime(getTime);
establishConnection();
}
void loop() {}
This fails on setting the Certificate and to use the ecc slot 0, because I am using ESP8266 that does not have cryptography in it (or at least I do not know). I saw in the library that there is another method in the BearSSL - void setEccCert(br_x509_certificate cert); , but I do not know how to initialize br_x509_certificate (here is the ref).
I decided to try something else, too, reading the certificate with SPIFFS, then adding to WiFiClientSecure. Unfortunately, I cannot load the private key to make client connection.
#include "FS.h" // File system commands to access files stored on flash memory
#include <ESP8266WiFi.h> // WiFi Client to connect to the internet
#include <PubSubClient.h> // MQTT Client to connect to AWS IoT Core
#include <NTPClient.h> // Network Time Protocol Client, used to validate certificates
#include <WiFiUdp.h> // UDP to communicate with the NTP server
#include "secrets.h"
// Utility macros and defines
#define NTP_SERVERS "pool.ntp.org", "time.nist.gov"
// Translate secrets.h defines into variables used by the sample
static const char* ssid = SECRET_WIFI_SSID;
static const char* password = SECRET_WIFI_PASS;
static const char* iot_hub_broker = SECRET_BROKER;
// callback function that will be called when the device receive a message
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();
}
// Memory allocated client variables
static WiFiUDP ntp_UDP;
static NTPClient time_client(ntp_UDP, "pool.ntp.org");
static WiFiClientSecure esp_client;
static PubSubClient mqtt_client(iot_hub_broker, 8883, callback, esp_client);
// Auxiliary functions
static void configureX509CertificateConnection() {
time_client.begin();
while(!time_client.update()) {
time_client.forceUpdate();
}
esp_client.setX509Time(time_client.getEpochTime());
Serial.println("Time client and ESP client are set up.");
// Attempt to mount the file system
if (!SPIFFS.begin()) {
Serial.println("Failed to mount file system");
return;
}
// Load certificate file from file system
File cert = SPIFFS.open("temperature-sensor-1-all.pem", "r");
if (!cert) {
Serial.println("Failed to open certificate from file system");
} else {
Serial.println("Successfully opened certificate file");
}
// Load private key file from file system
File private_key = SPIFFS.open("/temperature-sensor-1-private.pem", "r");
if (!private_key) {
Serial.println("Failed to open private key from file system");
}
else {
Serial.println("Successfully opened private key file");
}
delay(1000);
// Load private key to client connection
if (esp_client.loadPrivateKey(private_key)) {
Serial.println("Private key loaded to client connection");
}
else {
Serial.println("Private key not loaded to client connection");
}
}
static void connectToWiFi() {
Serial.begin(115200);
Serial.println();
Serial.print("Connecting to WiFi: ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("WiFi connected, IP address: ");
Serial.println(WiFi.localIP());
}
/*
* Establishses connection with the MQTT Broker (IoT Hub)
* Some errors you may receive:
* -- (-.2) Either a connectivity error or an error in the url of the broker
* -- (-.5) Check credentials - has the SAS Token expired? Do you have the right connection string copied into arduino_secrets?
*/
void connectToMQTT() {
}
static void establishConnection() {
if (WiFi.status() != WL_CONNECTED) {
connectToWiFi();
}
configureX509CertificateConnection();
}
// Arduino setup and loop main functions.
void setup() {
pinMode(2, OUTPUT);
digitalWrite(2, HIGH);
Serial.begin(115200);
establishConnection();
}
void loop() {}
P.S. I have it up and running on Raspberry Pi, but I have to make some experiments using the Arduino and ESP8266.
P.S.2. I am using Arduino with Atmega328P with integrated ESP8266 chip, but working only with the ESP8266. Additionally, I am using Arduino IDE 1.8.19 and installed the Azure IoT SDK C library (and some other required ones).
P.S.3. Additionally, I could find X509 certificate sample, but it is using the Paho Mqtt Client and I am not able reproduce it on Arduino.