Die Verbindung: openssl s_client
s_client baut eine TLS-Verbindung auf und gibt aus, was der Server dabei präsentiert. -servername ist wichtig: Ohne SNI liefern viele Server das falsche Zertifikat, weil auf einer IP mehrere Domains liegen. Das abschließende </dev/null beendet die Sitzung, sonst wartet openssl auf Eingaben.
openssl s_client -connect beispiel.de:443 -servername beispiel.de </dev/nullIn der Ausgabe stehen die Zertifikatskette (Certificate chain), das Zertifikat selbst in Base64, der Aussteller, die verwendete TLS-Version und am Ende "Verify return code". Steht dort 0 (ok), ist die Kette gegen die Stammzertifikate des Systems gültig. Andere Codes benennen das Problem: 10 für abgelaufen, 21 für ein nicht verifizierbares erstes Zertifikat (meist fehlt das Zwischenzertifikat), 18 für selbstsigniert.
Ablaufdatum, Aussteller und Namen auslesen
Die Ausgabe von s_client lässt sich an openssl x509 weiterreichen, das einzelne Felder ausgibt. So entstehen Einzeiler für Skripte und Cronjobs.
openssl s_client -connect beispiel.de:443 -servername beispiel.de </dev/null 2>/dev/null | openssl x509 -noout -dates
openssl s_client -connect beispiel.de:443 -servername beispiel.de </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject
openssl s_client -connect beispiel.de:443 -servername beispiel.de </dev/null 2>/dev/null | openssl x509 -noout -ext subjectAltName
openssl s_client -connect beispiel.de:443 -servername beispiel.de </dev/null 2>/dev/null | openssl x509 -noout -enddate -checkend 1209600-dates zeigt notBefore und notAfter, -ext subjectAltName alle Hostnamen, für die das Zertifikat gilt. -checkend 1209600 prüft, ob das Zertifikat in den nächsten 14 Tagen (in Sekunden) abläuft, und liefert Exit-Code 1, wenn ja. Das ist die einfachste Form eines Ablauf-Checks in einem Shell-Skript.
Kette und Zwischenzertifikate
Ein Zertifikat, das auf dem Entwicklungsrechner funktioniert und auf Android-Geräten oder in Mailclients scheitert, hat fast immer eine unvollständige Kette: Der Server liefert nur sein eigenes Zertifikat, nicht das Zwischenzertifikat der CA. Desktop-Browser besorgen sich das fehlende Glied selbst, andere Clients nicht. -showcerts zeigt alle Zertifikate, die der Server tatsächlich sendet.
openssl s_client -connect beispiel.de:443 -servername beispiel.de -showcerts </dev/null 2>/dev/null | grep -E "^ [0-9]+ s:|^ i:"Erwartet wird eine Kette aus zwei oder drei Einträgen: das Serverzertifikat (s: mit dem Hostnamen), dann das Zwischenzertifikat, dessen s: dem i: des Serverzertifikats entspricht. Erscheint nur ein Eintrag und der Verify-Code ist 21, fehlt das Zwischenzertifikat in der Serverkonfiguration: Bei nginx gehört die fullchain.pem in ssl_certificate, bei Apache seit 2.4.8 ebenfalls in SSLCertificateFile.
Mailserver: SMTP, IMAP und POP3
Mailserver-Zertifikate laufen unbemerkt ab, weil kein Browser warnt. Ports mit implizitem TLS (465, 993, 995) funktionieren wie 443. Ports mit STARTTLS (25, 587, 143, 110) beginnen unverschlüsselt und wechseln erst nach einem Kommando, deshalb braucht openssl die Option -starttls mit dem Protokoll.
openssl s_client -connect mail.beispiel.de:465 -servername mail.beispiel.de </dev/null
openssl s_client -connect mail.beispiel.de:993 -servername mail.beispiel.de </dev/null
openssl s_client -connect mail.beispiel.de:587 -starttls smtp -servername mail.beispiel.de </dev/null
openssl s_client -connect mail.beispiel.de:143 -starttls imap -servername mail.beispiel.de </dev/null
openssl s_client -connect mail.beispiel.de:25 -starttls smtp </dev/nullDer Hostname im Zertifikat muss zu dem passen, den die Mailclients verwenden. Ein Server, der als mail.beispiel.de konfiguriert ist, aber ein Zertifikat für server42.hoster.de ausliefert, erzeugt in Outlook und Apple Mail Warnungen bei jedem Start. Für den MX zählt der Hostname aus dem MX-Record, nicht die Domain.
TLS-Version und Cipher
Mit -tls1_2 oder -tls1_3 erzwingst du eine Version und siehst, ob der Server sie anbietet. Scheitert -tls1_3, spricht der Server noch kein TLS 1.3; gelingt -tls1 oder -tls1_1, sind veraltete Versionen noch aktiv, die abgeschaltet gehören.
openssl s_client -connect beispiel.de:443 -servername beispiel.de -tls1_3 </dev/null 2>&1 | grep -E "Protocol|Cipher"
openssl s_client -connect beispiel.de:443 -servername beispiel.de -tls1_1 </dev/null 2>&1 | grep -E "Protocol|error"Zertifikatsdatei lokal prüfen
Vor dem Einspielen lässt sich eine Zertifikatsdatei ohne Server lesen, und ein häufiger Fehler beim Wechsel ist ein Zertifikat, das nicht zum privaten Schlüssel passt. Stimmen die beiden Modulus-Hashes überein, gehören Zertifikat und Schlüssel zusammen.
openssl x509 -in cert.pem -noout -text
openssl x509 -in cert.pem -noout -modulus | openssl md5
openssl rsa -in privkey.pem -noout -modulus | openssl md5Von der Einzelprüfung zum Monitoring
Der -checkend-Einzeiler in einem Cronjob ist besser als nichts, prüft aber nur die Hosts, die jemand eingetragen hat, und schickt seine Mail an ein Postfach, das jemand lesen muss. Für viele Kundendomains samt Mailservern nimmt DomainWarn diese Arbeit ab: Es prüft jedes Zertifikat alle sechs Stunden, meldet 30 und 14 Tage vor Ablauf, erkennt Kettenfehler und passende Hostnamen und zeigt im Daily Digest, welche Zertifikate als Nächstes fällig sind.
Häufige Fragen
- openssl meldet "unable to get local issuer certificate", das Zertifikat ist aber gültig.
- Entweder fehlt das Zwischenzertifikat in der Serverkonfiguration (mit -showcerts prüfen), oder das System hat veraltete Stammzertifikate. Auf macOS hilft -CAfile mit einem aktuellen CA-Bundle, unter Linux das Paket ca-certificates aktualisieren.
- Warum zeigt openssl ein anderes Zertifikat als der Browser?
- Meist fehlt -servername. Ohne SNI liefert der Server sein Standardzertifikat, das zu einer anderen Domain auf derselben IP gehört.
- Wie prüfe ich das Zertifikat auf einem Server, der noch nicht im DNS steht?
- Mit -connect auf die IP-Adresse und -servername auf den künftigen Hostnamen: openssl s_client -connect 203.0.113.10:443 -servername beispiel.de. So testest du einen Umzug, bevor der A-Record umgestellt wird.