Sicherheit

Bevor die Netzwerkfunktionalität von Java betrachtet werden kann, muss die Sicherheitsfunktionalität der Sprache Java im Detail erläutert werden. Die im Folgenden angegebene Darstellung baut auf der in Kapitel 1.3 angegebenen allgemeinen Erläuterung des Sicherheitsmodells von Java auf.

Security-Manager

Jede Java-Anwendung kann einen eigenen Security-Manager verwenden, der sämtliche Sicherheitsrestriktionen einer Anwendung überwacht. Hierzu steht im Package java.lang die Klasse SecurityManager zur Verfügung, die als abstrakte Klasse die Programmierschnittstelle und teilweise auch Implementierungen von Java-Security-Managern anbietet.

Standardmäßig verfügt eine Anwendung nicht über einen Security-Manager. Operationen, die in Anwendungen ausgeführt werden, unterliegen daher den allgemeinen Sicherheitsrestriktionen, wenn kein spezieller Security-Manager definiert wird. Spezielle Browser und Appletviewer erzeugen allerdings beim Aufruf einen Security-Manager. Ein Applet unterliegt daher stets den Sicherheitsrestriktionen, die vom Security-Manager einer bestimmten Anwendung definiert werden, in der ein Applet ausgeführt wird.

Um einen Security-Manager für eine Anwendung zu erzeugen, wird die Methode getSecurityManager() der Klasse System verwendet:

syntax 

SecurityManager sm = System.getSecurityManager();

Der Aufruf dieser Methode liefert den Wert null zurück, wenn kein geeigneter Security-Manager zur Verfügung steht, der initialisiert werden könnte. Es sollte daher stets geprüft werden, ob eine Instanz zur Verfügung steht, bevor die entsprechenden Methoden aufgerufen werden.

Nachdem ein Security-Manager erzeugt wurde, muss die Erlaubnis dafür angefordert werden, bestimmte Operationen zu ermöglichen oder zu verbieten. Dieses Vorgehen wird von vielen Klassen der Java-Packages angewendet, bspw. von der Methode System.exit(), die den Java-Interpreter beendet, indem die Methode checkExit() des Security-Managers verwendet wird, um die Anwendbarkeit dieser Operation zu prüfen. Das folgende Beispiel illustriert dieses Vorgehen.

syntax 

SecurityManager s = System.getSecurityManager();

if (s != null) {

    s.checkExit(status);

}
//weitere Operationen, wenn checkExit() beendet ist

Wenn ein Security-Manager die exit-Operation genehmigt, wird die checkExit()-Methode regulär abgeschlossen, anderenfalls erzeugt der Aufruf der Methode checkExit() eine SecurityException-Ausnahme.

Die Klasse SecurityManager definiert eine Reihe weiterer Methoden, die zur Verifikation von Operationen eingesetzt werden können. Beispiele hierfür sind

  • checkAccess()
    zur Prüfung von Zugriffen auf Threads
  • checkPropertyAccess()
    zur Prüfung von Zugriffen auf angegebene Eigenschaften

Jede Operation oder Operationsgruppe verfügt über spezielle checkXXX()-Methoden. Zusätzlich repräsentiert die Menge der checkXXX()-Methoden die Menge an Operationen der Klassen der Java-Packages und der Laufzeitumgebung, die bereits der Überwachung durch den Security-Manager unterliegen. Typischerweise muss daher ein Programm keine der checkXXX()-Methoden des Security-Managers aufrufen, da dies bereits von den Klassen der Java-Packages und der Laufzeitumgebung realisiert wird. Wird allerdings ein eigener Security-Manager implementiert, so kann es durchaus sinnvoll sein, einige der checkXXX()-Methoden des Security-Managers zu überschreiben, um Sicherheitsregeln für bestimmte Operationen zu verändern, bzw. um weitere Operationen von einem Security-Manager überwachen zu lassen. Das hierzu notwendige Vorgehen wird im Folgenden noch detailliert erläutert.

Implementierung eines Security-Managers

Ein Security-Manager wird implementiert, indem eine Subklasse der Klasse SecurityManager erzeugt wird, die einige der Methoden der Klasse SecurityManager überschreibt, um Sicherheitsrestriktionen zu modifizieren.

Im Folgenden wird anhand eines Beispiels betrachtet, wie Zugriffe auf das Dateisystem eingeschränkt werden können. Hierbei wird jeweils die checkRead()-Methode des Security-Managers aufgerufen, wenn eine Datei zum Lesen geöffnet werden soll. In ähnlicher Art und Weise wird die Methode checkWrite() dazu verwendet, die Berechtigung zum Schreiben von Daten zu prüfen. Wenn der Security-Manager eine derartige Operation nicht zulässt, wird eine Security-Exception (Sicherheitsverletzung) ausgelöst.

Um diese Funktionalität zu implementieren, muss die Subklasse der Klasse SecurityManager die Methoden checkRead() und checkWrite() überschreiben. Die Klasse SecurityManager beinhaltet drei Arten von checkRead()-Methoden und zwei Arten von checkWrite()-Methoden. Jede dieser Methoden sollte verifizieren, ob eine Anwendung eine Datei zum Lesen oder Schreiben öffnen darf. Hierbei ist zu bedenken, dass Applets, die über das Netz geladen werden, Zugriffe auf das lokale Dateisystem meist nicht ausführen dürfen, falls der Security-Manager nicht modifiziert wird. Im folgenden Beispiel wird der Benutzer nach einem Passwort gefragt, wenn er auf das lokale Dateisystem zugreifen will. Der Zugriff wird gestattet, wenn das Passwort korrekt eingegeben wird.

syntax 

class PasswortSecurityManager extends SecurityManager {

    //Operationen

}

Anschließend wird innerhalb der Klasse PasswortSecurityManager eine als private gekennzeichnete Instanzvariable passwort deklariert, die das Passwort enthält, das der Benutzer eingeben muss, um Zugriff auf die Systemressourcen zu erhalten.

syntax 

    PasswortSecurityManager(String passwort) {

      super();
      this.passwort = passwort;

    }

Die anschließend deklarierte Methode accessOK() fungiert als Hilfsfunktion. Hier wird der Benutzer aufgefordert, ein Passwort einzugeben, das in der Folge überprüft wird. Die Methode liefert den Wert true zurück, wenn das Passwort korrekt ist, anderenfalls den Wert false. Zur Wiederholung der Konzepte, die im ersten Unterkapitel (Streaming) erläutert wurden, wird hier ein Dateneingabestrom verwendet.

syntax 

    private boolean accessOK() {

      int c;
      DataInputStream dis = new DataInputStream(System.in);
      String antwort;
      System.out.println("Bitte Passwort eingeben");
      try {

        antwort = dis.readLine();
        if (antwort.equals(passwort))

          return true;

        else

          return false;

      } catch (IOException e) {

        return false;

      }

    }

Den Abschluss der Implementierung bildet das Überschreiben der drei checkRead()-Methoden bzw. der zwei checkWrite()-Methoden.

syntax 

    public void checkRead(FileDescriptor filedescriptor) {

      if (!accessOK())

        throw new SecurityException("Zugriff verweigert");

    }
    public void checkRead(String filename) {

      if (!accessOK())

        throw new SecurityException("Zugriff verweigert");

    }
    public void checkRead(String filename, Object executionContext) {

      if (!accessOK())

        throw new SecurityException("Zugriff verweigert");

    }
    public void checkWrite(FileDescriptor filedescriptor) {

      if (!accessOK())

        throw new SecurityException("Zugriff verweigert");

    }
    public void checkWrite(String filename) {

      if (!accessOK())

        throw new SecurityException("Zugriff verweigert");

    }

}

Alle checkXXX()-Methoden rufen die Methode accessOK() auf, damit der Benutzer das Passwort eingeben kann. Wird der Zugriff verweigert, so wird eine Security-Exception erzeugt. SecurityException-Objekte sind Laufzeit-Exceptions und müssen daher nicht mittels throws als Teil der Methoden deklariert werden.

checkRead() und checkWrite() sind Beispiele dafür, wie checkXXX()-Methoden überschrieben werden können. Es ist zu beachten, dass nicht alle checkXXX()-Methoden eines Security-Managers überschrieben werden müssen, sondern stets nur diejenigen, deren Funktionsweise modifiziert werden soll. Die Standardimplementierung der checkXXX()-Methoden des Security-Managers erzeugt im Fehlerfall allerdings immer eine Security-Exception, verbietet also alle Operationen, die Sicherheitseinschränkungen unterliegen. Alle checkXXX()-Methoden des Security-Managers operieren auf dieselbe Art und Weise:

  • Wird der Zugriff gestattet, so wird die Methode beendet.
  • Anderenfalls wird ein SecurityException-Objekt generiert.

Installation des Security-Managers

Nachdem eine Subklasse der Klasse SecurityManager erzeugt wurde, kann diese als Security-Manager einer Java-Anwendung installiert werden. Hierzu muss die Methode setSecurityManager(), die Teil der Klasse System ist, verwendet werden.

Im folgenden Beispiel wird die Klasse PasswortSecurityManager als Security-Manager installiert. Um zu prüfen, ob der Security-Manager wirklich arbeitet, werden zwei Dateien geöffnet. Die erste Datei wird hierbei ausgelesen und in die zweite kopiert. Zunächst wird ein Eingabestrom angelegt, damit der Benutzer sein Passwort eingeben kann. Dieser Strom wird als Argument an den Security-Manager übergeben.

syntax 

import java.io.*;
public class TestSecurityManager {

    public static void main(String[] args) throws Exception {

      BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
      try {

        System.setSecurityManager(new PasswortSecurityManager("Open Java", buf));

      } catch (SecurityException e) {

        System.out.println("SecurityManager bereits gesetzt");

      }

Zuerst wird ein Security-Manager angelegt, der als Argument das Passwort Open Java erhält. Diese Instanz wird an die Methode setSecurityManager() übergeben. Der derart installierte Security-Manager ist so lange aktiv, wie die Anwendung läuft. Hierbei ist zu beachten, dass ein Security-Manager in einer Anwendung nur genau einmal gesetzt werden darf. Weitere Versuche resultieren in einer Security-Exception.

Die restlichen Zeilen des Programms kopieren die Datei beispiel1.txt in die Datei beispiel2.txt. Diese Funktion dient als Beweis dafür, dass der Security-Manager wie gewünscht arbeitet.

syntax 

    BufferedReader in = new BufferedReader(new FileReader("beispiel1.txt"));
    PrintWriter out = new PrintWriter(new FileWriter("beispiel2.txt"));
    String is;
    while ((is = in.readLine()) != null)

      out.println(is);

    in.close();
    out.close();

}

}

Es ist zu beachten, dass im Beispiel das Passwort zweimal angefordert wird, da auf zwei Dateien zugegriffen werden muss. Das Beispiel arbeitet nur dann korrekt, wenn das Passwort in beiden Fällen korrekt eingegeben wird. Anderenfalls stürzt die Anwendung ab, da die Exception nicht abgefangen wird.

Überschreiben von Methoden des Security-Managers

In Abhängigkeit von den Operationen, die der Security-Manager überwachen soll, müssen einige der checkXXX()-Methoden überschrieben werden. In Tab. 5-6 ist dargestellt, welche Operationen auf welchen Objekten eingesetzt werden können, um Sicherheitsrestriktionen umzusetzen.

Operationen auf

Sicherheitsoperationen

Sockets

checkAccept(String host, int port)
checkConnect(String host, int port)
checkConnect(String host, int port, Object executionContext)
checkListen(int port)

Threads

checkAccess(Thread thread)
checkAccess(ThreadGroup threadgroup)

Einladen von Klassen

checkCreateClassLoader()

Dateisystem

checkDelete(String filename)
checkLink(String library)
checkRead(FileDescriptor filedescriptor)
checkRead(String filename)
checkRead(String filename, Object executionContext)
checkWrite(FileDescriptor filedescriptor)
checkWrite(String filename)

Systemkommandos

checkExec(String command)

Interpreter

checkExit(int status)

Packages

checkPackageAccess(String packageName)
checkPackageDefinition(String packageName)

Eigenschaften

checkPropertiesAccess()
checkPropertyAccess(String key)
checkPropertyAccess(String key, String def)

Netzwerke

checkSetFactory()

Fenster

checkTopLevelWindow(Object window)

Tab. 5.6: Objekte und Sicherheitsoperationen

In Abhängigkeit von der angestrebten Sicherheitsfunktionalität können lediglich einige, aber auch alle dieser Methoden überschrieben werden. Die Standardimplementierung, die in der Klasse SecurityManager zur Verfügung steht, sieht wie folgt aus:

code 

public void checkXXX(. . .) {

    throw new SecurityException();

}

Kontrolle von Applets und Applications

Im Folgenden werden die Möglichkeiten erläutert, die in Java zur Kontrolle von Applets und Applications zur Verfügung stehen.

Applets

In Java werden Security-Manager dazu eingesetzt, Sicherheitsrisiken so weit wie möglich zu minimieren. Die meisten Browser installieren einen Security-Manager, mit dem die Ausführung von Applets kontrolliert werden kann. Jedes Applet kann in der Folge Systemressourcen nur dann verwenden, wenn der Security-Manager dies explizit gestattet. Java-Umgebungen, die mit JDK 1.2 kompatibel sind, müssen derartige Vorgänge in einer sog. Policy-Datei vermerken.

Das folgende Applet versucht, Daten in eine Datei des derzeit verwendeten Verzeichnisses zu schreiben. Dies ist solange unmöglich, wie kein Eintrag in einer Policy-Datei vorgenommen wird.

code 

import java.awt.*;
import java.io.*;
import java.lang.*;
import java.applet.*;
public class DateiSchreiben extends Applet {

    String datei = "beispiel";
    File f = new File(datei);
    DataOutputStream dos;
    public void init() {
    }
    public void paint(Graphics g) {

      try {

        dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(datei),100));
        dos.writeChars("Jetzt schreiben\n");
        dos.flush();
        g.drawString("In Datei geschrieben: " + datei, 10, 10);

      }catch (SecurityException e) {

        g.drawString("Security Exception: " + e, 10, 10);

      }
      catch (IOException ioe) {

        g.drawString("I/O Exception", 10, 10);

      }

    }

}

Wird das Applet in dieser Form aufgerufen, so wird eine Security-Exception erzeugt, da auf Ressourcen zugegriffen wird, auf die Applets für gewöhnlich nicht zugreifen dürfen.

Eine Policy-Datei liegt stets in Form von ASCII-Text vor und kann mit einem Texteditor oder mit dem im Folgenden beschriebenen Policy-Tool bearbeitet werden. Mit Hilfe des Policy-Tools ist die Bearbeitung wesentlich komfortabler, da die erforderliche Syntax nicht beachtet werden muss. Zur Verwendung des Policy-Tools müssen die folgenden Schritte durchlaufen werden:

  • Aufruf des Policy-Tools
  • Eintrag der erforderlichen Berechtigung
  • Speichern der Policy-Datei

ptBlank1 

Abb. 5.6: Policy-Tool

Diese Schritte sind für Benutzer verschiedener Betriebssysteme identisch. Es muss allerdings beachtet werden, dass sich die im Folgenden verwendeten Pfadnamen auf Windows-Betriebssysteme beziehen.

Um das Policy-Tool aufzurufen, muss lediglich die Anweisung policytool in der Kommandozeile eingegeben werden, woraufhin das Fenster des Policy-Tools erscheint. Jedes Mal, wenn das Policy-Tool aufgerufen wird, werden bereits einige Informationen im Fenster eingetragen, bspw. der Dateiname der Policy-Datei, die standardmäßig als .java.policy im Home-Directory des Benutzers angelegt wird. Kann das Policy-Tool diese Datei nicht finden, so erfolgt eine Fehlermeldung und das entsprechende Feld des Fensters bleibt leer (siehe Abb. 5-6).

AddEntryBlank1 

Abb. 5.5: Policy-Einträge

In diesem Fall kann entweder eine Policy-Datei geöffnet oder eine neue Policy-Datei angelegt werden. Wird das Policy-Tool zum ersten Mal verwendet, so steht noch keine derartige Datei zur Verfügung und das Fenster ist stets leer. In diesem Fall ist zunächst eine Policy-Datei anzulegen, wie im Folgenden beschrieben wird.

Um dem Beispiel-Applet die erforderlichen Berechtigungen einzuräumen, muss in der Policy-Datei ein entsprechender Eintrag angelegt werden. Hierzu ist zunächst die Option Add Policy Entry im Policy-Tool auszuwählen, wodurch ein neues Fenster erscheint (siehe Abb. 5-5). Ein Policy-Eintrag gibt eine oder mehrere Berechtigungen für Code-Segmente an, die entweder in Form einer URL, in Form signierten Codes oder in beiden Formen vorliegen können.

Die Textbereiche CodeBase und SignedBy werden dazu benutzt, anzugeben, welcher Code Berechtigungen erhalten soll. Der Parameter CodeBase zeigt hierbei an, wo der Code gespeichert ist, der die Berechtigung erhält. Wird dieses Feld leer gelassen, so bezieht sich die Berechtigung auf jedes Programm, das ausführbar ist.

AddPermBlank 

Abb. 5.7: Berechtigungen

Der Wert des Feldes SignedBy bezeichnet einen Alias-Namen für ein Zertifikat, das in einem Keystore gespeichert ist. Der öffentliche Schlüssel dieses Zertifikats wird dazu eingesetzt, um die digitale Signatur des Codes zu verifizieren. Die Berechtigung wird daher einem Code-Segment eingeräumt, das mit dem privaten Schlüssel unterschrieben ist, zu dem der öffentliche Schlüssel gehört, der im Keystore gespeichert ist und auf den sich der Alias-Name bezieht. Das SignedBy-Feld ist allerdings optional. Wird dieses Feld nicht ausgefüllt, so spielt es keine Rolle, ob der Code signiert ist oder nicht bzw., wer den Code signiert hat. Wurden beide Felder ausgefüllt, so beziehen sich die Berechtigungen nur auf den Code, der an der angegebenen Stelle gespeichert ist und der vom angegebenen Alias-Namen unterschrieben ist.

Nachdem CodeBase und eventuell SignedBy angegeben wurden, können die Berechtigungen angegeben werden. Hierzu muss der Button Add Permission betätigt werden, woraufhin das Fenster Permissions erscheint (siehe Abb. 5-7). Um die Berechtigungen geeignet zu setzen, sind die folgenden Schritte auszuführen:

  1. Auswahl des Punktes File Permission aus der Liste Permission:. Anschließend erscheint der vollständige Name des Berechtigungstyps (java.io.FilePermission).
  2. Angabe des Namens des Applets in das Feld, das mit Target Name bezeichnet ist, in diesem Fall also DateiSchreiben.
  3. Angabe der Schreibberechtigung, indem die Option write aus der Liste namens Actions ausgewählt wird.

Nachdem die Berechtigungen gesetzt wurden, muss der OK-Button und anschließend der Done-Button betätigt werden. Die gesetzten Berechtigungen sind dann jeweils im Fenster erkennbar.

Nachdem die Bearbeitung der Policy-Datei abgeschlossen ist, muss diese noch gespeichert werden. Hierzu wird aus dem Menü File die Option Save As ausgewählt, woraufhin der Name angegeben werden kann, unter dem die Policy-Datei gespeichert werden soll. Im Anschluss daran kann das Policy-Tool beendet werden, indem im Menü File die Option Exit ausgewählt wird.

Nachdem eine Policy-Datei erzeugt wurde, muss sie geladen werden. Wenn ein Applet bzw. eine Application mit einem Security-Manager abläuft, so werden standardmäßig die Policy-Dateien geladen, die in der sog. Security-Properties-Datei gespeichert sind. Bezeichnet man mit java.home das Verzeichnis, in dem Java installiert ist, so ist die Security-Properties-Datei in den folgenden Verzeichnissen zu finden:

  • java.home\lib/security\java.security (Windows)
  • java.home/lib/security/java.security (Solaris)

Die Speicherorte der Policy-Dateien werden in der Security-Properties-Datei stets in der Form policy.url.n=URL angegeben, wobei n eine Zahl sein muss. Beispiele für Security-Property-Dateien sind:

  • policy.url.1=file:${java.home}/lib/security/java.policy
  • policy.url.2=file:${user.home}/.java.policy

Üblicherweise bezeichnet man mit der Syntax ${variablenName} den Wert einer Eigenschaft. Der Ausdruck ${java.home} wird dann zur Laufzeit mit dem entsprechenden Wert ersetzt, der das Verzeichnis der Java-Installation angibt. ${user.home} wird entsprechend durch das Home-Verzeichnis des Benutzers ersetzt.

Um die im vorangegangenen Schritt erstellte Policy-Datei aufzurufen, sind zwei Vorgehensweisen denkbar:

  • Angabe der zusätzlichen Policy-Datei in einem Parameter, das dem Laufzeitsystem übergeben wird.
  • Hinzufügen einer Zeile in der Security-Properties-Datei, die die zusätzliche Datei angibt.

Um die erste Möglichkeit zu verwenden, muss in der Kommandozeile des Appletviewers der Name der Policy-Datei angegeben werden, die zusätzlich verwendet werden soll. Hierbei kann auch angegeben werden, dass die Security-Properties-Datei nicht verwendet werden soll.

code 

appletviewer -J-Djava.security.policy=eigenepolicy DateiSchreiben.html

Ergeben sich in der Ausführung des Applets Fehler, so muss die Policy-Datei bspw. mit Hilfe des Policy-Tools auf Fehler untersucht werden.

Die andere Möglichkeit besteht darin, der Security-Properties-Datei mit Hilfe eines Text-Editors eine weitere Zeile hinzuzufügen. Hierzu ist die URL anzugeben, unter der die Policy-Datei zu finden ist, bspw. in der folgenden Form (Windows-Systeme):

code 

policy.url.3=file:/C:/eigenepolicy

Nachdem die Security-Properties-Datei gespeichert wurde, kann das Applet bspw. mit Hilfe des Appletviewers ohne Angabe zusätzlicher Parameter ausgeführt werden. Ist dies nicht der Fall, so sollten Fehler in der Policy-Datei gesucht werden.

Applications

Wird eine Application ausgeführt, so wird nicht automatisch ein Security-Manager installiert. Um aber dieselben Sicherheitseinschränkungen verwenden zu können wie bei der Ausführung von Applets, kann der Interpreter mit der folgenden Option aufgerufen werden:

code 

java -Djava.security.manager dateiname

Hierbei wird standardmäßig die System-Policy-Datei geladen, die allen Code-Segmenten die Möglichkeit einräumt, auf allgemeine Eigenschaften zuzugreifen. Zusätzliche Eigenschaften werden allerdings nicht zur Verfügung gestellt, Zugriffe hierauf resultieren daher in Fehlern. Die System-Policy-Datei ist standardmäßig in folgendem Verzeichnis abgelegt:

  • java.home\lib\security\java.policy (Windows)
  • java.home/lib/security/java.policy (Solaris)

Um den Zugriff so einzurichten, wie es für eine spezielle Application erforderlich ist, muss erneut eine Policy-Datei modifiziert werden. Dies erfolgt in der Art und Weise, wie Policy-Dateien für Applets angelegt werden.

Zur Verwendung der Policy-Dateien stehen wiederum zwei Möglichkeiten zur Verfügung: Der Aufruf über die Kommandozeile oder der Eintrag in der Security-Properties-Datei. Zum Aufruf in der Kommandozeile ist die folgende Syntax zu verwenden:

code 

java -Djava.security.manager -Djava.security.policy=eigenepolicy dateiname

Die Modifikation der Security-Properties-Datei erfolgt in analoger Art und Weise wie bei Applets. Nachdem diese Datei geeignet verändert wurde, sind die Sicherheitseinschränkungen dann wirksam, wenn das Programm wie folgt aufgerufen wird:

code 

java -Djava.security.manager dateiname

APIs und Security-Tools

Im Folgenden werden die erweiterten Sicherheitsfunktionen von Java betrachtet. Um das Verständnis der folgenden Unterkapitel zu ermöglichen, werden zunächst wichtige Begriffe der Sicherheitstechnik eingeführt bzw. wiederholt. Werkzeuge (Tools), die hierbei zum Einsatz kommen, sind bspw. der Vorgang des Signierens von Code bzw. das Einräumen von Berechtigungen sowie der Dateiaustausch. APIs kommen zum Einsatz, um Signaturen zu erzeugen und um diese zu verifizieren.

Wenn ein Dokument elektronisch übertragen wird, so muss sichergestellt werden, dass das Dokument bzw. der Code auch tatsächlich vom Absender stammt, bzw. dass die Daten während der Übertragung nicht modifiziert wurden. Diese Funktion ist die Aufgabe digitaler Signaturen.

Digitale Signaturen

  1. Die grundsätzliche Verwendung von digitalen Signaturen erfolgt in folgenden Schritten:
  2. Ein Dokument bzw. Code wird mit Hilfe eines privaten Schlüssels signiert. Private Schlüssel können mit Hilfe des Werkzeugs keytool bzw. mit Hilfe von Methoden des Security-APIs erzeugt werden. Eine digitale Signatur wird erzeugt, indem das Werkzeug jarsigner oder API-Methoden verwendet werden.
  3. Das Dokument wird zusammen mit der Signatur an den Empfänger geschickt.
  4. Der Empfänger erhält außerdem den öffentlichen Schlüssel, der zum privaten Schlüssel des Senders gehört, mit dem die Daten signiert wurden.
  5. Mit Hilfe des öffentlichen Schlüssels kann der Empfänger sicherstellen, dass das Dokument bzw. der Code authentisch ist.

Zertifikate

Üblicherweise muss der Empfänger auch sicherstellen, dass der öffentliche Schlüssel authentisch ist. Zur Realisierung dieser Funktionalität werden Zertifikate eingesetzt. Ein Zertifikat enthält stets

  • einen öffentlichen Schlüssel,
  • den Namen der Entität (bspw. Person oder Firma), die das Zertifikat erstellt hat. Man bezeichnet diese Entität auch als Subject oder Owner eines Zertifikats. Der Name des Owners enthält meist eine (Teil-)Menge der folgenden Attribute: den Namen der Entität, die Organisationseinheit, die Organisation, die Stadt, den Staat und den Landes-Code.
  • eine digitale Signatur. Ein Zertifikat wird von einer Entität unterschrieben, um zu beweisen, dass der enthaltene öffentliche Schlüssel der aktuelle öffentliche Schlüssel einer anderen Entität ist (der des Owners).
  • den Namen des Unterzeichnenden.

Eine Möglichkeit, mit der der Empfänger überprüfen kann, ob ein Zertifikat gültig ist, besteht darin, die digitale Signatur des Unterzeichnenden mit Hilfe seines öffentlichen Schlüssels auszuwerten. Dieser Schlüssel kann wiederum von einer weiteren Institution zertifiziert sein, bis ein bestimmter öffentlicher Schlüssel zur Dekodierung verwendet wird, dem in jedem Fall vertraut werden kann.

Kann der Empfänger eine derartige Kette nicht aufbauen, da bspw. keine derartigen Instanzen zur Verfügung stehen, so kann der Fingerabdruck des Zertifikats berechnet werden. Hierzu kann das Werkzeug keytool (Optionen -import oder -printcert) eingesetzt werden. Jeder Fingerabdruck besteht aus einer kurzen Zahl, die das Zertifikat eindeutig und zuverlässig identifiziert (Hash-Code). Der Empfänger kann dann den Owner des Zertifikats kontaktieren und die Fingerabdrücke der gesendeten und der empfangenen Daten vergleichen. Sind diese identisch, so sind auch die Zertifikate gleich. Auf diese Art und Weise kann sichergestellt werden, dass ein Zertifikat während der Übertragung nicht modifiziert wurde. Eine weitere potentielle Unsicherheit besteht aber in der Identität des Absenders.

Manchmal kann ein Zertifikat auch selbst unterschrieben sein, indem die Unterschrift mit dem privaten Schlüssel geleistet wird, dessen korrespondierender öffentlicher Schlüssel im Zertifikat enthalten ist. Hierdurch kann bewiesen werden, dass der Inhalt des Zertifikats zu einem privaten Schlüssel gehört. Dieser Fall ist nur dann vertrauenswürdig, wenn der Empfänger den Absender bereits kennt und diesem vertraut.

Anderenfalls muss der Sender ein Zertifikat von einer vertrauenswürdigen dritten Partei erhalten, die auch als Certification Authority (CA) bezeichnet wird. Um dies zu erreichen, sendet man eine selbst signierte Nachricht, einen sog. Certificate Signing Request (CSR) an eine CA. Die CA verifiziert die Signatur, die in der CSR enthalten ist, und die Identität des Absenders. Anschließend bürgt die CA dafür, dass der Sender der Eigentümer eines öffentlichen Schlüssels ist, indem ein Zertifikat ausgestellt wird und dieses mit dem privaten Schlüssel der CA unterschrieben wird. Jeder, der dem öffentlichen Schlüssel der CA vertraut, kann dann die Signatur des Zertifikats verifizieren. In vielen Fällen wird die CA selbst von einer weiteren CA zertifiziert. Aus diesem Grund sind CAs meist in Form von Hierarchien organisiert. Lediglich der CA auf oberster Ebene müssen alle Partner vertrauen.

Zertifikate von Entitäten, denen ein Sender vertraut, werden üblicherweise in Schlüsseldatenbanken, sog. Keystores, als „trusted Zertifikate" geladen. Der öffentliche Schlüssel jedes Zertifikats kann dann dazu verwendet werden, um Signaturen zu verifizieren, die mit einem entsprechenden privaten Schlüssel erzeugt wurden. Derartige Prüfungen können folgendermaßen erfolgen:

  • mit Hilfe des Tools jarsigner, wenn ein Dokument/Code und die Signatur Teil einer JAR-Datei sind,
  • mit Hilfe von API-Methoden,
  • mit Hilfe des Laufzeitsystems, wenn ein Ressourcenzugriff erfolgen soll und eine Policy-Datei angibt, dass der Zugriff für Code erlaubt ist, dessen Signatur authentisch ist. Die class-Dateien und die Signatur müssen dann in einer JAR-Datei enthalten sein.

Wenn signierter Code oder signierte Dokumente an andere gesendet werden, so muss das Zertifikat beigefügt sein, das den öffentlichen Schlüssel enthält und mit dessen korrespondierendem privaten Schlüssel das Dokument bzw. der Code unterschrieben wurde. Mit Hilfe der Option -export des Werkzeugs keytool oder mit Hilfe von API-Methoden kann ein Zertifikat aus einem Keystore in eine Datei exportiert werden, die dann übertragen werden kann. Der Empfänger kann dann dieses Zertifikat in seinen Keystore als Trusted Certificate importieren, bspw. indem API-Methoden verwendet werden oder die Option -import des Werkzeugs keytool.

Wird jarsigner dazu verwendet, eine Signatur einer JAR-Datei zu erstellen, so lädt das Werkzeug das Zertifikat bzw. die entsprechende Zertifikatskette aus dem Keystore und speichert diese zusammen mit der Signatur in der JAR-Datei.

Keystores

Private Schlüssel und entsprechende Zertifikate mit öffentlichen Schlüsseln werden in Datenbanken, sog. Keystores gespeichert und mit einem Passwort geschützt. Ein Keystore kann stets zwei Arten von Einträgen beinhalten: Trusted Zertifikate und Einträge der Form [Schlüssel/Zertifikat]. Jeder Eintrag muss jeweils einen privaten Schlüssel und das entsprechende Zertifikat mit öffentlichem Schlüssel enthalten. Jeder Eintrag eines Keystores wird durch einen Alias-Namen identifiziert.

Der Besitzer eines Keystores kann eine Vielzahl verschiedener Schlüssel in einem Keystore verwalten, auf die mit Hilfe verschiedener Alias-Namen zugegriffen wird. Ein Alias-Name wird typischerweise nach einem bestimmten Kontext benannt, in dem der Besitzer den privaten Schlüssel verwendet, bspw. nach dem Einsatzzweck des Schlüssels. Der Eintrag signiereJarFiles könnte bspw. angeben, dass der Eintrag zur Signatur von JAR-Dateien verwendet wird. Das Werkzeug keytool kann dazu verwendet werden,

  • private Schlüssel und entsprechende Zertifikate mit öffentlichen Schlüsseln zu erzeugen,
  • Zertifikatsanfragen zu erzeugen, die an eine geeignete Certification Authority geschickt werden,
  • Zertifikatsantworten zu importieren, die von der kontaktierten Certification Authority gesendet wurden,
  • Zertifikate mit öffentlichen Schlüsseln zu importieren, die anderen Besitzern in Form von vertrauenswürdigen Zertifikaten gehören und
  • um den Keystore zu verwalten.

Auch API-Methoden können zum Zugriff auf einen Keystore bzw. zu dessen Verwaltung verwendet werden.

Tools und APIs

Das Security-API des JDKs kann, ebenso wie die Werkzeuge (Tools), in Kombination oder alleine dazu verwendet werden, um Schlüssel und Signaturen zu erzeugen und um Zertifikate zu importieren. Tools und APIs können daher dazu eingesetzt werden, um Dokumente oder Code auf sichere Art und Weise mit Dritten auszutauschen.

Um diesen Austausch vornehmen zu können, müssen Dokumente oder Code in JAR-Dateien gespeichert werden, die bspw. mit Hilfe des Werkzeugs jar erstellt werden können. Mittels JAR-Dateien können mehrere Dateien auf einfache Art und Weise zusammengebunden werden. Wird eine Datei signiert, so muss die resultierende digitale Signatur an anderer Stelle gespeichert werden. Im Falle einer JAR-Datei kann die Signatur allerdings direkt in der JAR-Datei gespeichert werden. Hierzu wird das Werkzeug jarsigner verwendet. An dieser Stelle sollte darauf aufmerksam gemacht werden, dass keine Unterscheidung zwischen der Unterzeichnung von Applets oder Applications gemacht wird. Beide Arten müssen in einer JAR-Datei gespeichert und signiert werden.

Wenn eine Laufzeitumgebung eine Code-Signatur auswerten will, muss die Person bzw. Organisation zunächst ein Zertifikat in ihren Keystore importieren, das den öffentlichen Schlüssel authentifiziert, der zum privaten Schlüssel gehört, mit dem der Code signiert wurde.

Signaturen

Nachdem im vorangegangenen Abschnitt grundlegende Begriffe der sicheren Datenübertragung erläutert wurden, wird in diesem Abschnitt erklärt, wie die Security-Tools keytool, jarsigner und policytool funktionieren bzw. wie sie zusammenwirken. In diesem Kontext wird nochmals die Funktion des Werkzeugs jar betrachtet, das mit jarsigner bei der Signatur von Java-Archiven zusammenarbeitet.

Zuerst wird erläutert, wie der Sender von Daten vorgehen muss, wenn er Daten signieren will. Die Techniken, die in Bezug auf den Sender in diesem Unterkapitel vermittelt werden, lassen sich in folgendes Schema gliedern:

  1. Erzeugung einer Anwendung
  2. Speicherung der Anwendung in einer JAR-Datei
  3. Vorgang des Signierens der JAR-Datei
  4. Export des Zertifikats, das den öffentlichen Schlüssel enthält, der wiederum zu dem privaten Schlüssel passt, mit dem die JAR-Datei unterschrieben wurde.
  5. Anschließend wird erläutert, wie der Empfänger der Daten vorgehen muss, der die signierte JAR-Datei und das Zertifikat erhält. Im Einzelnen geht der Empfänger in den folgenden Schritten vor:
  6. Verwendung von keytool zum Import des Zertifikats in einen Keystore. Hierbei wird ein Alias-Name für das Zertifikat angelegt.
  7. Verwendung des Policy-Tools zum Anlegen eines Eintrags in einer Policy-Datei, damit der signierte Code eine Datei lesen darf.
  8. Lesen einer Datei unter Verwendung eines Security-Managers.

Funktionalität des Senders

Im ersten Schritt wird zunächst eine Anwendung erstellt, die zu einem späteren Zeitpunkt beim Empfänger ausgeführt werden soll. Diese Anwendung beinhaltet eine sicherheitskritische Funktion, das Lesen von der Festplatte. Die Implementierung ist sehr einfach: Ein Benutzer gibt einen Dateinamen über die Kommandozeile an. Anschließend wird zum Auslesen einer Datei ein BufferedReader-Objekt erzeugt bzw. der Inhalt der jeweiligen Datei auf Kommandozeilenebene ausgegeben.

code 

import java.io.*;
public class Ausgabe{

    public static void zeilenAusgabe (BufferedReader in) throws IOException {

      String is;
      while ((is = in.readLine()) != null)

        System.out.println(is);

    }
    public static void main(String[] args) throws Exception {

      if (args.length >= 1)

        zeilenAusgabe(new BufferedReader(new FileReader(args[0])));

      else

        System.err.println("Eingabe: Ausgabe Dateiname");

    }

}

Es sollte beachtet werden, dass die Datei, die eingelesen werden soll, in einem anderen Verzeichnis gespeichert sein sollte, als in demjenigen, in dem der Java-Quellcode ausgeführt wird. Im Verlauf dieses Unterkapitels wird erkennbar werden, dass eine Anwendung, die innerhalb eines Security-Managers ausgeführt wird, Daten in solchen Verzeichnissen nicht lesen darf, die vom Verzeichnis der .class-Datei abweichen, wenn die diesbezüglichen Berechtigungen nicht gesetzt sind. Einen Anwendung darf Dateien allerdings immer aus dem Verzeichnis lesen, in dem auch die .class-Datei abgelegt ist. Das Beispiel kann daher nur dann sinnvoll getestet werden, wenn die Speicherorte der .class-Datei und der einzulesenden Datei unterschiedlich sind.

Nachdem die Anwendung erstellt und übersetzt ist, muss eine JAR-Datei erzeugt werden, die die Datei Ausgabe.class enthält. Hierzu ist das folgende Kommando einzugeben:

code 

jar cvf Ausgabe.jar Ausgabe.class

Hierdurch wird eine JAR-Datei Ausgabe.jar angelegt, in der die Datei Ausgabe.class gespeichert ist.

Nach dem Anlegen der JAR-Datei muss der Code signiert werden. Wenn hierzu noch kein geeigneter privater Schlüssel zur Verfügung steht, muss dieser zunächst zusammen mit dem dazugehörigen öffentlichen Schlüssel erstellt werden. Der öffentliche Schlüssel kann dann vom Empfänger dazu verwendet werden, um die Signatur zu verifizieren. Im Folgenden sei angenommen, dass Schlüssel für einen Benutzer namens beispiel, der für eine Firma namens Firma arbeitet, angelegt werden sollen. Um einen Keystore bzw. die notwendigen Schlüssel zu erzeugen, muss in der Kommandozeile die folgende Anweisung eingegeben werden:

code 

keytool -genkey -alias signaturDatei -keypass bspkap5 -keystore beispielstore -storepass Fir123ma

Es ist zu beachten, dass diese Anweisung in einer einzelnen Zeile eingegeben wird. In Tab. 5-7 ist angegeben, welche Bedeutung die einzelnen Teile der oben verwendeten Anweisung haben.

Option

Bedeutung

genkey

Erzeugung von Schlüsseln.

alias signaturDatei

Angabe des Alias-Namens, der die Schlüssel repräsentiert, die zur Signatur von Dateien verwendet werden.

keypass bspkap7

Angabe des Passworts für den zu erzeugenden privaten Schlüssel. Dieses Passwort wird stets zum Zugriff auf den Schlüssel verwendet, der im Keystore gespeichert ist. Dieser Eintrag ist optional; wird er ausgelassen, so wird das Schlüsselpasswort mit der Option abgefragt, dass dasselbe Schlüsselwort verwendet werden kann wie das des Keystores.

keystore beispielstore

Angabe des Namens bzw. optional des Pfads des Keystores, der erzeugt werden soll, falls noch nicht existent, bzw. des Keystores, der verwendet werden soll, falls bereits existent.

storepass Fir123ma

Angabe des Passworts, mit dem auf den Keystore zugegriffen werden kann. Wird diese Option nicht verwendet, so wird das Anlegen des Passworts automatisch initiiert.

Tab. 5.7: Erzeugung von Schlüsselpaaren

Aus Sicherheitsgründen sollten die Passwörter für Schlüssel und Keystore nicht in der Kommandozeile angegeben werden, da derartige Eingaben ausgespäht werden könnten. Anstelle dessen sollten die Optionen keypass und storepass nicht angegeben werden und das jeweilige Passwort dann spezifiziert werden, wenn das System dies verlangt.

Wenn das oben angegebene Kommando ausgeführt wird, werden Informationen über den sog. Distinguished Name angefordert. Hierbei werden die folgenden Daten abgefragt. Jeweils nach dem Ausdruck in eckigen Klammern ist angegeben, welche Informationen eingegeben wurden.

code 

What is your first and last name? [Unknown]: Stephan Fischer

What is the name of your organizational unit? [Unknown]: E-Technik

What is the name of your organization? [Unknown]: Firma

What is the name of your City or Locality? [Unknown]: Darmstadt

What is the name of your State or Province? [Unknown]: Germany

What is the two-letter country code for this unit? [Unknown]: DE

Is <CN=Stephan Fischer, OU=E-Technik, O=Firma, L=Darmstadt, ST=Germany, C=DE> correct? [no]:  y

Das derart eingesetzte keytool-Kommando erzeugt einen Keystore namens beispielstore, falls dieser nicht bereits existiert. Der Keystore wird in dem Verzeichnis gespeichert, in dem das Kommando ausgeführt wurde. Für Zugriffe wird das Passwort Fir123ma vereinbart. Weiterhin wird ein Paar aus öffentlichem und privatem Schlüssel angelegt, das über einen Distinguished Name näher bezeichnet ist.

Die Anweisung erzeugt weiterhin ein Zertifikat, das mit dem privaten Schlüssel unterschrieben ist (selbst signiert) und das den Distinguished Name im subject-Feld des Zertifikats enthält. Dieses Zertifikat hat eine Gültigkeit von 90 Tagen, falls nicht mittels der Option validity ein anderer Wert angegeben wird. Das Zertifikat ist dem privaten Schlüssel zugeordnet, der mit dem Alias-Namen signaturDatei angesprochen werden kann. Der private Schlüssel kann nur verwendet werden, wenn das Passwort, in diesem Fall bspkap5, bekannt ist.

Nach der Generierung von Schlüsseln und Keystore stehen alle Mittel zur Verfügung, um die bereits angelegte JAR-Datei zu signieren. Hierzu muss die folgende Anweisung auf Kommandozeilenebene verwendet werden:

code 

jarsigner -keystore beispielstore -signedjar sAusgabe.jar Ausgabe.jar signaturDatei

Bei der Ausführung dieser Anweisung wird der Keystore beispielstore verwendet. Der Name der signierten Datei wird mit der Option signedjar angegeben. Der letzte Ausdruck der Anweisung entspricht dem Alias-Namen des privaten Schlüssels, mit dem signiert werden soll. Führt man die Anweisung aus, so wird zunächst das Passwort des Keystores erfragt (Fir123ma), anschließend das Passwort des privaten Schlüssels (bspkap5). jarsigner extrahiert weiterhin das Zertifikat aus dem Keystore und hängt dieses an die Signatur der signierten JAR-Datei an.

Nachdem eine signierte JAR-Datei erzeugt wurde, muss das Laufzeitsystem des Code-Empfängers in die Lage versetzt werden, die Signatur zu authentifizieren, wenn die Anwendung, die in der signierten JAR-Datei gespeichert ist, versucht, eine Datei zu lesen und dabei die Policy-Datei dem signierten Code die notwendige Berechtigung einräumt. Um die Signatur authentifizieren zu können, muss der öffentliche Schlüssel des Senders im Keystore des Empfängers gespeichert sein. Hierzu muss eine Kopie des Zertifikats gesendet werden, das den öffentlichen Schlüssel authentifiziert. Um das Zertifikat aus dem Keystore in eine Datei zu kopieren, muss die folgende Anweisung eingegeben werden. Hierbei wird wiederum das Passwort des Keystores (Fir123ma) abgefragt. Die Datei, die das Zertifikat anschließend enthält, heißt in diesem Fall StephanFischer.cer.

code 

keytool -export -keystore beispielstore -alias signaturDatei -file StephanFischer.cer

Funktionalität des Empfängers

Der Empfänger, der die Anwendung ausführen möchte, geht in den folgenden Schritten vor:

  1. Verwendung von keytool zum Import des Zertifikats in einen Keystore. Hierbei wird ein Alias-Name für das Zertifikat angelegt.
  2. Verwendung des Policy-Tools zum Anlegen eines Eintrags in einer Policy-Datei, damit der signierte Code eine Datei lesen darf.
  3. Lesen einer Datei unter Verwendung eines Security-Managers.

Um eine Anwendung ausführen zu können, die in einer JAR-Datei gespeichert ist, muss der Empfänger die folgende Anweisung eingeben, bei der mit der Option cp (Classpath) angegeben wird, welche JAR-Datei aufgerufen werden soll. Hierbei soll die Datei gelesen werden, die unter dem Pfad ..\Hallo.txt gespeichert ist. Hierbei soll aber ein Security-Manager Verwendung finden, der mittels der Option -Djava.security.manager angegeben wird.

code 

java -Djava.security.manager -cp sAusgabe.jar Ausgabe ..\Hallo.txt

Es sollte nun unmittelbar verständlich sein, dass die Ausführung dieser Anweisung eine Exception generiert. Die Exception ergibt sich zwangsläufig, da die Anwendung versucht, auf das Dateisystem des Empfängers zuzugreifen, was bei Verwendung des Security-Managers verboten ist, da die Anwendung nicht über dementsprechende Berechtigungen verfügt.

Bevor dem signierten Code die notwendigen Berechtigungen eingeräumt werden können, muss das Zertifikat des Senders als sog. Trusted Certificate in den Keystore geladen werden.

Es sei nun angenommen, dass der Sender die signierte JAR-Datei sAusgabe.jar geschickt hat, die die Dateien Ausgabe.class und StephanFischer.cer enthält, wobei letztere das Zertifikat mit dem öffentlichen Schlüssel enthält, der zu dem privaten Schlüssel passt, mit dem die JAR-Datei signiert wurde. Um als Empfänger agieren zu können, muss zunächst ein Keystore namens beispielstore2 angelegt werden, in den anschließend das Zertifikat des Senders mit dem Alias-Namen stephan importiert wird. Zum Anlegen des Keystores sind die folgenden Schritte durchzuführen:

  • Wechsel in das Verzeichnis, das die Datei StephanFischer.cer enthält, also jeweils das Zertifikat des Senders.
  • Importierung eines Zertifikats, wobei in diesem Fall ein Keystore automatisch angelegt wird, falls dieser noch nicht existiert.

code 

keytool -import -alias stephan -file StephanFischer.cer -keystore beispielstore2

Da der Keystore noch nicht existiert, wird er angelegt, wobei ein Passwort verlangt wird. In diesem Fall wird das Passwort Fir321ma verwendet, das ansonsten beliebig gewählt werden kann. Das Werkzeug keytool gibt anschließend die Zertifikatsinformation aus und verlangt eine Verifikation, nach der im erfolgreichen Fall das Zertifikat als Trusted Certificate betrachtet wird. Die Ausgabe, die sich hierbei einstellt, ist in Abb. 5-8 angegeben. Analog kann man die Information des Zertifikats extrahieren, indem die folgende Anweisung eingegeben wird:

code 

keytool -printcert -file StephanFischer.cer

Man kann bspw. den Sender fragen, ob die Fingerprints, die Teil des Zertifikats sind, identisch mit denen sind, die der Sender verwendet hat; ob also während der Übertragung keine Modifikation des Zertifikats stattgefunden hat. Üblicherweise führt man diesen Vergleich durch, bevor man ein Zertifikat als Trusted Certificate akzeptiert.

keystore 

Abb. 5.8: Importierung von Zertifikaten

Nachdem das Zertifikat importiert wurde, muss eine Policy-Datei angelegt werden. An dieser Stelle sollen die notwendigen Konzepte nochmals wiederholt werden. Die Policy-Datei wird verwendet, um der empfangenen JAR-Datei die notwendigen Berechtigungen zum Lesen des Dateisystems einzuräumen. Dazu muss die JAR-Datei mit dem privaten Schlüssel signiert sein, der dem öffentlichen Schlüssel des Senders entspricht, der im Keystore des Empfängers abgelegt ist. Die Schritte zum Anlegen der Policy-Datei sind dann die folgenden:

  1. Aufruf des Policy-Tools
  2. Angabe des Keystores
  3. Anlegen eines Policy-Eintrags mit dem Alias-Namen SignedBy
  4. Speichern der Policy-Datei

Nach dem Aufruf des Policy-Tools muss zuerst der zu verwendende Keystore angegeben werden. Hierzu muss unter dem Menüpunkt Edit die Option Change Keystore ausgewählt werden. Es sei daran erinnert, dass der Keystore immer in Form einer URL angegeben werden muss, also bspw. als file:/C:/home/fisch/private/JavaBuch/Buchcode/Kapitel5/beispielstore2.

Anschließend muss ein Eintrag in der Policy-Datei angelegt werden. Dazu ist der Button Add Policy Entry zu betätigen. Zunächst ist anzugeben, von wem der Code signiert wurde. Hierzu ist das SignedBy-Textfeld auszufüllen, bspw. mit dem Namen stephan. Wenn beliebigem Code, der von diesem Sender empfangen wurde, Rechte eingeräumt werden sollen, so ist das Feld CodeBase leer zu lassen, anderenfalls muss hier in Form einer URL eine Datei oder ein Verzeichnis angegeben werden.

Um die notwendigen Berechtigungen zu setzen, wird anschließend der Button Add Permission betätigt. Im nun neu erscheinenden Fenster muss die Art der Berechtigung (im Fall dieses Beispiels FilePermission), der Dateiname, dem die Berechtigung eingeräumt wird (als absoluter Pfadname, nicht als URL), und die Art der Berechtigung (in diesem Fall READ) angegeben werden. Anschließend sollte der Anwender nicht vergessen, die Policy-Datei zu speichern.

Wie bereits  beschrieben wurde, kann die neu generierte Policy-Datei auf zwei Arten in die gesamten Sicherheitsregeln eingebunden werden:

  • Angabe der zusätzlichen Policy-Datei beim Aufruf der Laufzeitumgebung oder
  • Hinzufügen einer Zeile in die Security-Properties-Datei.

An dieser Stelle soll die erste Möglichkeit verwendet werden, die Angabe der Policy-Datei in der Kommandozeilenebene. Hierzu ist die oben genannte Anweisung wie folgt zu erweitern:

code 

java -Djava.security.manager -Djava.security.policy=policy -cp sAusgabe.jar Ausgabe ..\Hallo.txt

Hierbei bezeichnet der neu hinzugekommene Ausdruck -Djava.security.policy=policy die Datei policy im momentan verwendeten Verzeichnis, die die Berechtigungen für Daten des Senders stephan enthält. Ist bei der Erstellung der Policy-Datei kein Fehler aufgetreten, so kann nun die Anwendung mit der oben angegebenen Anweisung problemlos ausgeführt werden.

Datenaustausch

Soll ein wichtiges Dokument elektronisch versendet werden, so sollten die Daten signiert werden. Der Empfänger ist dann in der Lage, die Konsistenz des Dokuments zu überprüfen und festzustellen, ob während der Übertragung Modifikationen aufgetreten sind.

Funktionalität des Senders

Die Signatur eines Dokuments erfolgt nach exakt demselben Schema, das bereits im vorangegangenen Unterkapitel erläutert wurde, also in den folgenden Schritten:

  1. Erzeugung einer JAR-Datei mittels des jar-Tools, die das Dokument enthält.
  2. Erzeugung eines Schlüsselpaars mit Hilfe des Werkzeugs keytool. Optional kann hierbei ein sog. Certificate Signing Request (CSR) für das Zertifikat, das den öffentlichen Schlüssel enthält, generiert werden. Die CSR wird anschließend an eine Certification Authority geschickt, deren Antwort dann importiert werden muss. Die hierzu notwendigen Vorgänge sind im Folgenden angegeben.
  3. Signieren der JAR-Datei, indem das Werkzeug jarsigner und der private Schlüssel verwendet werden.
  4. Exportieren des Zertifikats, indem das Kommando keytool -export verwendet wird. Anschließend werden die signierte JAR-Datei und das Zertifikat an den Empfänger übermittelt.

Wird das Werkzeug keytool dazu verwendet, ein Paar aus privatem und öffentlichem Schlüssel zu erzeugen, so wird ein Eintrag in einem Keystore angelegt, der den privaten Schlüssel und ein selbst signiertes Zertifikat für den öffentlichen Schlüssel enthält. Dieses Vorgehen ist nur dann adäquat, wenn die Empfänger den Sender bereits kennen und diesem vertrauen.

Ein Zertifikat ist aber wesentlich vertrauenswürdiger, wenn es von einer Certification Authority (CA) unterschrieben ist. Um ein Zertifikat von einer CA unterschreiben zu lassen, muss zuerst ein Certificate Signing Request (CSR) erzeugt werden, indem die folgende Anweisung ausgeführt wird:

code 

keytool -certreq -alias alias -file csrDatei

In dieser Anweisung wird alias dazu verwendet, auf den jeweiligen Eintrag im Keystore zuzugreifen, der den privaten Schlüssel und das Zertifikat für den öffentlichen Schlüssel enthält. Der Ausdruck csrDatei bezeichnet den Namen der Datei, die hierbei erzeugt wird.

Anschließend wird diese Datei an eine CA, bspw. an die Firma VeriSign, übermittelt. Die CA authentifiziert dann den Sender und übermittelt ein Zertifikat, das den öffentlichen Schlüssel des Senders in unterschriebener Form authentifiziert. Die Antwort der CA muss im Folgenden importiert werden.

Wird ein Certificate Signing Request an eine Certification Authority übermittelt, so muss das Original im Keystore des Senders durch das Zertifikat, das die CA zurück schickt, ersetzt werden. Dies erfolgt, indem das übermittelte Zertifikat importiert wird. Dazu ist es jedoch zuerst notwendig, dass ein Trusted Certificate angelegt wird, mit dem der öffentliche Schlüssel der CA authentifiziert werden kann.

Zum Anlegen eines Trusted Certificate muss zunächst geprüft werden (bspw. über einen Vergleich der Fingerprints), ob das Zertifikat gültig ist. Ist das empfangene Zertifikat gültig, so kann es dem Keystore mit dem folgenden Kommando hinzugefügt werden:

code 

keytool -import -alias alias -file zertifikat.cer -keystore storename

Mit Hilfe dieses Kommandos wird ein Trusted Certificate erzeugt, dessen Inhalt aus der Datei zertifikat.cer kopiert wird. Der Eintrag im Keystore enthält dann die Daten der Datei zertifikat.cer und wird mit dem angegebenen Alias-Namen referenziert.

Steht ein Trusted Certificate zur Verfügung, so kann die Zertifikatsantwort der CA importiert werden, wobei das bisherige selbst signierte Zertifikat ersetzt werden muss. Die folgende Anweisung übernimmt diese Aufgabe.

code 

keytool -import -keystore beispielstore2 -alias stephan -file antwortDerCA

Funktionalität des Empfängers

Der Empfänger des Dokuments geht in den folgenden Schritten vor:

  1. Importierung des Zertifikats als Trusted Certificate, indem das Kommando keytool -import (siehe vorangegangenes Unterkapitel) verwendet wird.
  2. Verifizierung der Signatur der JAR-Datei, indem das Werkzeug jarsigner verwendet wird.

Nachdem das Zertifikat mit dem öffentlichen Schlüssel des Senders als Trusted Certificate in den Keystore des Empfängers importiert wurde, kann das Werkzeug jarsigner dazu verwendet werden, die Authentizität der Signatur der JAR-Datei zu prüfen. Hierdurch wird sichergestellt, dass die Datei während der Übertragung nicht modifiziert wurde. Möchte der Empfänger bspw. prüfen, ob die Datei sAusgabe.jar kompromittiert wurde, so muss die folgende Anweisung angegeben werden:

code 

jarsigner -verify -verbose -keystore beispielstore2 sAusgabe.jar

keyVerify 

Abb. 5.8: Verifikation von Dokumenten

Es ist zu beachten, dass das Kommando mit der Option -verbose ausgeführt werden muss, damit die notwendige Information auf der Kommandozeilenebene ausgegeben wird. In der in Abb. 5-9 dargestellten Ausgabe ist erkennbar, dass das übertragene Dokument Teil der JAR-Datei ist, die signiert wurde und ferner, dass die Signatur verifiziert wurde. Weiterhin wird angezeigt, dass sich der öffentliche Schlüssel zur Verifikation der Signatur im angegebenen Keystore befindet.

Zusammenfassung

In diesem Unterkapitel wurden die Sicherheitsaspekte von Java in einer detaillierten Form betrachtet, vor allem die Signatur von Daten, Policy-Dateien und die Verwendung von Keystores. Es sei jedoch darauf hingewiesen, dass diese Darstellung auf die Verwendung der Werkzeuge von Java (bspw. jar, jarsigner oder keytool) fokussiert ist. Werden die in Java enthaltenen APIs verwendet, so ist eine wesentlich weitgehendere Funktionalität realisierbar. Diese kann jedoch im Rahmen dieses Buches nicht erläutert werden. Einerseits kann der Leser die notwendigen Funktionen nur verstehen, wenn er ein Experte im Gebiet der Sicherheitstechnik ist. Andererseits wäre eine Darstellung derart umfangreich, dass sie den Rahmen dieses Buches sprengen würde. Nach der Lektüre dieses Teilkapitels sollte der Leser jedoch in der Lage sein, Sicherheitsfunktionen von Java derart verwenden zu können, dass generelle Aspekte der sicheren Datenübertragung mit Hilfe der vorgestellten Werkzeuge ohne Probleme realisiert werden können. Die Datenübertragung selbst ist Thema des folgenden Unterkapitels.


SPNavRight SPNavRight SPNavRight
BuiltByNOF