Es ist ja nicht so, als ob PHP nichts kann, ganz im Gegenteil: PHP kann alles, nur nicht das, was man brauch – naja, oder zumindest nicht ausreichend einfach. Da wäre z.B. die Geschichte mit der sicheren Kommunikation über SSL/TLS. PHP selber muss, wenn es auf einem Server läuft, SSL nicht weiter beherrschen wie das Auslesen der Umgebungsvariablen, die vom Apache gesetzt werden. Möchte man nun aber von einem fremden Server via PHP ein paar Daten sicher abfragen oder übertragen bekommen, darf man eine Krücke nach der anderen benutzen.
Da wäre zuerst einmal fsockopen
, was an sich sehr nett ist, und was bei einigen Communities dafür sorgt, dass man aus dem IRC-Channel gekickt wird, weil einer der Ops grad schlechte Laune hat. Aber gut. das hat jeder Op mal. fsockopen
erlaubt es bei PHP Client-Sockets sowohl für udp
, tcp
als auch weitere Protokollwrapper zu öffnen: Darunter auch SSL und TLS. Das ist an sich schon einmal recht praktisch, jedoch steht nirgends beschrieben, inwiefern mit diesen Funktionen eine Überprüfung des vom Server verschickten Zertifikates möglich ist, bzw. ob man ein eigenes Client-Zertifikat mitgeben kann. Wahrscheinlich über einen undokumentierten Experimental-Hack im Context-System – oder im Klartext: Nein. Das kommt also für meinen Anwendungsfall nicht in Frage.
Gehen wir zum nächsten Kandidaten: cURL. cURL ist so das Leichtgewicht in Sachen HTTP-Sprechen für Anfänger. Man kann damit alles Mögliche machen, nur nicht vernünftig arbeiten. Nach dem man mit curl_init
sich eine Resource geholt hat, kann man diese zwar konfigurieren und mit Hilfe der Funktion curl_setopt
eine Reihe verschiedener Einstellungen setzen: Darunter die SSL-Version, ob das Client- oder Server-Zertifikat geprüft werden sollte, die Zertifizierungskette, zu verwendende Cipher und noch ein wenig mehr. Wenn meine Gegenstelle aber nicht SSL, sondern TLS spricht, dann versagt auch cURL kläglich.
Bliebe noch die Socket-API. Diese ist zwar nicht allzu intuitiv und das Interface ist genauso vermurkst wie cURL, dafür kann man hier aber wenigstens ansatzweise das tun, was ich möchte: Arbeiten.
Obwohl nicht ganz: Bevor das nämlich geht, muss man dafür sorgen, dass die OpenSSL-Extension geladen und konfiguriert ist. Unter Windows ist dies am Einfachsten mit dem Download der PECL-Binaries der Extensions und einem Eintrag
extension=php_openssl.dll
in seiner php.ini
getan, aber viel größer ist der Aufwand meist auch nicht – vorausgesetzt PHP findet die richtigen Binaries für OpenSSL an sich. Unter Linux ist der Support meist bereits durch die Distribution mit in PHP eingebaut, ansonsten muss man sich PHP selber bauen, da es für die OpenSSL-Erweiterung AFAIK kein eigenes Paket gibt (für Debian habe ich zumindest keines gesehen).
OK, also wir haben die Socket-API, wir haben OpenSSL, aber wie quetscht man nun aus dem Socket das Zertifikat raus? Sucht man da bei Google, bekommt man unter anderem jemanden, der versucht hat, mit PHP SSL-Client-Zertifikat-Authentifizierung zu realisieren. Gut, nehmen wir seinen Ansatz und bauen das etwas um:
$site_cert = NULL;
$context = stream_context_create();
$result = stream_context_set_option($context, 'ssl', 'verify_host', true);
$result = stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
$result = stream_context_set_option($context, 'ssl', 'capture_peer_cert', true);
if ($fp = stream_socket_client("ssl://svn.benny-baumann.de:443/", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context)) {
if ($options = stream_context_get_options($context)) {
var_dump($options);
if (isset($options['ssl']) && isset($options['ssl']['peer_certificate'])) {
$site_cert = $options['ssl']['peer_certificate'];
}
}
fclose($fp);
}
if ($site_cert) {
openssl_x509_export($site_cert, $str_cert);
$pubkey = openssl_pkey_get_public($str_cert);
$keyinfo = openssl_pkey_get_details($pubkey);
var_dump($keyinfo);
}
Soweit so gut: SSL funktioniert. Wenn man aber jegliche Vorkommen von SSL durch TLS austauscht, so rennt man in einen weiteren Bug von PHP.
ARGH!
Hi BenBE,
hast du dafür schon eine Lösung gefunden?
Und in deinem Geshi fehlt die erkennung der funktion „openssl_key_get_details“ und „stream_socket_client“. ^^
MfG
neo
Kommentar by neo — 05.02.2009 @ 21:38:13