SSL-Verifizierung mit PHP schlägt fehlt (Fehler 14090086)

Wenn dich PHP mit dieser Fehlermeldung begrüßt, dann liegt es wohl daran, dass beim Abruf einer SSL-Ressource die Identität nicht verifiziert werden konnte. Und das ist auch gut so, denn der Sinn von SSL ist ja das Herstellen einer gesicherten Verbindung. Im Folgenden beschreibe ich zwei Wege, den Fehler zu beheben. Die erste Lösung ist quick’n’dirty und behebt nicht die Ursache und die zweite Lösung kann man als Best Practice betrachten.
So sieht die Fehlermeldung aus, die auch auftreten kann, wenn du eine Ressource mit z.B. imagecreatefrompng() abrufst:

Verifizierung des SSL Hosts unterdrücken

Im Grunde lässt sich der Fehler ganz einfach umgehen. Denn file_get_contents() kann mit Parametern gefüttert werden, die die Verifizierung der Ressource unterdrücken. Das sieht dann z.B. so aus:

Wer auf eine eigene Ressource zurückgreift, z.B. im lokalen Netz oder zu Testzwecken, kann damit leben. Aus Sicherheitsgründen ist die Lösung allerdings nicht zu empfehlen.

Die Zertifikate des SSL Hosts manuell einrichten

Der o.g. Fehler hat eigentlich eine simple Ursache: PHP kennt den Aussteller des Zertifikates nicht und verweigert aus Sicherheitsgründen den Aufbau einer Verbindung. Wir müssen also nur dafür sorgen, dass PHP dem Aussteller vertraut. Das passiert, in dem wir dessen Zertifikat bzw. die komplette Zertifikatskette in eine Datei packen und diese dann an PHP übermitteln.

Schritt besorgen wir uns die Zertifikate der Certification Authority. Das funktioniert recht einfach mit dem SSL-Checker von sslshopper.com. In meinem Fall liefert dieser nur zwei Zertifikate zurück. Das eigentliche Server-Zertifikat, das für meine Seite ausgestellt wurde, und das Root-Zertifikat für die Stelle, die mir das Zertifikat ausgestellt hat – die sogenannte Certification Authority. Es ist durchaus auch denkbar, dass in dieser Liste noch andere Zertifikate stehen, die so genanten Intermediate-Zertifikate; dazu später mehr.

 SSL Zertifikats-Kette mit den Intermediate Zertifikaten

SSL Zertifikats-Kette mit den Intermediate Zertifikaten

Die Zertifikats-Kette zusammenstellen

Wir beginnen also mit dem Zertifikat, mit dem unserer Server ausgestattet ist. In OS X bzw. Linux ist das über die Kommandozeile möglich. Hierzu muss nur der Parameter connect angepasst werden (die eigene URL) und die Ausgabe-Datei am Ende des Kommandos:

Fertig, die Datei www.nickyreinert.de.crt liegt in deinem Home-Verzeichnis und enthält das SSL-Zertifikat für deinen Server.

Die Zertifikatskette vervollständigen

Als nächstes besorgen wir uns die komplette Zertifikatskette. Auch das ist in den meisten Fällen sehr einfach. Dazu reicht es, nach den Common-Names zu suchen, die der SSL-Checker uns gemeldet hat. Also z.B. thawte DV SSL SHA256 CA und thawte Primary Root CA – G3.

Das führt uns natürlich zu der Seite von Thawte und diesen beiden Ressourcen:

  • https://search.thawte.com/library/VERISIGN/ALL_OTHER/KB_IMAGES/SO26817/Certs/SHA2/thawte_DV_SSL_SHA256_CA.cer
  • https://www.thawte.com/roots/thawte_Primary_Root_CA-G3_SHA256.pem

Diese laden wir uns herunter und speichern sie in einer Datei ab, also auch in deinem Home-Verzeichnis unter ~/thawteRoot.crt bzw. ~/thawteIntermediate.crt ab. Fertig. Und kurz vor dem Ziel!

Die Zertifikate zusammenführen

Der nächste Schritt ist einfach. Wir packen nun alle Zertifikate in eine Datei. Das machen wir entweder mit einem Text-Editor oder über die Kommandozeile. Die Reihenfolge sollte eigentlich keine Rolle spielen. Ich empfehle aber, das Root-Zertifikat an den Anfang der Datei zu packen, ggf. gefolgt von den Intermediates und schließlich, ganz am Ende, dem eigenen Server-Zertifikat. Auf der Kommandozeile sieht das z.B. so aus:

Das wars.

PHP die Zertifikatskette mitteilen

Als letztes müssen wir unserem PHP-Script noch mitteilen, dass es auch diese Zertifikatskette berücksichtigen soll. Auch das passiert über den Parameter, den ich oben schon angesprochen habe. Doch diesmal erlauben wir PHP, den SSL-Host zu verfizieren:

Geschafft. PHP sollte nun, zur Laufzeit, den SSL-Host überprüfen und dabei auf die Zertifikate zurückgreifen, die die Authentizität einwandfrei bestätigen.

Man kann den Verweis auch an anderer Stelle definieren. Die PHP-Funktion openssl_get_cert_locations(); teilt uns mit, wo PHP nach gültigen Zertifikaten sucht:

Hier fällt z.B. die Datei /Applications/XAMPP/xamppfiles/share/curl/curl-ca-bundle.crt auf. Diese wird in der php.ini mit dem Parameter

gesetzt. Auch in dieser Datei liegen eine Menge von Root- bzw. Intermediate-Zertifikaten. Mitunter macht es mehr Sinn, diese Einstellung in der php.ini zu nutzen, damit auch curl() darauf zurückgreifen kann.

Wenn du PHP-FPM benutzt, ist die Config-Datei etwas anders aufgebaut:

Zusammenfassung

Die SSL-Fehlermeldung hat ihren Sinn. PHP ist nicht in der Lage, die Authentizität des Servers zu überprüfen. Der korrekte Weg, das Problem zu beheben, ist das Zertifikat der entsprechenden Seite herunterzuladen, die Intermediate-Zertifikate und das Root-Zertifikat zu besorgen und alle Zertifikate im PEM-Format in eine Text-Datei zu packen.
Danach kannst du entweder auf die URL zugreifen und über den Stream-Kontext auf diese Datei mit der Zertifikats-Kette verweisen, oder du legst in den globalen PHP-Einstellungen fest, dass PHP diese Datei beim nächsten Mal berücksichtigen soll.