Ein grundsätzliches Ziel der Entwickler von Java war die sichere Ausführbarkeit von Java-Applets. Dazu werden die Rechte, die ein Applet bei der Ausführung erhält, streng limitiert. Java verwendet dazu das Konzept der Sandbox. Eine Sandbox ist eine gesicherte Umgebung, in der ein Applet läuft, und die unter anderem verbietet, auf die Festplatte des Benutzers zuzugreifen oder zusätzliche Übertragungskanäle eines Netzwerks zu öffnen und spezifische Benutzerinformation preiszugeben. Abb. 1.3: Sandbox-Modell in JDK 1.0. Im Gegensatz dazu wird aber lokaler Code (Application) als vertrauenswürdig betrachtet und darf daher auf alle Systemressourcen zugreifen. Man erkennt an dieser Stelle erneut den wichtigen Unterschied zwischen Application und Applet. Die Entscheidung, welche Art von Code auf welche Daten zugreifen darf, wird hierbei von einem Security-Manager getroffen. Das Sandbox-Modell ist in Abb. 1-3 dargestellt. Systemsicherheit Besondere Beachtung schenkten die Entwickler von Java der möglichen Gefährdung des Betriebssystems, insbesondere der Reglementierung des zur Verfügung stehenden Arbeitsspeichers. Zum einen ist ein direkter Speicherzugriff unter Java nicht möglich, da die Verwendung von Zeigern (Pointern) nicht möglich ist, zum anderen benutzt Java keine Speicherbereiche, die vom Betriebssystem oder von anderen wichtigen Systemressourcen genutzt werden. Sicherheit in JDK 1.1 Es zeigte sich schnell, dass die Forderungen der Sandbox in JDK 1.0 zu restriktiv sind, da Applets keinerlei Zugriffe auf Systemressourcen erhalten können, selbst wenn dies gewünscht wird. Aus diesem Grund wurde in JDK 1.1 das Konzept des signed Java-Applets eingeführt. Dieses hat Zugriff auf alle Systemressourcen und arbeitet außerhalb der Sandbox, wenn der öffentliche Schlüssel, der zur Verifikation der Signatur verwendet wird, vertrauenswürdig ist (zur Theorie der Verschlüsselung siehe [FSBS98]). Meist sind diese Applets solche, die von einem Unternehmen entwickelt und über ein Intranet verwendet werden oder solche, die ein Autor digital signiert, bevor sie über ein Netzwerk übertragen werden.
Abb. 1.4: Sicherheitsmodell in JDK 1.1. Signierte Applets werden meist mit der jeweiligen Signatur in signierten Java-ARchives (JARs) übertragen (zu JAR siehe Kapitel 4.3). Signed Applets verhalten sich wie reguläre Programme, was insbesondere bedeutet, dass für die Ausführung keinerlei Sicherheitsgarantien gegeben werden können, da sämtliche Einschränkungen, die die Sandbox anbietet, außer Kraft gesetzt sind. Unsignierte Applets werden aber weiterhin in der Sandbox ausgeführt. Das in JDK 1.1 verfolgte Konzept ist in Abb. 1-4 dargestellt. Das Java Security Application Programming Interface (API) wurde erstmals in JDK 1.1 zur Verfügung gestellt. Es enthält eine grundlegende Kryptographiefunktionalität, einschließlich der APIs für digitale Signaturen und Hash-Funktionen sowie abstrakten Schnittstellen zur Schlüssel- und zur Zertifikatsverwaltung. Eine einführende Darstellung der theoretischen Grundlagen der Sicherheit findet sich bspw. in [FSBS98]. Es sollte deutlich betont werden, dass die Arbeit am Java-Security-API in JDK 1.1 längst noch nicht abgeschlossen ist und sich in zukünftigen Versionen daher grundlegende Änderungen ergeben können. Es ist derzeit in JDK 1.1 noch nicht möglich, öffentliche oder private Schlüssel zu importieren oder diese zu exportieren, sowie digitale Signaturen zu überprüfen. Sicherheit in JDK 1.2 In JDK 1.2 wurde das Sicherheitskonzept von Java weiter verbessert. Obwohl die Einführung von signed Applets in JDK 1.1 einen wesentlichen Fortschritt darstellte, beinhaltet sie doch einen Nachteil: Wenn ein Applet vertrauenswürdig ist, kann dieses gleichzeitig auf alle Systemressourcen zugreifen. Eine Möglichkeit, ein Applet gesichert in der Sandbox auszuführen und diesem gleichzeitig zu vertrauen, existierte bis zur Entwicklung von JDK 1.2 nicht. Abb. 1.5: Sicherheitsmodell in JDK 1.2 Eine wesentliche Neuerung von JDK 1.2 ist daher die Einführung von Security Policies, die die Berechtigungen angeben, die Code von verschiedenen Entwicklern eingeräumt werden. Diese Rechte können einerseits von Benutzern, aber auch von Systemadministratoren gesetzt werden und spezifizieren den Zugriff auf genau eine Ressource (bspw. Lese- oder Schreibzugriffe auf Verzeichnisse). Um die Vergabe von Rechten effizient handhaben zu können, wird der Code in JDK 1.2 in individuelle Bereiche (sog. Domains) eingeteilt, wobei jede Domäne eine Menge von Klassen enthält, denen dieselben Rechte zugeteilt werden. Das Sicherheitsmodell in JDK 1.2 ist derart eine Obermenge der früheren Java-Versionen, da eine der Domänen problemlos so eingerichtet werden kann, dass sie wie die Sandbox in JDK 1.0 oder JDK 1.1 funktioniert. Wie vorher auch sind Applications auch in JDK 1.2 keinerlei Einschränkungen unterworfen. Die Sicherheitsarchitektur von JDK 1.2 ist in Abb. 1-5 abgebildet. In der Abbildung sind vier Domänen zu sehen, von denen zwei die bereits aus JDK 1.1 bekannten Konzepte des vollständigen Zugriffs und der Sandbox darstellen. Um die sicherheitsrelevanten Erweiterungen in JDK 1.2 verstehen zu können, werden zunächst die Grundlagen der Verschlüsselung von Datenübertragungen erläutert. Eine ausführliche Darstellung findet sich bspw. in [FSBS98]. Verschlüsselungsverfahren Die Entwicklung von Verschlüsselungsverfahren, die keinen sicheren Kanal zur Übertragung des Schlüssels benötigen, wurde Mitte der 70er Jahre begonnen. Zusammengefasst werden diese Methoden unter dem Begriff der asymmetrischen Verschlüsselung oder Public-Key-Verfahren. Im Gegensatz zur symmetrischen Verschlüsselung basiert die Idee der asymmetrischen Verschlüsselung auf der Erkenntnis, dass man Schlüsselpaare verwenden kann, die sich nicht voneinander ableiten lassen. Ein Teilnehmer hat demzufolge einen öffentlichen und einen privaten Schlüssel. Der private Schlüssel wird geheimgehalten, während der öffentliche Schlüssel jedem bekannt sein kann. Die Berechnung der Schlüssel gewährleistet dabei, dass die beiden Schlüssel eindeutig voneinander abhängig sind, das heißt, zu jedem öffentlichen Schlüssel existiert ein privater Schlüssel. Es ist aber bei geeigneter Schlüssellänge mit vertretbarem Aufwand nicht möglich, einen Schlüssel mit Hilfe des anderen zu berechnen. Eine Nachricht, die mit dem öffentlichen Schlüssel verschlüsselt wurde, kann nur mit dem dazugehörenden privaten Schlüssel wieder entschlüsselt werden. (Die Bedeutung liegt dabei auf „nur".) Mit dem öffentlichen Schlüssel kann die Nachricht nicht wieder entschlüsselt werden; nur mit dem dazugehörigen privaten Schlüssel kann die Nachricht wieder in Klartext umgewandelt werden. Durch diese Eigenschaft kann eine vertrauliche Datenübermittlung folgendermaßen ablaufen: Beide Parteien erzeugen jeweils einen öffentlichen und einen privaten Schlüssel. A sendet seinen öffentlichen Schlüssel an B. B verwendet diesen Schlüssel zur Chiffrierung der Nachricht und sendet die verschlüsselte Nachricht an A. A entschlüsselt die Nachricht mit seinem privaten Schlüssel. B behält den öffentlichen Schlüssel von A für weitere Nachrichten. Will A eine Nachricht an B senden, so benötigt er analog zuerst dessen öffentlichen Schlüssel. Eine Nachricht, die mit dem privaten Schlüssel verschlüsselt wurde, kann nur mit dem dazugehörenden öffentlichen Schlüssel wieder entschlüsselt werden.Vertraulichkeit der übertragenen Daten kann mit dieser Eigenschaft nicht erreicht werden. Da der öffentliche Schlüssel jedermann bekannt sein kann, kann auch jeder, der Zugriff auf Daten hat, die mit dem privaten Schlüssel verschlüsselt wurden, diese Daten entschlüsseln. Ausnutzen kann man hingegen wieder die Eigenschaft, dass es nur einen eindeutigen Schlüssel, nämlich den dazugehörenden öffentlichen gibt, mit dem die Daten wieder entschlüsselt werden können. Erhält man eine Nachricht, die man mit dem öffentlichen Schlüssel von A entschlüsseln kann, weiß man sicher, dass nur A diese Nachricht versendet hat, denn nur er hat den privaten Schlüssel. Gewährleistet werden kann durch dieses Verfahren also die Authentizität der Nachricht. Niemand anders als A kann eine Nachricht so verschlüsseln, dass man sie mit dem öffentlichen Schlüssel von A wieder entschlüsseln kann. Das Verschlüsseln einer Nachricht mit dem privaten Schlüssel wird auch als digitale Signatur einer Nachricht bezeichnet. Die Kombination beider Eigenschaften kann also verwendet werden, um die Daten sicher zu übertragen und gleichzeitig den Absender zweifelsfrei festzustellen. Analog zur Unterzeichnung von Nachrichten können zur Garantie der Echtheit auch öffentliche Schlüssel unterzeichnet werden. Sind an der Kommunikation allerdings nur zwei Parteien beteiligt, ergibt sich ein Problem: A signiert seinen öffentlichen Schlüssel mit seinem privaten Schlüssel und versendet ihn. Um diesen Schlüssel auf Echtheit zu prüfen, muss man den öffentlichen Schlüssel allerdings schon haben. Es muss daher zumindest zur ersten Prüfung eines Schlüssels eine weitere Instanz hinzugezogen werden. Hierzu dienen Zertifizierungsinstanzen. Für jeden bei einer zentralen Instanz registrierten Benutzer wird jeweils ein öffentliches und privates Schlüsselpaar erzeugt. Der private Schlüssel muss hier allerdings persönlich vom Benutzer abgeholt werden. Der öffentliche Schlüssel wird mit Informationen versehen, die den Benutzer eindeutig identifizieren und von einer Zertifizierungsinstanz mit deren öffentlichem Schlüssel unterschrieben (Zertifikat). Daher wird diese Instanz auch Certification Authority (CA) genannt. Der öffentliche Schlüssel der CA wird an alle registrierten Benutzer verteilt. Die CA stellt damit für alle Benutzer eine vertrauenswürdige Instanz dar. Erhält man einen öffentlichen Schlüssel, der von der CA unterschrieben wurde, so kann man sicher sein, dass die betreffende Person sich vor der CA ausgewiesen hat und der Schlüssel echt ist. Vertraut man also der CA, so kann man auch Schlüsseln trauen, bei denen man durch Prüfung mit dem öffentlichen Schlüssel der CA feststellen kann, dass sie von ihr signiert wurden. Zudem muss der Schlüssel für einen ersten Nachrichtenaustausch nicht zwischen den Partnern versendet werden. Vielmehr kann ein Sender sich den benötigten öffentlichen Schlüssel des Partners bei der CA besorgen. Zusätzlich kann die CA weitere Aufgaben übernehmen. Eine dieser Aufgaben ist es z. B., alle Benutzer darüber zu informieren, dass ein öffentlicher Schlüssel nicht mehr gültig ist. Dieser Fall kann eintreten, wenn der private Schlüssel eines Benutzers in fremde Hände fällt. Wird dies der CA gemeldet, so trägt sie den ungültigen Schlüssel in eine Certification Revocation List (CRL) ein, erzeugt für den Benutzer ein neues Schlüsselpaar und erklärt den alten öffentlichen Schlüssel für ungültig. Verschlüsselung in JDK 1.2 Bereits JDK 1.1 enthält eine Schnittstelle zu Sicherheitsfunktionen, die sog. Java Cryptography Architecture (JCA), mittels derer die Verwendung und Weiterentwicklung einer kryptographischen Funktionalität in Java ermöglicht wird. In seiner ursprünglichen Form beinhaltet die JCA eine Architektur, die mehrfache und interoperable Implementierungen derselben Funktionen ermöglicht. Erscheint es also vorteilhaft, ein Verschlüsselungsverfahren neu zu implementieren, so ist dies – auch unter Rückgriff auf bereits existierende andere Module – leicht möglich. In diesem Zusammenhang spielt der Begriff des Cryptographic Service Provider (CSP) eine große Rolle, der ein Java-Paket bezeichnet, das die konkrete Implementierung einer Untermenge der kryptographischen Aspekte des JDK-Security-API unterstützt. Abb. 1.6: JCA-Module in JDK 1.2. In JDK 1.1 kann ein derartiger Provider bspw. eine Implementierung einer oder auch mehrerer digitaler Signaturalgorithmen enthalten. JDK 1.2 ergänzt dieses Modell um die folgenden Dienste: Erzeugung und Management einer Schlüsselverwaltung (Keystore), Generierung und Verwaltung der Parameter, die die Algorithmen benötigen, Unterstützung von Konvertierungsroutinen zwischen verschiedenen Schlüsseldarstellungen, Verwaltung von Zertifikaten (Generierung von Zertifikaten und von Zertifikats-Widerrufslisten (Certificate Revocation Lists, CRLs) und Zufallszahlengenerator zur Erzeugung von Schlüsseln. Die in JDK 1.2 angebotenen Kryptographie-Erweiterungen (Java Cryptography Extensions, JCE) ergänzen das JDK um Anwendungsschnittstellen (APIs) zur Verschlüsselung, zum Schlüsselaustausch und zur Nachrichtenauthentifizierung (Message Authentication Code, MAC). Betrachtet man die Gesamtheit der JCE und der kryptographischen Aspekte des JDK, so hat der Anwender eine vollständige, plattformunabhängige Kryptographie-Schnittstelle zur Verfügung. Eine Besonderheit der JCE liegt darin, dass sie nicht Bestandteil des eigentlichen JDK ist. Kryptographische Verfahren unterliegen in den USA einer strengen Exportkontrolle, weshalb die JCE unabhängig vom JDK als Erweiterung angeboten werden. In Abb. 1-6 ist der Aufbau der JCA dargestellt. Das Service Provider Interface (SPI) repräsentiert die Methoden, die von kryptographischen Dienstanbietern (Cryptographic Service Providers, CSPs) implementiert werden müssen. Zur Realisierung des SPI wurde JDK 1.2 um sog. Engine Classes erweitert, die einen kryptographischen Dienst in einer abstrakten Art und Weise definieren. Dazu werden bspw. die Methoden des APIs definiert, die es den Anwendungen erlauben, auf spezielle Typen kryptographischer Dienste zuzugreifen. Als abstrakte Notation existiert hierzu jedoch keine algorithmische Implementierung; diese muss vom Provider dann generiert werden, wenn der Dienst tatsächlich angeboten werden soll. Man kann daher die abstrakte Notation durchaus mit einem Katalog vergleichen, der verschiedene Varianten und Details eines Produkts enthält. Bestellt nun ein Kunde dieses Produkt in einer speziellen Ausführung, so wird dieses aus der abstrakten Notation der Katalogsbeschreibung in ein konkretes Produkt der realen Welt überführt. Die Anwendungsschnittstellen, die durch die Engine Class angeboten werden, werden als Service Provider Interface (SPI) implementiert. Dies impliziert, dass für jede Engine Class eine dazugehörige abstrakte SPI-Klasse existieren muss, die die SPI-Methoden definiert, die ein Provider implementieren muss. Möchte bspw. ein Anwender eine Instanz der Signature Engine Class verwenden, um auf die Funktionalität zuzugreifen, die zur digitalen Signatur einer Datei notwendig ist, so müsste eine tatsächliche Implementierung einer Signatur-SPI-Subklasse vorliegen, die einen speziellen Signaturalgorithmus realisiert (bspw. SHA1 in DSA). SUN-Provider In JDK 1.2 ist bereits ein Standard-Provider enthalten, der SUN-Provider. Dieser beinhaltet: eine Implementierung des Digital Signature Algorithmus (DSA). eine Implementierung der Hash-Funktionen MD5 (RFC 1321) und SHA-1. einen Generator für DSA-Schlüsselpaare, der gemäß des DSA-Algorithmus ein Paar erzeugt, das einen öffentlichen und einen privaten Schlüssel enthält. einen Generator, der Parameter für den DSA-Algorithmus erzeugt. Dies beinhaltet auch ein Verwaltungswerkzeug für die Algorithmusparameter. eine DSA-Key Factory, die bidirektionale Konversionen zwischen privaten und öffentlichen DSA-Schlüsselobjekten und dem zugrunde liegenden Schlüsselmaterial vornimmt. einen Pseudo-Zufallszahlengenerator für das proprietäre „SHA1PRNG"-Format. eine Certificate Factory für X.509-Zertifikate und Widerrufslisten für Zertifikate (Certificate Revocation Lists, CRLs). eine Schlüsselverwaltung (Keystore) für den proprietären Keystore-Typ JKS. Sicherheitswerkzeuge in JDK 1.2 JDK 1.2 stellt ergänzend die folgenden drei Werkzeuge zur Verfügung: das Werkzeug keytool kann zur Erzeugung von Paaren von öffentlichen und privaten Schlüsseln verwendet werden. Weiterhin können mit keytool Zertifikatsketten importiert, exportiert, dargestellt und erzeugt werden. Zertifikate werden hierbei mit X.509 v1 signiert. Außerdem können auch Zertifikate angefordert werden. das Werkzeug jarsigner signiert Dateien im JAR-Format (Java Archive Format) und verifiziert die Authentizität der Signatur(en) unterzeichneter JAR-Dateien. Die Verwendung von JAR wird in Kapitel 4.3 detailliert erläutert. das Werkzeug policytool erzeugt und modifiziert die Dateien, in denen die Sicherheitsaspekte der lokalen Installation festgelegt sind (Policy Configuration).