openssl-win-root: Den Windows-Zertifikatsspeicher mit Ruby nutzen
Wer Ruby unter Windows einsetzt, sieht früher oder später eine HTTPS-Anfrage mit einem Fehler bei der Zertifikatsprüfung scheitern, obwohl sich dieselbe Seite im Browser problemlos öffnen lässt. openssl-win-root ist ein kleines Gem, das genau hier hilft.
Zuerst die Ehre, wem sie gebührt: Geschrieben hat das Gem Stas Ukolov, das ursprüngliche Repository ist github.com/ukoloff/openssl-win-root. Die erste Version erschien 2015. Mein Anteil ist klein, ich beschreibe ihn weiter unten.
Das Problem
OpenSSL in Ruby schaut nicht in den Windows-Zertifikatsspeicher, sondern braucht eigene CA-Zertifikate. RubyInstaller liefert zwar eine Datei cacert.pem mit, aber wie das README des Gems erwähnt, wird diese Datei nicht automatisch aktualisiert. Sie entspricht dem Stand, als Ihre Ruby-Version gebaut wurde. Neuere Stammzertifikate können also fehlen.
Windows hingegen hält seinen eigenen Speicher für vertrauenswürdige Stammzertifikate aktuell. Dort liegen auch Zertifikate, die Ihre Firma installiert, zum Beispiel per Gruppenrichtlinie. Ruby sieht sie schlicht nicht.
Was das Gem macht
Wenn Sie es unter Windows laden, macht openssl-win-root Folgendes:
- Es liest die vertrauenswürdigen Stammzertifikate aus dem Windows-Systemspeicher (über die
crypt32-API, mit Hilfe von Fiddle). - Es schreibt sie als PEM-Dateien in einen Ordner
pemim Verzeichnis des Gems, mit den Dateinamen, die OpenSSL für ein Zertifikatsverzeichnis erwartet. - Es fügt diesen Ordner dem Standard-Zertifikatsspeicher von OpenSSL hinzu und setzt die Umgebungsvariable
SSL_CERT_DIRdarauf.
Ein Netzwerkzugriff ist dafür nicht nötig. Auf anderen Betriebssystemen tut das Gem nichts, es kann also problemlos in einem plattformübergreifenden Projekt stehen.
So verwenden Sie es
Fügen Sie es dem Gemfile hinzu, nur für Windows:
gem 'openssl-win-root' if Gem.win_platform?
Führen Sie danach bundle aus oder installieren Sie es direkt:
gem install openssl-win-root
Laden Sie es, bevor Sie HTTPS-Anfragen machen:
require 'openssl/win/root'
require 'net/http'
Net::HTTP.get(URI('https://www.ruby-lang.org/')).length
Verwendet Ihr Projekt Bundler.require, wie etwa Rails, brauchen Sie das explizite require nicht.
Die exportierten Zertifikate lassen sich auch ausserhalb von Ruby nutzen. Setzen Sie SSL_CERT_DIR oder die Option -CApath des Befehls openssl auf den Ordner, den diese Methode liefert:
OpenSSL::Win::Root.path
Worauf Sie achten sollten
- Die Zertifikate werden bei jedem
requirein das Verzeichnis des Gems exportiert. Der Prozess braucht dort also Schreibrechte. - Unter Windows Server kommt es vor, dass der Zertifikatsspeicher ein Stammzertifikat noch nicht aktiviert hat. Als Workaround beschreibt das README, die Seite einmal in einem (Microsoft-)Browser zu öffnen oder eine Anfrage über
WinHttpRequestmitwin32olezu machen. Das bringt Windows irgendwie dazu, das fehlende Stammzertifikat zu aktivieren. - Die neueste Version, 1.1.2, stammt vom Januar 2023, und das Gem ruft die Windows-API über Fiddle auf. Testen Sie es mit der Ruby-Version, die Sie tatsächlich einsetzen.
Meine Rolle
Als Ruby 3.2 erschien, funktionierte das Gem nicht mehr: Es rief File.exists? auf, und diese Methode wurde in Ruby 3.2 entfernt. Das ist dasselbe Problem, über das ich in meinem Beitrag zum Gem file_exists schreibe. Ich habe dafür einen Pull Request mit der Ein-Wort-Korrektur eröffnet.
Ich bin einer der beiden Inhaber des Gems auf RubyGems und habe im Januar 2023 die Version 1.1.2 aus meinem Fork veröffentlicht. Diese Version hat sehr wenig geändert:
- aus
File.exists?wurdeFile.exist?, damit das Gem unter Ruby 3.2 wieder läuft, - der nicht benötigte Build-Schritt für eine Extension wurde aus dem Gemspec entfernt,
- das README erhielt Hinweise zur
cacert.pemvon RubyInstaller und zum oben erwähnten Verhalten unter Windows Server.
Alles andere, also die eigentliche Idee und die Umsetzung, ist die Arbeit von Stas.