Smartcards zur Nutzerauthentifizierung in Java EE-Webanwendungen

Security mit Smartcards
Kommentare

Smartcards, Kartenleser, PKI, digitale Signatur – für manche Entwickler, die sich an die Authentifizierung mittels Login und Passwort gewöhnt haben, klingt das nach einem komplexen System, hohem Aufwand oder proprietären Nischensystemen. Dieser Artikel räumt einige dieser Vorurteile aus und tritt mit technischen Handreichungen und einfach nachvollziehbaren Beispielen den Gegenbeweis an.

Die Nutzerauthentifizierung mittels Username und Passwort ist immer noch Standard bei der Entwicklung von Webanwendungen. Auch in vielen Web-Services-Umgebungen ist Authentifizierung nach wie vor ein Problem. Ein SSL-Client-Zertifikat mit einer Passphrase – am besten PIN-geschützt auf einer Smartcard – erhöht die Sicherheit der Authentifizierung und ermöglicht auch die sichere Nutzung von Anwendungen über das Internet. Basierend auf der Erfahrung aus mehreren Projekten zeigt dieser Artikel am Beispiel des Tomcat, wie einfach dieser Authentifizierungsmechanismus realisierbar ist – sowohl in neu entwickelten Anwendungen als auch als Erweiterung zu bereits bestehenden Projekten.

Zertifikate – warum und wofür

Gegenüber der überwiegend verwendeten Passwort-Authentifizierung bietet die Authentifizierung mit Client-Zertifikaten mehrere Vorteile:
  • Es gibt keine schwachen Passwörter mehr.
  • Die Weitergabe der Login-Information wird erschwert.
  • Mit einem Zertifikat kann der Zugang zu vielen Diensten ermöglicht werden (Single Sign-on).
  • Diese Form der Authentifizierung lässt sich sehr gut auch auf andere Dienste wie z.B. Web Services übertragen.
Ein universelles Allheilmittel sind Zertifikate jedoch nicht. Für ihren Einsatz müssen einige Voraussetzungen erfüllt sein. Zunächst müssen die Zertifikate ausgestellt und verwaltet werden, d.h., es muss eine PKI (Public Key Infrastructure, in diesem Falle eine CA) vorhanden sein. Solange Kartenleser und digitale Ausweise nicht ausreichend verbreitet sind, eignet sich die Smartcard-Authentifizierung eher für geschlossene Benutzergruppen – also firmenintern oder im B2B-Bereich. In besonders sicherheitskritischen Projekten etwa im Healthcare-Bereich kann die Verwendung von Smartcards zu den Anforderungen gehören. Eine Smartcard-Authentifizierung kann aber auch sicherheitsbewussten und technisch versierten Nutzern optional angeboten und parallel zur Anmeldung mit Username und Passwort betrieben werden (wie bereits bei manchen Online-Banking-Angeboten).
Kartenleser  Bei der Auswahl von Kartenlesern, Karten und Software für die Einbindung in den Browser ist es ratsam, alles aus einer Hand zu beziehen, um Kompatibilitätsprobleme zu vermeiden. Es gibt verschiedene Klassifikationen von Kartenlesern: Das Mindeste, was ein Kartenleser mitbringen muss, ist eine eigene Tastatur (oft als Kartenleser Klasse 2 bezeichnet), um ein Ausspähen der PIN durch Keylogger o.Ä. auszuschließen. Wird serverseitig ein erneutes Handshake gestartet (z.B. nach Ablauf der Session bei Inaktivität), so ist es nicht selbstverständlich, dass ein Benutzer zur erneuten PIN-Eingabe aufgefordert wird, solange der Browser nicht geschlossen wird. Dieses PIN-Caching birgt im nicht privaten Bereich Sicherheitsrisiken, falls der User den Arbeitsplatz verlässt und weder den Browser schließt noch die Karte aus dem Leser entfernt. Außerdem sollten Browser und Plattformunterstützung beim Hersteller erfragt werden.

Zertifikate – auch für Clients

Im Falle der Kommunikation über HTTPS weist sich das Serversystem gegenüber dem Nutzer mit einem Zertifikat aus. So kann der Nutzer sicher sein, mit dem entsprechenden System, z.B. der Bank, zu kommunizieren. Die Authentifizierung des Nutzers erfolgt dann aber meistens über den so gesicherten Kanal mittels Username und Passwort. Der Server kann auch vom Client verlangen, sich beim Verbindungsaufbau (SSL Handshake) ebenfalls mit einem Zertifikat auszuweisen. Dieses Zertifikat ist idealerweise auf einer Smartcard gespeichert und mit einer PIN geschützt und verlässt so niemals die Karte; vielmehr wird auf dem Kartenleser der SSL-Session-Key signiert.

Zertifikate – die Praxis

Zum Entwickeln und Testen werden weder Kartenleser noch Smartcards benötigt: Beteiligt sind ein Browser mit SSL-Client-Zertifikat und ein Application Server (unsere Beispiele beziehen sich auf Tomcat in der Version 5.5.7). Die Umstellung auf den produktiven Einsatz (etwa mit Apache2/mod_ssl/mod_jk und tatsächlichen Kartenlesern) ist dann mit wenigen Handgriffen erledigt. Das Aufsetzen einer CA zu Entwicklungszwecken und die Erzeugung der Zertifikate sind im Textkasten beschrieben. Mit dem Import der CA- und Client-Zertifikate in den Browser ist auf Nutzerseite die Umstellung erledigt. Zum Testen mit mehreren Accounts ist es ratsam, im Browser die Option zu aktivieren, jedes Mal bei der SSL-Authentifizierung nachzufragen, welches Zertifikat vorgewiesen werden soll. Serverseitig müssen erst einmal der HTTPS Connector aktiviert und ein Server-Zertifikat erzeugt werden. Diese beiden Schritte sind in der server.xml des Tomcat und hier erklärt. Damit der Server ein Zertifikat vom Client verlangt, muss die Option clientAuth=  true  in der Tomcat-Konfiguration gesetzt werden, sodass die Konfiguration des SSL Connector wie in Listing 1 aussieht. Dann fragt Tomcat das Zertifikat ab.

Programmatische vs. deklarative Sicherheit

Deklarative Sicherheit bedeutet, dass Authentifizierung und Autorisierung mit den im Application Server enthaltenen Mitteln gelöst und ausschließlich in Konfigurationsdateien eingestellt werden (Container Managed Security). Bei programmatischer Sicherheit hingegen übernimmt die Anwendung selbst diese Aufgaben. Beide Ansätze haben Vor- und Nachteile: Deklarative Sicherheit spart Code und regelt alles an einer Stelle. Eine Designentscheidung, die so oder so bewertet werden kann: Durch die typische Aufgabentrennung zwischen Entwickler und Administrator bzw. Hoster gibt der Entwickler die Kontrolle über die Authentifizierung und Autorisierung ab. Abhängig von den benutzten Features kann deklarative Sicherheit aber die Portierbarkeit einschränken und eine Anwendung auf einen Application Server oder eine bestimmte Version des Servers oder der Servlet-Spezifikation festlegen. Beide Ansätze können sich gut ergänzen. Tomcat kennt SSL-Client-Zertifikate als Authentifizirungsmechnismus; dieses Verfahren kann im Deployment Descriptor ( web.xml ) der Anwendung eingestellt werden:
CLIENT-CERT
Deklarative Sicherheit stützt sich im Tomcat auf so genannte Realms [s. „Literatur“]. Diese sehen eine Authentifizierung auf Basis von Username und Passwort vor. Werden sie zusammen mit Client-Zertifikaten eingesetzt, so ersetzt der Distinguished Name des Zertifikats den Usernamen; das Passwort wird ignoriert. Ein Usereintrag in der tomcat-users.xml für die Memory-Realm sähe z.B. so aus:
<user username="EMAILADDRESS=mustermann@tembit.de,
CN=Markus Mustermann, O=Tembit, L=Berlin, S=Berlin,
C=DE" password="null" roles="admin"/>
Bis einschließlich Version 5.5.4 funktioniert das jedoch nur mit der sehr einfachen und recht unflexiblen MemoryRealm. Erst ab Version 5.5.5 sind die Probleme im Zusammenspiel mit JDBC- und DatasourceRealms behoben (vgl. Bug 30352). Mit einer auf obiger Lösung aufbauenden Konfiguration kann eine Authentifizierung mit Client-Zertifikaten in aktuellen Tomcat-Versionen inzwischen schnell und einfach realisiert werden. Die Kompatibilität zu älteren Versionen, die Flexibilität sowie der geringe Aufwand einer eigenen Implementierung lassen aber auch den im Folgenden beschriebenen programmatischen Sicherheitsansatz sinnvoll erscheinen.

Serverseitige Zertifikatsextraktion und -analyse

In einem Servlet wird auf die Zertifikatsinformation wie in Listing 2 zugegriffen.
//get certificates from request
Object o = request.getAttribute("javax.servlet.request.X509Certificate");
java.security.cert.X509Certificate clientcert = null;
java.security.cert.X509Certificate[] certificates = null;
if(o != null) {
certificates = (java.security.cert.X509Certificate[]) o ;
clientcert = certificates[0];
} else { //error: no client cert in request }
Zu beachten ist hier die Unterscheidung zwischen den Klassen java.security.cert.X509Certificate , javax.security.cert.X509Certificate und dem Attribut javax.servlet.request.X509Certificate . Hier gilt: Beim Arbeiten mit Zertifikaten wird stets die Klasse X509Certificate aus dem Paket java.security.cert benutzt. Aus diesem Zertifikatsobjekt können eine Reihe Informationen abgefragt und zur Authentifizierung und Autorisierung eingesetzt werden:
  • Distinguished Name (DN) des Inhabers: getSubjectDN()
  • Seriennummer des Zertifikats: getSerial-Number()
  • Gültigkeitszeitraum des Zertifikats: checkValidity()
  • verschiedene Informationen zum Aussteller: getIssuerDN()
  • öffentlicher Schlüssel bzw. Fingerprint: getSignature() Informationen zu den zulässigen Verwendungszwecken, Signaturalgorithmen und Protokollversionen des Zertifikats
Bei Verwendung von Apache2 mit mod_ssl bzw. der oben beschriebenen Connector-Konfiguration werden der Aussteller, die Unterschrift des Ausstellers und der Gültigkeitszeitraum automatisch geprüft.

Integration in die Anwendung

Wird eine bestehende Anwendung komplett auf die Authentifizierung mittels Zertifikaten umgestellt (gibt es keine User-Name-/Passwortabfrage mehr), so kann – wie bei deklarativer Sicherheit mit Realms – der SubjectDN den User-Namen ersetzen. Zusätzlich kann die SerialNumber als Passwort benutzt werden, um Probleme mit erneuerten und somit doppelt ausgestellten Zertifikaten für die gleiche Person zu umgehen. Die Datenstrukturen selbst und die Prüfung der Zugriffsrechte bleiben von dieser Änderung unberührt. Bei neuen Projekten sollte analog vorgegangen werden. Integriert werden kann die programmatische Zertifikatsprüfung an einer zentralen Stelle der Applikation. Dazu bieten sich verschiedene Möglichkeiten an:
  • Valve bzw. Filter: Für Code, der jeden Request behandelt, bietet Tomcat schon lange das Valve-Konzept. Ein Beispiel für eine spezielle SSLCertAccessValve wird im nächsten Abschnitt entwickelt. Ein Filter lässt sich analog implementieren.
  • Ersetzen der User-Name/Passwort-Prüfung: Wurde in einer bestehenden Anwendung eine programmatische Login-Prozedur implementiert, so kann der Code an genau dieser Stelle ausgetauscht werden.
  • zentrale Codestelle: Existieren Codestellen, die von jedem Request durchlaufen werden (wie etwa ein RequestProcessor im Struts-Framework), so kann der Zertifikatstest hier eingebaut werden.
  • Ein manueller Aufruf entsprechender Prüfroutinen aus verschiedenen Stellen der Anwendung heraus ist zwar möglich, aber nicht empfehlenswert.
Vorteile einer Valve sind die Wiederverwendbarkeit dieser Struktur in anderen Anwendungen wie Web Services und die Gewissheit, dass wirklich jeder einzelne Request geprüft wird – in extrem sicherheitskritischen Bereichen kann eine derart paranoide Prüfung sinnvoll und wichtig sein. Nachteile sind die etwas schlechtere Performance aufgrund der häufigen Prüfung des Zertifikats und der nötige Eintrag in der Konfiguration des Servers.

Grenzstation – eine AccessValve

Um eigene Valves komfortabel zu implementieren, bietet Tomcat mit org.apache.catalina.valves.ValveBase eine abstrakte Klasse, in der nur noch die Methode in voke(…) überschrieben werden muss:
package com.tembit.example;
//import declarations
public class SSLCertAccessValve extends org.apache.catalina.valves.ValveBase {
void invoke(Request request, Response response)
throws java.io.IOException, javax.servlet.ServletException {
//check certificate here
}
}
Eingebunden wird die Valve durch einen Eintrag im Engine-, Host- oder Context-Abschnitt der Konfigurationsdatei server.xml : <Valve className=  com.tembit.example.SSLCertAccessValve  /> . Innerhalb dieser Valve kann entweder auch gleich die Rechteprüfung erfolgen oder aber für Legacy-Anwendungen mit bestehender Rechtestruktur ein entsprechender Parameter wie die User-ID im Request gesetzt werden, um bestehende Strukturen weiter zu nutzen.

Web Services

Zum Schluss noch ein kurzer Exkurs zu Web Services mit Apache Axis. Serverseitig ist die Integration des Zertifikatcodes anhand von AccessValve ideal, da an bestehenden Web Services kein Code verändert bzw. in neuen Projekten kein Code eingefügt werden muss.
Development CA mit OpenSSL Zum Entwickeln und Testen einer Smartcard-Anwendung kann die dafür benötigte CA durch das bei OpenSSL mitgelieferte Skript CA.pl simuliert werden.
Die OpenSSL-Pakete für Linux/Unix finden sich unter www.openssl.org, für Windows empfiehlt sich die Binärdistribution von www.slproweb.com. Initialisierung und CA-Zertifikat Zur Erzeugung wird CA.pl -newca aufgerufen. Das Skript fragt die nötigen Daten ab und erzeugt ein selbst signiertes CA-Zertifikat. Server-Zertifikat für Tomcat Zunächst wird ein Keystore für den Server erzeugt, wobei zu beachten ist, dass der Common Name (CN) des Serverzertifikats auf den Servernamen (z.B. www.example.com ) ausgestellt wird:
keytool -genkey -keystore tomcat.keystore -alias mykey -storepass 
  change_me -keyalg RSA
Damit Tomcat Client-Zertifikate validieren kann, muss der auch die TrustStore-Rolle übernehmen, dafür wird das CA-Zertifikat binär kodiert
openssl x509 -in demoCAcacert.pem -outform DER -out CA-cert.cer
und dann in den Keystore importiert:
keytool -keystore tomcat.keystore -storepass change_me -alias 
ca -import -file CA-cert.cer
Anschließend wird ein Certificate Signing Request (CSR) erzeugt, von der CA signiert und wieder in den Keystore importiert:
keytool -certreq -keystore tomcat.keystore -storepass change_me -keyalg 
  RSA -alias mykey -file newreq.pem
CA.pl -signreq
openssl x509 -in newcert.pem -outform DER -out newcert.cer
keytool -keystore tomcat.keystore -storepass change_me -alias 
  mykey -import -file newcert.cer
Nutzerzertifikate Nutzerzertifikate für Browser und Smartcards werden dann mit dem CA-Skript erzeugt und im PKCS#12- Format exportiert:
CA.pl -newreq
CA.pl -signreq
CA.pl -pkcs12 
Auf der Client-Seite wird ein Java-Keystore generiert (analog zum Tomcat-Server-Keystore, siehe Kasten). Werden in der WSDL die entsprechenden URLs so geändert, dass sie das HTTPS-Protokoll benutzen und dann die Stubs daraus generiert, so kann Axis Client-Zertifikate automatisch benutzen. Lediglich die Dateinamen und Passwörter des Keystore für den eigenen Schlüssel und des Trust-store für das Server- bzw. CA-Zertifikat muss in diesen System.properties angegeben werden:
javax.net.ssl.keyStore
javax.net.ssl.keyStorePassword
javax.net.ssl.trustStore
javax.net.ssl.trustStorePassword
Alles Weitere erledigt Axis automatisch. Für Web-Services-Frameworks, die SSL nicht unterstützen, gibt es als Workaround die Möglichkeit, stunnel auf der Client-Maschine einzusetzen und in der web Services Description Language die URLs der Services auf <a href=“http://localhost:/“ class=“elf-external elf-icon“ rel=“nofollow“>http://localhost:/… zu ändern. In der Konfiguration des stunnel werden dann der Server und das Zertifikat eingetragen. Die Verwendung von Chipkarten ist in diesem Falle nicht ganz so einfach. Diese als Zertifikatsquelle nutzen kann ebenfalls stunnel (mit einigen Erweiterungen). Alternativ wäre dann eine Integration in die Client-Stubs mithilfe eines entsprechenden Karten-Framework in Java zu nennen. Eine Lösung für dieses Szenario würde aber den Rahmen dieses Artikels sprengen.

Produktives System

Sowohl aus Performance- als auch aus Sicherheitsgründen empfiehlt es sich für den produktiven Einsatz, einen umfassend auditierten und erprobten Webserver wie z.B. Apache2 mit mod_ssl als Frontend vor dem Application Server zu betreiben. Die beiden Server werden dann über das Protokoll AJP verbunden. Das dazu eingesetzte mod_jk muss die Zertifikatsdaten von mod_ssl an den Application Server weiterreichen; das neuere mod_jk2 verursacht an dieser Stelle noch Probleme. CA-Zertifikat und Server-Zertifikat werden Apache-typisch an den entsprechenden Stellen installiert. Für den Export der Daten und das Abfragen des Client-Zertifikats werden in der Datei httpd.conf (oder je nach Distribution auch ssl.conf ) des Apache diese Zeilen eingefügt:
SSLOptions +ExportCertData +StdEnvVars +CompatEnvVars
SSLVerifyClient require
Im Abschnitt zur Konfiguration des mod_jk sind diese Optionen wichtig:
JkExtractSSL On
JkHTTPSIndicator HTTPS
JkSESSIONIndicator SSL_SESSION_ID
JkCIPHERIndicator SSL_CIPHER
JkCERTSIndicator SSL_CLIENT_CERT
Diese Konfiguration sorgt dafür, dass Zertifikatsinformationen transparent an den Application Server durchgereicht werden und der Anwendung ohne Unterschiede zu obigem Beispielcode zur Verfügung stehen.

Fazit

Eine Smartcard-Lösung zur Authentifizierung ist nicht nur sehr sicher, sondern auch besonders eingängig für die Nutzer. In Situationen, in denen ein solcher Ansatz Sinn macht, muss eigentlich nur ein Betreiber für die CA gefunden werden – entweder firmenintern oder als externer Dienstleister. Die eigentliche Implementierung in der Anwendung und dem Application Server ist – wie gezeigt – recht einfach; viele Vorurteile und Befürchtungen sind nicht berechtigt. Es wäre wünschenswert, dass mehr Entwickler diese Technologien in ihren Projekten anbieten und einsetzen.
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -