Ausgefeilte Sicherheitsmechanismen sind bei jeder Kommunikationstechnik von großer Bedeutung. Absender und Empfänger von Nachrichten verlassen sich auf die Sicherheit der Übertragung. Bei Web Services gilt das ganz besonders, da diese Technik ihre Stärke gerade im unternehmensübergreifenden Bereich besitzt. Dort soll oft aus Kosten- und Flexibilitätsgründen das öffentliche Internet und damit ein unsicheres Medium genutzt werden. Der folgende Artikel beschreibt Apache WSS4J, eine freie Implementierung der OASIS Web Services Security v1.0 Spezifikation.
Ein verbreiteter Ansatz zum Schutz von Kommunikationsbeziehungen stellt die Verschlüsselung auf der Ebene des Transferprotokolls dar. Dabei hat sich HTTPS etabliert, das im Zusammenhang mit Web Services allerdings nicht ausreichend ist. Bei HTTPS handelt es sich um eine Punkt-zu-Punkt-Verschlüsselung. Deshalb können die übermittelten Nachrichten nicht durch andere Beteiligte verarbeitet, ergänzt oder modifiziert werden. Darüber hinaus bleibt die Verschlüsselung auf ganz oder gar nicht beschränkt. Das Verschlüsseln der Daten für unterschiedliche Empfänger und die digitale Signatur als verbindlicher Nachweis von Ursprung und Authentizität sind kein Bestandteil des Protokolls.
Abhilfe für diese Probleme bietet der alternative oder ergänzende Einsatz von Verfahren auf der Datenebene. Diese Mechanismen wirken Ende-zu-Ende und ermöglichen neben der Verschlüsselung vor allem die Authentifizierung von Absender und Empfänger und die digitale Signatur. Als Standard für den Einsatz solcher kryptografischer Verfahren hat sich hierfür WS-Security [1] etabliert. Mit WSS4J [2] aus dem Apache-Axis-Projekt steht eine interessante Implementierung zur Verfügung, die hier am Beispiel eines sehr einfachen Services demonstriert werden soll. Bei WSS4J handelt es sich um ein relativ junges Projekt, das bisher noch kein Bestandteil der normalen Axis-Distribution [3] ist. Ein großes Interesse an dieser Implementierung ist aber bereits erkennbar.
Beispielanwendung
Der zu sichernde Service stellt eine einfache Methode public String echo(String name) zur Verfügung, die lediglich den übergebenen String erweitert und wieder zurückliefert. Das ZIP-Archiv dieser Demo ist auf der Homepage des Magazins erhältlich und enthält unter anderem auch ein Ant-Skript, das den normalen Axis-Deployment-Prozess automatisiert. Bei der Absicherung mit WS-Security wird der Web Service Deployment Descriptor um weitere sicherheitsrelevante Einstellungen erweitert und dem JAR werden noch ein paar Dateien, wie z.B. der Keystore, hinzugefügt. Der Deployment Descriptor wurde ursprünglich von WSDL2Java generiert und dürfte ohne die WSS4J-spezifischen Erweiterungen bekannt sein.
Um WSS4J nutzen zu können, wird der Handler-Mechanismus von Axis benutzt. Einen Handler kann man als Filter in den Eingangs- oder Ausgangsdatenstrom einhängen. Bei WSS4J sind das die beiden Handler-Klassen WSDoAllSender und WSDoAllReceiver. Der Receiver wird immer in den Eingangsdatenstrom, der Sender in den Ausgangsdatenstrom eingehängt. Wie Abbildung 1 zeigt ist das beim Service eingehend der RequestFlow und ausgehend der ResponseFlow, beim Client ist es genau umgekehrt, eingehend der ResponseFlow und ausgehend der RequestFlow.

Abb. 1: RequestFlow und ResponseFlow bei Client und Service
Die Konfiguration geschieht auf der Service-Seite über den Web Service Deployment Descriptor und auf der Client-Seite per XML-Konfigurationsdatei. Über die Java-Umgebungsvariable axis.ClientConfigFile wird der Axis Runtime der Pfad der Konfigurationsdatei mitgeteilt. Im Beispiel sind das die Dateien service_deploy.wsdd auf der Service-Seite und die client_deploy.wsdd auf der Client-Seite.
Bei der Konfiguration muss beachtet werden, wie die Wirkung der einzelnen Datenströme sein soll. Für einen ausgehenden Datenstrom wird die Klasse WSDoAllSender als Handler eingebunden, für einen eingehenden Datenstrom die Klasse WSDoAllReceiver. Für beide Klassen gibt es Konfigurationsparameter, mit denen das Verhalten gesteuert werden kann. Für einen Service sieht das dann wie im folgenden Beispiel dargestellt aus. Bei einem Client muss analog die Zuordnung von WSDoAllReceiver zum ResponseFlow und von WSDoAllSender zum RequestFlow erfolgen.
<requestFlow>
<handler type="soapmonitor" />
<handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
</handler>
</requestFlow>
<responseFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllSender">
</handler>
<handler type=soapmonitor />
</responseFlow>
Der hier im Beispiel eingesetzte SOAPMonitor ist Teil der Axis-Distribution und wird als Hilfsmittel zur Visualisierung der ausgetauschten XML-Dokumente eingesetzt. Er muss zunächst, wie in der Axis-Dokumentation beschrieben, kompiliert und konfiguriert werden. Über einen Link auf der Axis-Startseite gelangt man dann zum Monitor und kann sich die eingehenden und ausgehenden XML-Dokumente anschauen.
Kryptografie bei Web Services
Die Ergänzungen hinsichtlich der Sicherheit bei der Datenübertragung sind, wie bei J2EE gewohnt, hauptsächlich über einen Deployment Descriptor konfigurierbar. Darüber hinaus kann bei Bedarf aber natürlich auch programmatisch auf die Sicherheitsmechanismen zugegriffen werden. Definiert werden muss unter anderem, wo der Keystore mit den verwendeten Schlüsseln zu finden ist und welche kryptografischen Aktionen für die eingehenden und ausgehenden Datenströme in welcher Reihenfolge durchgeführt werden sollen.
Wie bei asymmetrischen Verfahren üblich, müssen Sender und Empfänger jeweils ein Schlüsselpaar, bestehend aus einem öffentlichen und einem privaten Schlüssel, besitzen. Beide Kommunikationspartner müssen die öffentlichen Schlüssel des jeweils anderen kennen und als vertrauenswürdig einstufen. Bei WS-Security bedeutet das, dass entweder (wie hier im Beispiel) die vertrauenswürdigen öffentlichen Schlüssel im Voraus ausgetauscht werden oder dass man vertrauenswürdige Mechanismen für den Schlüsselaustausch und die entsprechende Zertifizierung von geprüften Schlüsseln einsetzen muss.
Digitale Signatur mit WSS4J
Die digitale Signatur bescheinigt, dass die übertragenen Daten unverändert angekommen sind und dass sie dem Unterzeichner in dieser Form bekannt sind. Eine solche Unterschrift kann sich beispielsweise auf einen Bestellauftrag samt Bestellmenge, Bestelldatum und Rechnungsanschrift beziehen und dadurch die Rechtsverbindlichkeit dokumentieren.
Der Absender signiert eine Nachricht mit seinem eigenen privaten Schlüssel. Der Empfänger kann die Signatur mithilfe des öffentlichen Schlüssels des Absenders überprüfen. Dazu muss er auf Basis der Informationen innerhalb der übermittelten Nachricht den öffentlichen Schlüssel des Absenders ermitteln können. In der Regel sendet der Absender diesen Schlüssel in Form eines Zertifikats in der Nachricht bereits mit, sodass der Empfänger nur noch dessen Glaubwürdigkeit überprüfen muss.
Die Konfigurationsparameter für die digitale Signatur sind in Tabelle 1 zusammengefasst. Sollen mehrere Aktionen kombiniert eingesetzt werden, so trägt man diese durch Leerzeichen getrennt ein. Die Properties-Datei enthält unter anderem den Namen des verwendeten Keystores und den Klassennamen des Crypto-Providers. Auf der ausgehenden Seite wird mit user definiert, welcher private Schlüssel aus dem Keystore für die Signatur verwendet werden soll. Die passwordCallbackClass gibt schließlich noch eine Klasse an, die das Passwort des privaten Schlüssels liefert, sodass dieses nicht im Klartext in der Konfiguration hinterlegt werden muss. Sie implementiert das Interface javax.security.auth.callback.Callback aus der Java Authentication and Authorization Specification (JAAS). Diese Klasse kann das Passwort beispielsweise über einen Dialog vom Benutzer abfragen oder aus einer separaten Konfigurationsdatei lesen. In der Demo beinhaltet sie das Passwort der Einfachheit halber als einfacher String.
Die Konfiguration auf der Service-Seite sieht etwa wie in Listing 1 dargestellt aus. Wenn man sowohl den Request als auch den Response zwischen Client und Server digital signieren möchte, dann muss man den WSDoAllReceiver beim Service im RequestFlow und beim Client im ResponseFlow definieren. Der WSDoAllSender wird analog beim Service in den ResponseFlow und beim Client in den RequestFlow eingehängt.
Verschlüsselung mit WSS4J
Bei WS-Security wird in der Regel die Kombination aus symmetrischen und asymmetrischen Verschlüsselungsverfahren eingesetzt. Die eigentliche Verschlüsselung der Daten erfolgt mit einem temporären symmetrischen Session-Key. Dieser wird dann wiederum mit dem öffentlichen, asymmetrischen Schlüssel des Empfängers verschlüsselt und der Nachricht beigefügt (wrapped key). Dieses Vorgehen hat neben dem Performance-Vorteil auch noch den Effekt, dass man den Session-Key auch mehrfach, für unterschiedliche Empfänger verschlüsselt, beifügen kann. Eine Nachricht lässt sich also so verschlüsseln, dass sie sich von unterschiedlichen Empfängern entschlüsseln lässt.
Wie in Tabelle 2 dargestellt, muss bei der Verschlüsselung auf der ausgehenden Seite angegeben werden, mit welchem öffentlichen Schlüssel die Verschlüsselung erfolgen soll. Dies kann statisch anhand der ID im Keystore erfolgen, wie das im Beispiel auf der Client-Seite erfolgt ist. Auf der Server-Seite ist es jedoch bei einem kombinierten Einsatz von Verschlüsselung und digitaler Signatur eleganter, den öffentlichen Schlüssel des Absenders direkt zu verwenden. Dieser ist von der digitalen Signatur her bereits bekannt und kann über das spezielle Schlüsselwort useReqSigCert konfiguriert werden. Die Callback-Klasse liefert bei der Verschlüsselung das Passwort des privaten Schlüssels, mit dem die Daten auf der eingehenden Seite entschlüsselt werden.
Die Konfiguration sieht auf der Service-Seite etwa wie in Listing 2 dargestellt aus. Ein vorhandenes signaturePropFile wird bei kombiniertem Einsatz von Signatur und Verschlüsselung für die Verschlüsselung wieder verwendet, sodass kein zusätzlicher Eintrag für ein encryptionPropFile und ein decryptionPropFile erforderlich ist. Bei einem Service werden der Abschnitt mit dem WSDoAllReceiver in den RequestFlow und der Abschnitt mit dem WSDoAllSender in den ResponseFlow eingehängt. Bei einem Client verhält es sich entsprechend umgekehrt.
Fazit und Ausblick
Der Einsatz von WS-Security bietet umfangreiche Möglichkeiten der Absicherung einer Web-Service-Kommunikation. Neben den hier betrachteten Mechanismen zur Verschlüsselung und zur digitalen Signatur sind unter anderem auch die Authentifizierung, die Berechtigungsweitergabe und der Schlüsselaustausch auf Basis von XML möglich.
Die Standardisierung dieser Sicherheitsmechanismen ermöglicht den plattformübergreifenden Einsatz und trägt zur weiteren Verbreitung von Web Services bei. Die Interoperabilität verschiedener WS-Security-Implementierungen ist natürlich der entscheidende Punkt. Im Rahmen diverser Interoperabilitäts-Initiativen von OASIS (Organization for the Advancement of Structured Information Standards) [4] oder WS-I (Web Services Interoperability Organization) [5] wird versucht sicherzustellen, dass auch unterschiedliche Implementierungen und Plattformen zusammenarbeiten. Diverse Tests mit unterschiedlichen Konstellationen stimmen bisher optimistisch. So wurde auf dem Gartner Application Integration and Web Services Summit in Los Angeles im April 2005 die Interoperabilität von 14 unterschiedlichen Implementierungen erfolgreich demonstriert.
Weitere Informationen über WSS4J findet man auf der Homepage und im Wiki [6]. Darüber hinaus liefert die API-Dokumentation und die Mailing-Liste des Projekts weitere nützliche Hinweise. Den noch recht jungen Stand des Projekts merkt man bei der Dokumentation zurzeit allerdings noch recht deutlich. Hoffentlich bekommt diese gelungene Implementierung von WS-Security mit der angestrebten stärkeren Integration in Axis 2.0 noch zusätzlichen Auftrieb.
Listing 1: Beispiel einer Signature-Konfiguration <deployment . . . >
<service . . . >
<RequestFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
<parameter name="action" value="signature" />
<parameter name="signaturePropFile" value="crypto.properties" />
</handler>
</RequestFlow>
<ResponseFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllSender">
<parameter name="action" value="signature" />
<parameter name="signaturePropFile" value="crypto.properties" />
<parameter name="user" value="13cd3ab6-b542-479a-ac5f-2d874f63883e" />
<parameter name="passwordCallbackClass" value="simple.demo.server.PWCallback" />
</handler>
</ResponseFlow>
</service>
</deployment>
Listing 2: Beispiel einer Encryption-Konfiguration <deployment . . . >
<service . . . >
<RequestFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
<parameter name="action" value="signature encrypt" />
<parameter name="signaturePropFile" value="crypto.properties" />
<parameter name="passwordCallbackClass" value="simple.demo.server.PWCallback" />
</handler>
</RequestFlow>
<ResponseFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllSender">
<parameter name="action" value="signature encrypt" />
<parameter name="signaturePropFile" value="crypto.properties" />
<parameter name="user" value="13cd3ab6-b542-479a-ac5f-2d874f63883e" />
<parameter name="passwordCallbackClass" value="simple.demo.server.PWCallback" />
<parameter name="encryptionUser" value="useReqSigCert" />
</handler>
</ResponseFlow>
</service>
</deployment>
Andy Wolf ist als Consultant bei Unilog Avinci in unterschiedlichen Beratungs- und Implementierungsprojekten tätig. Er beschäftigt sich seit Jahren mit Java und XML und arbeitet schwerpunktmäßig an den Themen Web Services und serviceorientierte Architektur. Links & Literatur