Broker-Tests

MQTT-TLS-Verbindung testen (openssl, mosquitto, Code)

MQTT über TLS auf Port 8883 testen: openssl-s_client-Checks, Zertifikatskette, SNI und Hostname-Prüfung, mosquitto --cafile und Lösungen für typische Fehler.

Aktualisiert · 9 Min. Lesezeit

MQTT-TLS-Verbindung testen (openssl, mosquitto, Code)

Um eine MQTT-TLS-Verbindung zu testen, prüfst du drei Ebenen nacheinander: zuerst den TLS-Handshake selbst mit openssl s_client, dann eine echte MQTT-Sitzung mit mosquitto_sub oder mosquitto_pub und schließlich die Client-Bibliothek, die deine Geräte verwenden. Jede Ebene kann aus anderen Gründen scheitern — getrennt getestet weißt du genau, welche kaputt ist.

Alle Beispiele nutzen broker.connectmq.com auf Port 8883 (MQTT über TLS). Ersetze den Host durch deinen eigenen Broker.

Schritt 1: Handshake mit openssl prüfen

openssl s_client spricht TLS, aber kein MQTT — genau das willst du: Es zeigt das Zertifikat, das der Broker sendet, und ob dein Rechner ihm vertraut, ohne dass eine MQTT-Bibliothek dazwischenfunkt.

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

Was die Optionen bewirken:

  • -servername sendet SNI (Server Name Indication). Broker und Load Balancer mit mehreren Namen wählen darüber das Zertifikat aus. Ohne SNI bekommst du eventuell ein Standardzertifikat und einen Hostname-Fehler.
  • -verify_hostname lässt openssl prüfen, ob das Zertifikat für diesen Namen gilt — dieselbe Prüfung, die dein MQTT-Client macht.
  • -showcerts gibt jedes Zertifikat aus, das der Server sendet, damit du siehst, ob das Intermediate dabei ist.
  • </dev/null beendet die Verbindung direkt nach dem Handshake, statt auf Eingaben zu warten.

Am Ende der Ausgabe suchst du nach:

Gesundes Ergebnis
Verify return code: 0 (ok)

Alles andere ist ein Problem, das du auch im MQTT-Client sehen wirst:

openssl-ErgebnisBedeutung
Verify return code: 20 (unable to get local issuer certificate)Die CA (oder ein fehlendes Intermediate) ist nicht in deinem Trust Store
Verify return code: 18 (self-signed certificate)Das Serverzertifikat ist selbst signiert; übergib die CA mit -CAfile
Verify return code: 19 (self-signed certificate in certificate chain)Die Kette endet in einer privaten Root, der du nicht vertraust
Verify return code: 10 (certificate has expired)Zertifikat auf dem Broker erneuern
Verify return code: 62 (hostname mismatch)Das Zertifikat enthält den verwendeten Namen nicht
wrong version numberDer Port spricht unverschlüsseltes MQTT, kein TLS (oft 1883)
connect: Connection refused / TimeoutPort geschlossen oder gefiltert — kein TLS-Problem

Bei einem Broker mit privater CA fügst du -CAfile ca.crt hinzu und wiederholst den Befehl. Liefert er dann 0 (ok), sind deine Zertifikate in Ordnung und der Client braucht nur dieselbe CA-Datei.

Zertifikat schnell untersuchen

Subject, Issuer, SAN und Ablaufdatum
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

Prüfe, dass subjectAltName genau den Hostnamen enthält, den deine Geräte verwenden. Moderne Clients ignorieren den Common Name des Zertifikats; ein Name, der nur im CN steht, zählt also nicht. Für einen CI-Check, der zwei Wochen vor Ablauf fehlschlägt, leitest du das Zertifikat in openssl x509 -noout -checkend 1209600.

Um zu testen, welche TLS-Versionen der Broker akzeptiert, wiederhole den ersten Befehl mit -tls1_2 oder -tls1_3. Ein Gerät mit altem TLS-Stack, das nur TLS 1.2 spricht, braucht einen Broker, der 1.2 weiterhin anbietet — siehe TLS 1.2 vs. 1.3 bei MQTT.

Schritt 2: Echte MQTT-Sitzung über TLS

Ein erfolgreicher Handshake beweist noch nicht, dass der MQTT-Listener dahinter funktioniert. Abonniere und publishe über 8883:

Terminal 1 — Subscribe über TLS
mosquitto_sub -h broker.connectmq.com -p 8883 \
  --cafile /etc/ssl/certs/ca-certificates.crt \
  -u "$MQTT_USER" -P "$MQTT_PASS" \
  -t testmqtt/tls/check -v -d
Terminal 2 — Publish über TLS
mosquitto_pub -h broker.connectmq.com -p 8883 \
  --cafile /etc/ssl/certs/ca-certificates.crt \
  -u "$MQTT_USER" -P "$MQTT_PASS" \
  -t testmqtt/tls/check -m "hello over tls"

Die mosquitto-Clients aktivieren TLS nur, wenn du eine CA-Option angibst. Nutze --cafile mit deinem CA-Bundle (der Pfad oben gilt für Debian/Ubuntu; unter macOS mit Homebrew liegt es unter $(brew --prefix)/etc/ca-certificates/cert.pem), --capath /etc/ssl/certs oder --tls-use-os-certs ab mosquitto 2.x. Bei einer privaten CA zeigt --cafile auf deine ca.crt. Für Mutual TLS ergänzt du --cert client.crt --key client.key — siehe Client-Zertifikate und mTLS.

Schlägt es fehl, ist mosquitto wortkarg: Es gibt Error: A TLS error occurred. aus und sonst nichts. Geh zurück zu Schritt 1 — openssl sagt dir, warum. Funktioniert openssl, aber mosquitto nicht, vergleiche den Hostnamen: -h muss der Name im Zertifikat sein, keine IP-Adresse.

Schritt 3: Aus deinem Client-Code testen

Zum Schluss testest du mit der Bibliothek, die deine Anwendung nutzt. Zertifikatsspeicher und Standardwerte unterscheiden sich je nach Sprache — eine Verbindung, die mit mosquitto klappt, kann hier trotzdem scheitern.

tls_check.py (paho-mqtt 2.x)
import os
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, reason_code, properties):
    print("CONNACK:", reason_code)
    client.publish("testmqtt/tls/check", "hello from python over tls", qos=1)

c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, protocol=mqtt.MQTTv5)
c.tls_set()  # System-CAs, Hostname-Prüfung aktiv; ca_certs="ca.crt" für eine private CA
c.username_pw_set(os.environ["MQTT_USER"], os.environ["MQTT_PASS"])
c.on_connect = on_connect
c.connect("broker.connectmq.com", 8883)
c.loop_forever()
tls-check.mjs (MQTT.js 5)
import mqtt from "mqtt";

const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
  username: process.env.MQTT_USER,
  password: process.env.MQTT_PASS,
  // ca: fs.readFileSync("ca.crt"),   // nur für eine private CA
  reconnectPeriod: 0,
});

client.on("connect", () => {
  console.log("connected over TLS");
  client.publish("testmqtt/tls/check", "hello from node over tls", () => client.end());
});
client.on("error", (err) => console.error(err.code, err.message));

Die Fehler, die dabei entstehen, sind genau die, nach denen gesucht wird:

BibliothekFehlerHäufige Ursache
Python[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificateCA oder Intermediate fehlt
Pythoncertificate verify failed: Hostname mismatch, certificate is not valid for '...'Verbindung per IP oder mit falschem Namen
Python[SSL: WRONG_VERSION_NUMBER] wrong version numberTLS-Client an einem unverschlüsselten Port
Node.jsUNABLE_TO_VERIFY_LEAF_SIGNATURE — unable to verify the first certificateServer sendet das Intermediate nicht
Node.jsSELF_SIGNED_CERT_IN_CHAINPrivate CA nicht in ca übergeben
Node.jsERR_TLS_CERT_ALTNAME_INVALID — Hostname/IP does not match certificate's altnamesName nicht in der SAN-Liste

Die Lösungen findest du einzeln in certificate verify failed, TLS-Handshake fehlgeschlagen und Hostname mismatch. Der Code-Generator erzeugt TLS-fähige Verbindungs-Snippets für weitere Sprachen.

Kurze TLS-Test-Checkliste

  1. openssl s_client mit -servername und -verify_hostname liefert Verify return code: 0 (ok).
  2. -showcerts zeigt das Serverzertifikat und das Intermediate.
  3. Das SAN enthält genau den Hostnamen der Clients; das Zertifikat läuft nicht bald ab.
  4. mosquitto_sub und mosquitto_pub tauschen auf 8883 mit aktiver Prüfung eine Nachricht aus.
  5. Die Bibliothek deiner Anwendung verbindet sich mit ihrem Standard-Trust-Store (oder mit der privaten CA, die du mitlieferst).
  6. Der unverschlüsselte Port 1883 ist in der Produktion geschlossen oder deaktiviert, wenn TLS Pflicht ist.
  7. Geräte mit älteren TLS-Stacks verbinden sich mit der TLS-Version und den Cipher Suites, die du erlaubst.

Du willst zusätzlich einen Check im Browser? Der Online-MQTT-Client verbindet sich über sichere WebSockets auf 8084, die in vielen Setups dasselbe Zertifikat nutzen — ein schneller Weg, die TLS-Seite des Brokers zu bestätigen, bevor du ein Gerät debuggst.

Wer TLS Ebene für Ebene testet, macht aus einem vagen „TLS-Fehler“ in wenigen Minuten eine konkrete Ursache. Behalte den openssl-Befehl und den Ablauf-Check in deinen Skripten und führe sie aus, sobald ein Zertifikat erneuert wird oder sich ein Load Balancer ändert.

Häufig gestellte Fragen

Wie prüfe ich, ob ein MQTT-Broker TLS unterstützt?

Führe openssl s_client -connect host:8883 -servername host aus. Klappt der Handshake, siehst du die Zertifikatskette und 'Verify return code: 0 (ok)'. Ein Fehler 'wrong version number' bedeutet, dass der Port unverschlüsseltes MQTT spricht, kein TLS.

Warum funktioniert TLS im Browser, aber nicht auf meinem Gerät?

Meist sendet der Server sein Intermediate-Zertifikat nicht mit. Browser können fehlende Intermediates selbst nachladen; openssl, mosquitto und Embedded-TLS-Stacks können das nicht. Prüfe, ob s_client -showcerts das Intermediate anzeigt, und korrigiere die Zertifikatsdatei des Brokers.

Ist --insecure oder tls_insecure_set(True) sicher?

Nur als vorübergehende Diagnose auf deinem eigenen Rechner. Diese Optionen schalten die Hostname-Prüfung ab und machen die Verbindung anfällig für Man-in-the-Middle-Angriffe. Korrigiere stattdessen das Zertifikat oder den Hostnamen und lass die Prüfung aktiv.