OpenSSL: Implementierung innerhalb eines Client- und Server-Programms, Teil 2

Der erste Artikel zur OpenSSL-Implementierung legte der Fokus auf den SSL-Server. Der zweite Teil konzentriert sich auf das zukünftige Client-Programm. Mit dem Gespann aus Server und Client des Beispiels ergibt sich eine abgeschlossene Sicht auf die OpenSSL-Programmierung.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 19 Min.
Von
  • Oliver Müller
Inhaltsverzeichnis

Der erste Artikel zur OpenSSL-Implementierung legte der Fokus auf den SSL-Server. Dieser Beitrag konzentriert sich auf das zukünftige Client-Programm. Mit dem Gespann aus Server und Client des Beispiels ergibt sich eine abgeschlossene Sicht auf die OpenSSL-Programmierung und eine Grundlage für eigene Experimente.

Der erste Teil behandelte den SSL-Server "WOPR". Erste Tests der Server-Funktionen waren mit OpenSSLs eigenem s_client möglich. An der Stelle soll nun ein speziell für WOPR programmierter SSL-Client – das Terminal – folgen. Auch der zweite Teil orientiert sich an dem bisweilen recht eigenwilligen Dialog mit dem Computer WOPR aus dem Film "War Games".

Für das Client-Programm gelten die gleichen Bedingungen fürs Kompilieren und Linken aus Teil 1: Ein moderner C++-Compiler, wie GNUs g++, GNU make und ein Unix-kompatibles System oder eine Unix-kompatible Systemschicht, zum Beispiel Cygwin, sind unabdingbar.

Mehr Infos

OpenSSL – Struktur und Beispielcode

Den für für die Umsetzung in der Praxis benötigten Beispielcode findet man hier (mueller_openssl_wopr.tar).

Im Gegensatz zum WOPR-Server ist das WOPR-Terminal eher schlicht gestrickt. Es wird vollständig in der Datei terminal.cpp implementiert. Den Aufwand, eine separate Klasse zu erzeugen, spart man durch die einfachen Funktionen. Das Terminal arbeitet die Initialisierungsschritte sequenziell ab und endet in einer Schleife, die das Wechselspiel aus Befehlseingabe und Serverausgabe verarbeitet. Alles liegt in der – wenn auch etwas größeren, aber dennoch überschaubaren – main()-Funktion.

Die ersten Schritte hin zu den OpenSSL-Funktionen sind beim Client identisch mit denen des Servers.Im Terminal-Programm ist SSL_library_init() zum Initialisieren von OpenSSL die erste aufgerufene Funktion. Anschließend laden SSL_load_error_strings() und ERR_load_BIO_strings() die Fehlermeldungen. Schließlich erzeugt wieder SSL_CTX_new() den SSL-Kontext – dieses Mal jedoch für einen SSLv3-Client. Ab jetzt geht es clientspezifisch weiter.

Der nächste Schritt für den Client ist, ihm mitzuteilen, wo er die CA-Zertifikate findet. Auf diese Weise kann der Client Server dahingehend prüfen, ob sie diejenigen sind, für die sie sich ausgeben. Grundsätzlich lassen sich bei OpenSSL mehrere CA-Zertifikate in einer einzigen, großen PEM-kodierten Datei und/oder in einem Verzeichnis mit einzelnen PEM-Dateien für jedes CA-Zertifikat bereitstellen. Beide Orte kann man parallel über den zweiten und dritten Parameter der Funktion SSL_CTX_load_verify_locations() übergeben. Soll einer der beiden nicht genutzt werden, setzt man ihn auf NULL. Als erster Parameter ist das Context-Objekt zu übergeben, für das die CA-Lokationen zu setzen sind. Anschließend können Verbindungen, die man von dem Context-Objekt ableitet, Serverzertifikate auf ihre Gültigkeit überprüfen.

An der Stelle nutzt das WOPR-Terminal lediglich die Option, eine einzige PEM-Datei (gegebenenfalls mit mehreren CA-Zertifikaten) zu laden. Der Verzeichnisparameter ist daher auf NULL gesetzt. Optional erfolgt jetzt das Initialisieren des "pseudo random number generator" (PRNG). Doch hierzu später mehr.

Wie im ersten Teil angesprochen, verwendet OpenSSL eine eigene Abstraktionsschicht zum Kapseln von BSD-Netzwerk-Sockets. Diese BIO-Objekte lassen sich aus dem Context-Objekt um SSL-Verschlüsselung aufgewertet instanziieren. Das ist der nächste Schritt im Terminal-Programm. Die Funktion BIO_new_ssl_connect() erzeugt aus dem Context-Objekt heraus ein neues BIO-Objekt mit SSL-Verschlüsselung.

Über BIO_get_ssl() lässt sich die auf dem BIO thronende SSL-Engine (= SSL-Objekt) ermitteln. Hieran erkennt man das Schichtenmodell. Das SSL-Objekt verwendet das darunter liegende BIO, um seine verschlüsselten Daten ins Netz zu senden und daraus zu empfangen. Das BIO wiederum setzt auf einem Netzwerk-Socket auf, um die Verbindung zum Netz herzustellen. Über das SSL-Objekt als obersten Teil des "Stapels" lassen sich später alle Schreib- und Leseoperationen realisieren.

An der Stelle nutzt das Terminal das SSL-Objekt auch, um in der SSL-Engine das Flag SSL_MODE_AUTO_RETRY zu setzen. Damit geht die SSL-Engine in den "Blocking Mode". Lese- und Schreib-Funktionen kehren nur bei erfolgreich abgeschlossenem "handshake" zurück. Andernfalls würden die Funktionen Fehler zurückliefern, die gesondert in der Applikation zu behandeln wären.