Das Symptom
Irgendwo klemmt eine HTTPS-Verbindung: Eine App meldet einen Zertifikatsfehler,
ein Skript bricht ab, ein Partner beschwert sich. Also der Griff zum Werkzeug,
das jeder kennt – openssl s_client -connect host:443. Die Ausgabe rauscht
durch, am Ende steht eine aufgebaute Verbindung, und die Sache ist für dich
erledigt: Bei dir geht es ja.
Zwei Tage später geht es beim anderen immer noch nicht. Beim genaueren Hinsehen stand ganz unten in deiner Ausgabe eine Zeile mit einem Prüfergebnis ungleich null, die im Scrollen untergegangen ist. Das Werkzeug hat nie behauptet, das Zertifikat sei in Ordnung. Es hat nur nicht widersprochen.
Die „Layer 8“-Wahrheit
s_client ist kein Prüfprogramm. Die eigene Dokumentation sagt das in einem Satz, den man einmal gelesen haben sollte: Dieses Kommando ist ein Testwerkzeug und darauf ausgelegt, den Handshake nach beliebigen Zertifikatsprüfungsfehlern fortzusetzen; im Ergebnis akzeptiert es jede vom Gegenüber gesendete Zertifikatskette, vertrauenswürdig oder nicht. Die Manpage hängt sogar die Warnung an, dass Nicht-Testanwendungen sich so nicht verhalten sollten, weil sie damit für Man-in-the-Middle-Angriffe angreifbar würden. Wer aus einem erfolgreichen Verbindungsaufbau auf ein gültiges Zertifikat schließt, hat das Werkzeug also nicht falsch bedient – er hat es für etwas gehalten, das es ausdrücklich nicht sein will.
Der Schalter, der aus dem Testwerkzeug einen Prüfer macht, heißt -verify_return_error. Er schaltet die Serverzertifikatsprüfung ein wie -verify, gibt Prüffehler aber zurück, statt weiterzumachen, und bricht den Handshake typischerweise mit einem Fatal Error ab. Zum Gegenstück -verify schreibt die Manpage entwaffnend offen, dass die Prüfung ohne diesen Schalter nach Fehlern weiterläuft, damit man alle Probleme einer Kette auf einmal sieht – und dass die Verbindung als Nebeneffekt niemals an einem Prüffehler des Serverzertifikats scheitert. Beide Verhaltensweisen sind sinnvoll. Man muss nur wissen, in welcher der beiden man gerade steckt.
Dieselbe Sorgfalt gilt für -showcerts. Was dort erscheint, ist die vom Server gesendete Zertifikatsliste in der Reihenfolge des Servers, und die Manpage stellt in einem eigenen Satz klar: Es ist keine verifizierte Kette. Das ist genau der Unterschied zwischen „was kam an“ und „was ergibt geprüft einen Pfad zu einem Vertrauensanker“. Für die Fehlersuche ist die rohe Liste trotzdem oft die wertvollste Information überhaupt, weil man an der Anzahl der Einträge sofort sieht, ob der Server seine Zwischenzertifikate überhaupt mitliefert.
Bleiben zwei Stolperstellen, die nichts mit Kryptografie zu tun haben. Erstens die SNI: Ohne -servername füllt OpenSSL die Erweiterung selbst mit dem an -connect übergebenen Namen, sofern der wie ein DNS-Name aussieht, und ohne -connect mit „localhost“ – seit Version 1.1.1 der Standard. Wer per IP-Adresse testet, prüft deshalb schnell ein ganz anderes Zertifikat als gemeint. Zweitens das Hängenbleiben im Skript: Die Manpage beschreibt, dass s_client in nicht-interaktiven Umgebungen die Verbindung vorzeitig schließen kann, vor allem mit TLS 1.3, und empfiehlt dagegen -ign_eof zusammen mit einer Umleitung von /dev/null. Beides sind keine Zertifikatsprobleme. Beides wird regelmäßig für eines gehalten.