Sécurité MQTT

Échec du handshake TLS MQTT : comment le diagnostiquer et le corriger

Un guide de dépannage pas à pas pour les échecs de handshake TLS MQTT, avec openssl s_client pour identifier ports erronés, AC manquantes, problèmes de SNI, dérive d'horloge, incompatibilités de version TLS et proxys d'interception.

Illustration du dépannage d'un échec de handshake TLS MQTT montrant un handshake rompu entre un client et un broker avec une sortie de terminal de diagnostic
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

Lorsqu'une erreur d'échec du handshake TLS MQTT apparaît, la connexion meurt avant même que MQTT ne démarre, et la plupart des bibliothèques clientes la signalent avec un message peu utile. Ce guide vous montre comment isoler la cause en quelques minutes avec openssl s_client, puis passe en revue les six problèmes à l'origine de presque tous les échecs : mauvais port, AC manquante, SNI, dérive d'horloge, incompatibilité de version TLS et proxys d'interception. Tous les tests visent broker.connectmq.com, le serveur MQTT public gratuit, sur le port 8883 : vous pouvez donc comparer un point de terminaison qui fonctionne avec le vôtre.

Ce qui se passe pendant un handshake TLS

Avant que votre client n'envoie le paquet MQTT CONNECT, il doit mener à bien un handshake TLS avec le broker :

  1. Le client envoie un ClientHello avec les versions TLS et suites de chiffrement prises en charge, ainsi que le nom du serveur (SNI).
  2. Le broker répond avec la version et la suite choisies, ainsi que sa chaîne de certificats.
  3. Le client vérifie la chaîne par rapport aux racines de confiance, contrôle les dates de validité et fait correspondre le nom d'hôte.
  4. Les deux côtés dérivent les clés et passent au trafic chiffré.

Un échec à n'importe quelle étape ferme la connexion. Votre bibliothèque MQTT affiche alors quelque chose comme A TLS error occurred (mosquitto), ssl.SSLError (Python), ECONNRESET ou une erreur de certificat (Node.js), ou state=-2 (PubSubClient sur ESP32). C'est la raison sous-jacente qu'il vous faut, et OpenSSL l'affiche directement. Pour les réglages de référence corrects, consultez notre guide de configuration TLS MQTT.

Étape 1 : reproduire le handshake avec openssl s_client

Lancez cette commande depuis la même machine ou le même réseau que le client en échec :

openssl s_client -connect broker.connectmq.com:8883 \
  -servername broker.connectmq.com -showcerts </dev/null

Un résultat sain se termine par des lignes semblables à :

Verify return code: 0 (ok)

et indique le protocole et la suite de chiffrement négociés. Si OpenSSL réussit mais que votre application échoue, le problème se trouve dans la configuration de l'application (chemin de l'AC, nom d'hôte, options de la bibliothèque). Si OpenSSL échoue aussi, le problème vient du réseau, du magasin de confiance ou de l'horloge. Cette seule comparaison divise le champ de recherche par deux.

Variantes utiles :

# Résumé court du protocole, de la suite de chiffrement et de la vérification
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -brief </dev/null

# Vérifier par rapport à un fichier d'AC précis, comme le ferait un appareil embarqué
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com \
  -CAfile isrgrootx1.pem </dev/null

# Inspecter les dates et les noms du certificat
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

Échec du handshake TLS MQTT : symptômes et causes

Symptôme Cause probable Correction
wrong version number ou packet length too long Client TLS qui parle à un port en clair (1883) Utiliser le port 8883 pour TLS
La connexion se bloque puis expire Client MQTT en clair sur le port 8883, ou port bloqué par un pare-feu Activer TLS dans le client ; tester l'accessibilité
unable to get local issuer certificate (code 20) Le client ne fait pas confiance à l'AC racine Installer le bon bundle d'AC ou pointer vers lui
self-signed certificate in certificate chain (code 19) Un proxy d'interception ou un antivirus remplace le certificat Contourner l'inspection ou faire délibérément confiance à l'AC du proxy
certificate is not yet valid (code 9) L'horloge de l'appareil retarde Synchroniser l'heure avec NTP avant de se connecter
certificate has expired (code 10) L'horloge avance, ou le certificat du serveur a réellement expiré Vérifier l'horloge de l'appareil et les dates du serveur
Hostname mismatch / IP address mismatch Connexion par IP ou par un mauvais nom Se connecter avec le nom d'hôte figurant sur le certificat
no protocols available / unsupported protocol Client limité à des versions TLS que le serveur ne propose pas Autoriser TLS 1.2 ou plus récent dans le client
alert handshake failure Aucune suite de chiffrement commune ou SNI absent Mettre à jour la bibliothèque TLS ; envoyer le SNI

Cause 1 : mauvais port ou mauvais schéma

C'est l'erreur la plus courante. Le port 1883 parle MQTT en clair ; le port 8883 attend du TLS dès le premier octet. Si votre client démarre TLS sur le port 1883, OpenSSL signale wrong version number car la réponse du serveur n'est pas un enregistrement TLS. Si un client en clair se connecte au port 8883, le broker attend un ClientHello qui n'arrive jamais et la connexion finit par se fermer.

Vérifiez aussi votre schéma d'URL. Dans MQTT.js, mqtt:// signifie en clair et mqtts:// signifie TLS ; dans les navigateurs, utilisez wss://broker.connectmq.com:8084/mqtt. En Python, c'est l'appel à tls_set() qui active TLS ; changer uniquement le port ne suffit pas.

Pour vérifier si le port est seulement joignable :

# Linux/macOS
nc -vz broker.connectmq.com 8883

# Windows PowerShell
Test-NetConnection broker.connectmq.com -Port 8883

Les réseaux d'entreprise et les réseaux invités bloquent souvent le port 8883. S'il est bloqué, le WebSocket sécurisé sur le port 8084 est un contournement courant ; voir MQTT sur WebSocket.

Cause 2 : certificat d'AC manquant ou erroné

L'erreur de vérification 20 signifie que le client n'a pas pu construire une chaîne jusqu'à une racine de confiance. Situations typiques :

  • Les clients mosquitto n'utilisent pas le magasin du système, sauf si vous passez --capath /etc/ssl/certs (Linux) ou --cafile. Sous Windows, téléchargez le PEM ISRG Root X1 et passez --cafile.
  • Les images Docker minimales n'ont souvent pas le paquet ca-certificates.
  • Les appareils embarqués ont reçu la mauvaise racine, un PEM tronqué ou le certificat feuille au lieu de la racine.
  • Python sur macOS installé depuis python.org nécessite de lancer une fois son installateur de certificats ; les détails se trouvent dans SSL CERTIFICATE_VERIFY_FAILED avec MQTT.

Le certificat du broker ConnectMQ est émis par Let's Encrypt, donc tout magasin de confiance système à jour contient déjà la racine nécessaire.

Cause 3 : SNI absent

Le Server Name Indication indique au serveur quel nom d'hôte vous voulez. Les points de terminaison répartis en charge et multi-tenant peuvent présenter un certificat par défaut, voire rejeter le handshake, lorsque le SNI est absent. La plupart des bibliothèques envoient le SNI automatiquement quand vous vous connectez avec un nom d'hôte. Les problèmes surviennent lorsque le code résout d'abord l'adresse IP et se connecte à l'IP, ou avec des piles TLS très anciennes. Passez toujours le nom d'hôte à la bibliothèque cliente, et dans les tests OpenSSL, incluez toujours -servername.

Cause 4 : dérive d'horloge

Les certificats ne sont valides qu'entre deux dates. Un Raspberry Pi sans heure réseau, un ESP32 qui n'a pas terminé sa synchronisation NTP ou une VM restaurée depuis un vieil instantané peuvent être décalés de plusieurs jours, voire de plusieurs décennies. Vérifiez avec date -u sous Linux, et sur les microcontrôleurs, attendez la synchronisation NTP avant d'ouvrir la connexion TLS. Le guide sur l'épinglage de certificat sur ESP32 inclut une boucle d'attente NTP prête à l'emploi.

Cause 5 : incompatibilité de version TLS ou de suite de chiffrement

Un vieux firmware, d'anciens environnements Java ou des réglages codés en dur comme ssl.PROTOCOL_TLSv1 en Python peuvent ne proposer que des versions désactivées sur les serveurs modernes. Testez les versions qu'un serveur accepte :

openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -tls1_2 </dev/null
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -tls1_3 </dev/null

Configurez votre client pour négocier automatiquement (par exemple ssl.PROTOCOL_TLS_CLIENT en Python) plutôt que de figer une seule version. Notre article sur MQTT avec TLS 1.3 détaille les choix de versions et de suites de chiffrement.

Cause 6 : inspection TLS et proxys

Certains pare-feu d'entreprise et antivirus interceptent TLS en présentant leur propre certificat. Le symptôme est l'erreur de vérification 19 avec un nom d'émetteur appartenant au produit de sécurité. Demandez à votre équipe réseau d'exempter le nom d'hôte du broker ou, si l'inspection est obligatoire, faites explicitement confiance à l'AC de l'organisation dans cet environnement uniquement. Ne livrez jamais d'appareils avec la vérification désactivée pour contourner un proxy.

Checklist méthodique

  1. Lancez openssl s_client avec -servername depuis l'environnement en échec.
  2. Vérifiez que le port correspond au transport : 1883 en clair, 8883 TLS, 8084 WebSocket sécurisé.
  3. Vérifiez que le client se connecte par nom d'hôte, pas par IP.
  4. Vérifiez que le magasin de confiance ou le fichier d'AC contient la bonne racine.
  5. Vérifiez l'horloge système en UTC.
  6. Autorisez la négociation automatique de la version TLS.
  7. Écartez les proxys en testant depuis un autre réseau, comme un partage de connexion mobile.
  8. Seulement une fois le handshake réussi, déboguez les erreurs au niveau MQTT, comme des identifiants incorrects ou un conflit de client ID.

Ce dernier point compte : un handshake réussi suivi d'une déconnexion avec un code de raison CONNACK relève d'un problème d'authentification ou de protocole, pas de TLS.

Moins de problèmes de handshake avec un broker MQTT managé

Beaucoup d'incidents TLS sur les brokers auto-hébergés viennent du côté serveur : certificats expirés, chaînes incomplètes ou listener mal configuré après une mise à niveau. Un broker MQTT managé transfère cette responsabilité au fournisseur. ConnectMQ sert TLS sur le port 8883 et le WebSocket sécurisé sur le port 8084 avec des certificats Let's Encrypt renouvelés automatiquement : si un handshake échoue, vous savez qu'il faut regarder côté client. Vous pouvez d'abord tester n'importe quel client sur le serveur MQTT public gratuit, réservé aux tests et aux prototypes, et notre guide du broker MQTT cloud explique la place des brokers hébergés en production.

Démarrer avec ConnectMQ

Créez un broker gratuit sur app.connectmq.com avec l'offre Serverless sans carte bancaire, copiez les identifiants générés et utilisez le client MQTT intégré au navigateur dans le portail pour confirmer que le broker fonctionne avant de déboguer le code de votre propre appareil.

Questions fréquentes

Pourquoi mosquitto_sub affiche-t-il seulement « A TLS error occurred » ?

Les clients mosquitto masquent le détail OpenSSL. Lancez openssl s_client avec le même hôte, le même port et le même fichier d'AC pour voir l'erreur de vérification exacte.

Un mauvais nom d'utilisateur ou mot de passe peut-il provoquer un échec du handshake TLS ?

Non. Les identifiants sont vérifiés après le handshake, lors de l'échange MQTT CONNECT. Un problème d'identifiants se manifeste par un refus CONNACK, pas par une erreur TLS.

Mon client fonctionne sur le Wi-Fi de la maison mais pas au bureau. Pourquoi ?

Le réseau du bureau bloque probablement le port 8883 ou inspecte le trafic TLS. Testez avec Test-NetConnection ou nc, et essayez le WebSocket sécurisé sur le port 8084.

Faut-il désactiver la vérification des certificats pour réussir à se connecter ?

Non. Désactiver la vérification masque le vrai problème et supprime la protection contre les brokers imposteurs. Corrigez plutôt l'AC, le nom d'hôte ou l'horloge.

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.