Der MQTT-Fehler „ssl certificate verify failed“ bedeutet, dass Python das Zertifikat des Brokers während des TLS-Handshakes abgelehnt hat. Das ist eine Sicherheitsfunktion, die ihre Arbeit macht, und die Lösung besteht fast nie darin, sie abzuschalten. Dieser Leitfaden zeigt, wie Sie den genauen Grund aus der Fehlermeldung ablesen und ihn mit paho-mqtt unter Windows, macOS, Linux und Docker korrekt beheben. Als bekanntermaßen funktionierende Referenz dient broker.connectmq.com auf Port 8883.
Die Fehlermeldung lesen
Die vollständige Exception sieht meist so aus:
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
unable to get local issuer certificate (_ssl.c:1000)
Entscheidend ist der Teil nach dem Doppelpunkt. Die drei häufigen Varianten deuten auf unterschiedliche Ursachen hin:
| Detail der Meldung | Bedeutung | Wahrscheinlichste Lösung |
|---|---|---|
unable to get local issuer certificate |
Python findet die Root-CA nicht, die die Kette signiert hat | CA-Bundle reparieren oder bereitstellen |
self-signed certificate in certificate chain |
Etwas hat ein Zertifikat präsentiert, das von einer nicht vertrauenswürdigen privaten CA signiert ist | Inspektion durch Proxy oder Virenscanner oder eine nicht konfigurierte private Broker-CA |
Hostname mismatch, certificate is not valid for '...' |
Der Name, mit dem Sie sich verbunden haben, steht nicht im Zertifikat | Mit dem korrekten Hostnamen verbinden, nicht mit einer IP |
certificate has expired |
Uhr falsch oder Serverzertifikat abgelaufen | Systemzeit korrigieren oder Serverzertifikat erneuern |
Schritt 1: Prüfen, ob Python den Broker verifizieren kann
Dieses kurze Skript testet TLS ohne MQTT, genau mit den Einstellungen, die paho-mqtt standardmäßig verwendet:
import socket
import ssl
HOST, PORT = "broker.connectmq.com", 8883
ctx = ssl.create_default_context()
print("OpenSSL:", ssl.OPENSSL_VERSION)
print("Default verify paths:", ssl.get_default_verify_paths())
with socket.create_connection((HOST, PORT), timeout=10) as sock:
with ctx.wrap_socket(sock, server_hostname=HOST) as tls:
cert = tls.getpeercert()
print("Connected with", tls.version())
print("Issuer:", dict(x[0] for x in cert["issuer"]))
print("Valid until:", cert["notAfter"])
Scheitert dieses Skript mit demselben Fehler, liegt das Problem beim Trust Store der Python-Umgebung, bei der Uhr oder beim Netzwerk. Gelingt es, aber Ihr MQTT-Code scheitert, liegt das Problem in der Konfiguration des MQTT-Clients: ein falscher ca_certs-Pfad, eine Verbindung per IP oder ein Broker mit privatem Zertifikat.
Schritt 2: Ein korrektes TLS-Setup für paho-mqtt verwenden
Mit paho-mqtt 2.x ist dies die Basis, die mit jedem Broker mit öffentlich vertrauenswürdigem Zertifikat funktioniert, auch mit ConnectMQ:
import ssl
import time
import certifi
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
TOPIC = "connectmq/demo/a8d2/verify-test"
def on_connect(client, userdata, flags, reason_code, properties):
print("CONNACK:", reason_code)
client.subscribe(TOPIC)
client.publish(TOPIC, "certificate verified")
def on_message(client, userdata, msg):
print("Received:", msg.payload.decode())
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="py-verify-a8d2")
client.on_connect = on_connect
client.on_message = on_message
# certifi liefert das CA-Bundle von Mozilla, unabhängig vom Store des Betriebssystems
client.tls_set(ca_certs=certifi.where(), cert_reqs=ssl.CERT_REQUIRED)
# Privater ConnectMQ-Broker:
# client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.connect(BROKER, 8883, keepalive=60)
client.loop_start()
time.sleep(3)
client.loop_stop()
client.disconnect()
Installieren Sie die Abhängigkeiten mit pip install "paho-mqtt>=2.0" certifi. Die Übergabe von certifi.where() sorgt für identisches Verhalten auf jedem Betriebssystem und ist oft die schnellste dauerhafte Lösung. Halten Sie certifi aktuell, denn CA-Listen ändern sich im Laufe der Zeit.
Lösungen für jede Plattform
macOS
Python-Installer von python.org bringen ihr eigenes OpenSSL mit und nutzen nicht die macOS-Schlüsselbundverwaltung. Bis Sie das mitgelieferte Skript ausführen, haben sie überhaupt keine CA-Zertifikate, was zu unable to get local issuer certificate führt. Beheben Sie das, indem Sie das Zertifikatsskript des Installers ausführen:
/Applications/Python\ 3.12/Install\ Certificates.command
Passen Sie die Versionsnummer an Ihre Installation an. Homebrew-Python nutzt das CA-Bundle von Homebrew und funktioniert meist sofort; falls nicht, führen Sie brew reinstall ca-certificates aus. Die explizite Verwendung von certifi.where() umgeht das Problem in jedem Fall.
Windows
Unter Windows lädt Python Zertifikate aus dem Windows-Zertifikatspeicher. Fehler haben typischerweise eine von zwei Ursachen:
- Ein veralteter Root-Store auf einem Rechner ohne Updates, sodass eine neuere Root fehlt. Installieren Sie Windows-Updates oder verwenden Sie certifi.
- TLS-Inspektion durch einen Virenscanner oder einen Firmen-Proxy, was zu
self-signed certificate in certificate chainführt. Prüfen Sie den Aussteller, den das Skript aus Schritt 1 ausgibt; nennt er ein Sicherheitsprodukt, fängt dieses Produkt die Verbindung ab.
Besonders häufig ist der veraltete Root-Store auf Industrie-PCs und Edge-Gateways in der Fertigung, die oft jahrelang ohne Betriebssystem-Updates in einem abgeschotteten OT-Netz laufen. Hier ist ein mit der Anwendung ausgeliefertes, gepflegtes CA-Bundle (etwa certifi) meist robuster als der Store des Betriebssystems. Nehmen Sie die Aktualisierung des Bundles in Ihren Patch-Prozess für die Anlage auf.
Linux
Python nutzt die Standardpfade von OpenSSL, unter Debian und Ubuntu meist /etc/ssl/certs/ca-certificates.crt, auf Red-Hat-basierten Systemen /etc/pki/tls/certs/ca-bundle.crt. Fehlt das Paket oder ist es veraltet:
# Debian / Ubuntu / Raspberry Pi OS
sudo apt update && sudo apt install --reinstall ca-certificates
sudo update-ca-certificates
# Fedora / RHEL
sudo dnf reinstall ca-certificates
Docker und schlanke Images
Images wie python:3.12-slim enthalten meist CA-Zertifikate, alpine- und Distroless-Varianten aber unter Umständen nicht. Fügen Sie das Paket explizit hinzu:
FROM python:3.12-alpine
RUN apk add --no-cache ca-certificates
Oder installieren Sie certifi im Image und verweisen Sie im Code darauf. Sie können OpenSSL auch über die Umgebungsvariable SSL_CERT_FILE auf ein Bundle verweisen.
Die Variante Hostname Mismatch
Hostname mismatch erscheint, wenn Sie client.connect("203.0.113.10", 8883) aufrufen oder einen internen Alias verwenden, der nicht im Zertifikat steht. TLS prüft, ob der angefragte Name in den Subject Alternative Names des Zertifikats vorkommt. Verbinden Sie sich mit dem echten Hostnamen des Brokers. Für ConnectMQ ist das broker.connectmq.com. Wenn Sie zum Testen eine bestimmte IP erzwingen müssen, tragen Sie sie in Ihre hosts-Datei ein, statt sich per IP zu verbinden.
Private oder selbstsignierte Broker
Wenn Sie Ihren eigenen Broker mit einer privaten CA betreiben, wird ihm das öffentliche CA-Bundle nie vertrauen. Übergeben Sie die private CA explizit mit client.tls_set(ca_certs="my-ca.crt") und stellen Sie sicher, dass das Broker-Zertifikat den korrekten Hostnamen in seinem SAN enthält. Der Ablauf und der damit verbundene Wartungsaufwand werden unter selbstsignierte Zertifikate für MQTT erklärt.
Die Kette mit OpenSSL untersuchen
Wenn die Ausgabe von Python nicht genügt, zeigt OpenSSL die genaue Kette, die der Server sendet, und wo die Prüfung abbricht:
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -showcerts </dev/null
Lesen Sie den Abschnitt Certificate chain von oben: Eintrag 0 ist das Broker-Zertifikat, die folgenden Einträge sind Zwischenzertifikate, und jede i:-Zeile (Aussteller) sollte zur s:-Zeile (Subject) des nächsten Eintrags passen. Der abschließende Verify return code sagt Ihnen, ob der eigene Trust Store von OpenSSL die Kette akzeptiert. Meldet OpenSSL 0 (ok), Python aber scheitert, nutzt Python einen anderen Trust Store als OpenSSL; die Ausgabe von ssl.get_default_verify_paths() wie in Schritt 1 deckt das meist auf. Nennt der Aussteller von Eintrag 0 den Hersteller Ihres Virenscanners oder Ihrer Firewall, sehen Sie TLS-Inspektion statt des echten Broker-Zertifikats.
Um gezielt gegen certifi zu vergleichen, ergänzen Sie -CAfile mit dem Pfad, den python -c "import certifi; print(certifi.where())" ausgibt. Erfolg mit certifi und Fehlschlag ohne bestätigen, dass der Store des Betriebssystems das Problem ist.
Was Sie nicht tun sollten
Suchergebnisse schlagen oft diese Zeilen vor:
client.tls_set(cert_reqs=ssl.CERT_NONE)
client.tls_insecure_set(True)
Sie lassen den Fehler verschwinden, indem sie die Serverauthentifizierung deaktivieren. Ihre Zugangsdaten gehen dann an jeden Server, der antwortet, auch an den eines Angreifers. Verwenden Sie sie, wenn überhaupt, nur für ein paar Minuten lokales Debugging gegen einen Labor-Broker, niemals in Code, der committet wird. Der Artikel zu den Best Practices für MQTT-Sicherheit erklärt, warum die Authentifizierung des Brokers genauso wichtig ist wie die Verschlüsselung.
Checkliste für die schnelle Lösung
- Lesen Sie das Detail nach
certificate verify failed. - Führen Sie das Skript aus Schritt 1 in derselben Umgebung aus.
- Verwenden Sie
certifi.where()alsca_certsoder reparieren Sie den Store des Betriebssystems. - Verbinden Sie sich per Hostname, nicht per IP.
- Prüfen Sie mit
date -uauf Uhrzeitprobleme. - Sehen Sie sich den Zertifikatsaussteller an, um Proxys zu erkennen.
- Stellen Sie bei privaten Brokern Ihre eigene CA-Datei bereit.
Wenn Sie nach der Behebung des Handshakes immer noch nicht weiterkommen, behandelt unser Leitfaden zur Behebung von MQTT-TLS-Handshake-Fehlern Ports, SNI und Protokollversionen, und das Tutorial zu Python MQTT TLS zeigt vollständige Muster für die Produktion.
Warum öffentliche Zertifikate Zeit sparen
Die meisten Fehler bei der Zertifikatsprüfung verschwinden, wenn der Broker ein Zertifikat einer öffentlich vertrauenswürdigen CA verwendet und es automatisch erneuert. Genau so funktioniert ConnectMQ: ein verwalteter MQTT-Broker mit einem Let's-Encrypt-Zertifikat auf den Ports 8883 und 8084, sodass Standard-Python-Installationen es ohne zusätzliche Dateien verifizieren. Sie können jeden Test aus diesem Artikel auf dem kostenlosen öffentlichen MQTT-Broker nachvollziehen, der für Tests und nicht für Produktionsdaten gedacht ist, bevor Sie auf private Zugangsdaten umsteigen.
Starten Sie mit ConnectMQ
Registrieren Sie sich unter app.connectmq.com, wählen Sie den kostenlosen Serverless-Plan ohne Kreditkarte, erstellen Sie einen Broker und führen Sie das obige paho-mqtt-Beispiel mit Ihrem generierten Benutzernamen, Passwort und Ihrer Client ID aus.
Häufig gestellte Fragen
Warum funktioniert derselbe Code unter Linux, aber nicht unter macOS?
Der macOS-Build von python.org wird ohne CA-Zertifikate ausgeliefert, bis Sie „Install Certificates.command“ ausführen. Python unter Linux nutzt das System-Bundle, das in der Regel bereits installiert ist.
Ist es sicher, certifi statt des Stores des Betriebssystems zu verwenden?
Ja. certifi enthält die kuratierte Root-Liste von Mozilla. Halten Sie es einfach per pip aktuell, und beachten Sie, dass es keine privaten oder unternehmensinternen CAs enthält.
Bedeutet dieser Fehler, dass der Broker kompromittiert ist?
Selten. Fehlende CA-Bundles, falsche Hostnamen, Uhrzeitprobleme und TLS-Inspektion sind weitaus häufiger. Prüfen Sie zur Sicherheit den Ausstellernamen.
Brauche ich die Zertifikatsdatei von ConnectMQ?
Nein. ConnectMQ verwendet ein öffentlich vertrauenswürdiges Let's-Encrypt-Zertifikat, sodass ein Standard-CA-Bundle wie certifi oder der Store des Betriebssystems ausreicht.



