Sécurité MQTT

Épinglage de certificat et validation de l'AC sur ESP32 pour MQTT

Trois façons de valider le certificat d'un broker sur un ESP32 : embarquer une AC racine, utiliser le bundle de certificats ESP-IDF ou épingler. Découvrez les compromis et obtenez du code MQTT fonctionnel pour chacune.

Illustration de l'épinglage de certificat ESP32 avec un microcontrôleur conservant un certificat racine épinglé pendant sa connexion TLS à un broker MQTT
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

L'épinglage de certificat ESP32, l'intégration d'une AC racine et l'utilisation du bundle de certificats ESP-IDF sont trois façons différentes de s'assurer que votre appareil parle au vrai broker MQTT et non à un imposteur. Ce tutoriel les compare honnêtement, explique laquelle survit aux renouvellements de certificats et fournit du code Arduino et ESP-IDF fonctionnel qui se connecte à un broker MQTT sur le port 8883.

Pourquoi la validation compte sur les microcontrôleurs

De nombreux tutoriels ESP32 appellent client.setInsecure() pour « faire marcher TLS ». Cela chiffre le trafic mais saute complètement l'authentification du serveur. Un attaquant sur le réseau, par exemple un point d'accès pirate avec le même SSID, peut présenter n'importe quel certificat, recevoir le nom d'utilisateur et le mot de passe MQTT de l'appareil, puis relayer ou modifier chaque message. Un chiffrement sans authentification ne vous protège que des écoutes passives.

Contrairement à un ordinateur portable, un ESP32 n'a pas de magasin d'AC de système d'exploitation. Vous devez décider à quoi l'appareil fait confiance et l'inscrire dans le firmware. Cette décision détermine aussi le comportement de l'appareil des années plus tard, quand les certificats du broker changeront ; c'est pourquoi elle mérite plus de réflexion que le copier-coller du premier extrait trouvé.

Trois stratégies de validation comparées

Stratégie Ce à quoi l'appareil fait confiance Survit au renouvellement du certificat feuille Survit à un changement d'AC Coût en flash Effort
AC racine embarquée Un ou deux certificats racines Oui Seulement si la racine reste la même Faible (environ 2 Ko par PEM) Faible
Bundle de certificats ESP-IDF De nombreuses racines publiques courantes Oui Généralement oui Plus important (des dizaines de Ko) Faible
Épinglage du certificat feuille ou de la clé Un certificat ou une clé serveur précis Souvent non Non Minime Élevé (exige une discipline OTA)

AC racine embarquée (choix par défaut recommandé)

Vous embarquez le certificat racine qui ancre la chaîne du broker. Tout certificat valide émis sous cette racine pour le bon nom d'hôte est accepté, de sorte que les renouvellements de routine sont invisibles pour l'appareil. C'est le bon choix pour la plupart des projets. À strictement parler, c'est aussi une forme d'épinglage : vous épinglez la confiance sur une seule AC au lieu des centaines auxquelles un navigateur fait confiance.

Bundle de certificats ESP-IDF

ESP-IDF fournit un bundle de certificats racines courants (dérivé de la liste de Mozilla) qui peut être compilé dans le firmware. L'appareil fait confiance aux mêmes AC publiques qu'un navigateur. Vous perdez un peu de rigueur, mais si le broker change un jour d'AC publique, les appareils continuent de fonctionner.

Épinglage du certificat feuille ou de la clé publique

Vous codez en dur l'empreinte du certificat exact ou de la clé publique du serveur. C'est la confiance la plus restreinte, mais elle casse chaque fois que le certificat du serveur est remplacé. Avec les certificats Let's Encrypt, de courte durée et renouvelés automatiquement, le certificat feuille est remplacé tous les deux mois environ, voire plus tôt, et la clé peut aussi changer. Épingler le certificat feuille d'un tel broker est un moyen sûr de mettre une flotte hors ligne. N'épinglez les clés feuilles que si vous maîtrisez le serveur, gardez délibérément la clé stable et pouvez pousser des mises à jour OTA avant tout changement.

Épinglage de certificat ESP32 sur une AC racine avec Arduino

Le broker ConnectMQ présente un certificat Let's Encrypt. Les chaînes actuelles de Let's Encrypt mènent à ISRG Root X1 (RSA), et Let's Encrypt exploite aussi ISRG Root X2 (ECDSA). Embarquer les deux vous donne un ensemble de confiance restreint avec une marge pour un futur changement de chaîne. Téléchargez les fichiers PEM depuis la page des certificats de Let's Encrypt et collez leur contenu complet, l'un après l'autre, dans la chaîne ci-dessous. mbedTLS sait analyser plusieurs certificats PEM concaténés dans un même tampon.

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>

const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-password";

const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 8883;
const char* CLIENT_ID = "esp32-pin-5b21";   // utilisez YOUR_CLIENT_ID sur un broker privé
const char* TOPIC = "connectmq/demo/5b21/status";

// Ancres de confiance épinglées : collez les PEM complets d'ISRG Root X1 et d'ISRG Root X2
static const char PINNED_ROOTS[] = R"PEM(
-----BEGIN CERTIFICATE-----
...ISRG Root X1...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
...ISRG Root X2...
-----END CERTIFICATE-----
)PEM";

WiFiClientSecure tlsClient;
PubSubClient mqtt(tlsClient);

void syncClock() {
  configTime(0, 0, "pool.ntp.org", "time.google.com");
  Serial.print("Waiting for NTP");
  while (time(nullptr) < 1700000000) {
    delay(300);
    Serial.print(".");
  }
  Serial.println(" done");
}

void connectMqtt() {
  while (!mqtt.connected()) {
    Serial.print("Connecting to MQTT over TLS... ");
    // Broker privé : mqtt.connect(CLIENT_ID, "YOUR_USERNAME", "YOUR_PASSWORD")
    if (mqtt.connect(CLIENT_ID)) {
      Serial.println("connected");
      mqtt.publish(TOPIC, "online", true);
    } else {
      char err[100];
      tlsClient.lastError(err, sizeof(err));
      Serial.printf("failed, state=%d, tls=%s\n", mqtt.state(), err);
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(250);
  Serial.println("Wi-Fi connected");

  syncClock();
  tlsClient.setCACert(PINNED_ROOTS);
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setBufferSize(512);
}

void loop() {
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  static unsigned long last = 0;
  if (millis() - last > 10000) {
    last = millis();
    char msg[48];
    snprintf(msg, sizeof(msg), "{\"uptime\":%lu}", millis() / 1000);
    mqtt.publish(TOPIC, msg);
  }
}

L'appel à lastError() mérite d'être conservé dans les builds de production : il transforme un vague state=-2 en message mbedTLS lisible, comme un certificat expiré ou une AC inconnue. La vérification du nom d'hôte se fait automatiquement car WiFiClientSecure reçoit le nom d'hôte via setServer() ; ne remplacez donc jamais le nom d'hôte par une adresse IP.

Utiliser le bundle de certificats ESP-IDF

Si vous compilez directement avec ESP-IDF, le composant esp-mqtt peut valider par rapport au bundle intégré. Activez-le dans menuconfig sous Component config, mbedTLS, Certificate Bundle (l'option par défaut des AC courantes convient), puis :

#include "esp_crt_bundle.h"
#include "esp_log.h"
#include "mqtt_client.h"

static const char *TAG = "mqtt_bundle";

static void mqtt_event_handler(void *args, esp_event_base_t base,
                               int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
    if (event_id == MQTT_EVENT_CONNECTED) {
        ESP_LOGI(TAG, "Connected over TLS");
        esp_mqtt_client_publish(event->client,
                                "connectmq/demo/5b21/idf", "hello", 0, 1, 0);
    } else if (event_id == MQTT_EVENT_ERROR) {
        ESP_LOGE(TAG, "MQTT error, check TLS and credentials");
    }
}

void mqtt_start(void)
{
    const esp_mqtt_client_config_t cfg = {
        .broker.address.uri = "mqtts://broker.connectmq.com:8883",
        .broker.verification.crt_bundle_attach = esp_crt_bundle_attach,
        .credentials.client_id = "esp32-idf-5b21",
        // .credentials.username = "YOUR_USERNAME",
        // .credentials.authentication.password = "YOUR_PASSWORD",
    };
    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&cfg);
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID,
                                   mqtt_event_handler, NULL);
    esp_mqtt_client_start(client);
}

Appelez mqtt_start() une fois le Wi-Fi connecté et l'heure réglée par SNTP. Pour épingler plutôt une seule racine dans ESP-IDF, définissez .broker.verification.certificate avec votre chaîne PEM embarquée et laissez crt_bundle_attach non défini. La documentation ESP-MQTT d'Espressif liste toutes les options de vérification.

Éviter de briquer une flotte

Quelle que soit la stratégie choisie, préparez-vous au jour où les ancres de confiance changeront :

  1. Épinglez les racines, pas les feuilles, sauf si vous maîtrisez le cycle de vie de la clé du serveur.
  2. Embarquez une ancre de secours, par exemple une seconde racine de la même AC, pour qu'un changement de chaîne ne coupe pas la connectivité.
  3. Gardez les mises à jour OTA indépendantes de la confiance MQTT, ou au minimum vérifiez que le chemin OTA fonctionne encore quand le TLS MQTT échoue. Notre guide sur la mise à jour OTA de l'ESP32 via MQTT présente une approche.
  4. Synchronisez l'heure avant de vous connecter. Selon la configuration du core, mbedTLS vérifie les dates de validité des certificats, et une horloge bloquée en 1970 fait paraître chaque certificat comme pas encore valide.
  5. Journalisez les erreurs TLS à distance lorsqu'une connexion finit par réussir, afin de repérer tôt les problèmes de validation sur le terrain.
  6. Suivez les annonces des AC. Les AC publiques annoncent bien à l'avance les changements de racines et d'intermédiaires ; inscrivez un rappel dans votre calendrier de maintenance.

Dépannage

  • state=-2 avec « X509 - Certificate verification failed » : PEM erroné ou tronqué, ou la racine embarquée n'ancre pas la chaîne du broker. Vérifiez avec openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -showcerts.
  • « The certificate validity starts in the future » : la synchronisation NTP n'est pas terminée.
  • Mémoire insuffisante pendant le handshake : TLS a besoin de quelques dizaines de kilo-octets de tas ; libérez des tampons, réduisez les autres allocations et évitez d'ouvrir plusieurs sockets TLS à la fois.
  • Fonctionne sur 1883, échoue sur 8883 : un pare-feu bloque peut-être le port 8883. Essayez le WebSocket sécurisé sur 8084 si votre bibliothèque le prend en charge.

Pour d'autres symptômes, consultez la résolution des échecs de handshake TLS MQTT et notre tutoriel MQTT sur SSL avec l'ESP32.

Choisir une stratégie pour votre projet

Pour un projet personnel ou une petite flotte que vous pouvez reflasher à la main, l'AC racine embarquée l'emporte nettement : quelques lignes de code, un faible coût en flash et aucune surprise au renouvellement des certificats. Pour un produit commercial qui restera des années sur le terrain, réfléchissez d'abord au scénario de changement d'AC. Embarquez deux racines de l'AC du broker, ou utilisez le bundle ESP-IDF pour que les appareils continuent de faire confiance à toute AC publique courante, et assurez-vous que votre chemin OTA fonctionne toujours si le TLS MQTT échoue un jour. L'épinglage du certificat feuille n'a sa place que dans les systèmes où vous exploitez le serveur, gardez délibérément sa clé stable et disposez d'un processus testé pour déployer un nouveau pin avant le changement de clé. Quel que soit votre choix, consignez la décision à côté de la version du firmware, pour que la personne qui maintiendra la flotte plus tard sache quelles ancres de confiance sont déployées et où.

Pourquoi un broker MQTT managé simplifie l'épinglage

L'épinglage n'est jamais plus stable que la gestion des certificats du serveur. Avec un broker auto-hébergé, les renouvellements, le choix des chaînes et les changements de clés vous incombent, et chaque erreur se traduit par des appareils hors ligne. Un broker MQTT managé comme ConnectMQ renouvelle automatiquement son certificat Let's Encrypt reconnu publiquement, si bien que les appareils qui épinglent les racines Let's Encrypt continuent de se connecter au fil des renouvellements de routine. Vous pouvez prototyper avec le code ci-dessus sur le serveur MQTT public gratuit, réservé aux tests et aux prototypes, puis passer à des identifiants privés sans toucher au code TLS. Pour le parcours complet côté appareil, commencez par notre tutoriel IoT avec l'ESP32.

Démarrer avec ConnectMQ

Inscrivez-vous sur app.connectmq.com, choisissez l'offre gratuite Serverless sans carte bancaire et créez un broker. Copiez le nom d'utilisateur, le mot de passe et le client ID générés dans le sketch ci-dessus, conservez les racines épinglées, et votre ESP32 se connectera sur le port 8883 avec une validation complète du certificat.

Questions fréquentes

setInsecure() est-il acceptable pour les tests ?

Seulement sur un banc d'essai avec des identifiants jetables. Il désactive l'authentification du serveur : ne livrez jamais de firmware qui l'utilise.

Faut-il épingler l'empreinte du certificat de broker.connectmq.com ?

Non. Le certificat du serveur est renouvelé automatiquement et son empreinte change. Épinglez plutôt les certificats racines de Let's Encrypt.

Combien de flash le bundle de certificats ESP-IDF utilise-t-il ?

Cela dépend de la liste d'AC sélectionnée dans menuconfig ; la liste complète des AC courantes coûte des dizaines de kilo-octets, tandis qu'un seul PEM racine embarqué coûte environ deux kilo-octets.

L'épinglage de certificat remplace-t-il un nom d'utilisateur et un mot de passe ?

Non. L'épinglage authentifie le broker auprès de l'appareil. L'appareil a toujours besoin de ses propres identifiants pour que le broker puisse l'authentifier.

Articles liés

Rotation des identifiants MQTT sans interruption de service

Une stratégie à double identifiant, étape par étape, pour la rotation des noms d'utilisateur et mots de passe MQTT sur un parc : préparer le nouvel identifiant, basculer avec repli, vérifier, puis seulement révoquer l'ancien.

8 min de lecture

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.