Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 3. Auflage
 <<    <     >    >>   API  Kapitel 47 - Sicherheit und Kryptographie

47.3 Signierte Applets



In diesem Abschnitt wird beschrieben, wie einem Applet der Zugriff auf geschützte Ressourcen gewährt werden kann. Wir gehen dazu in folgenden Schritten vor:

  1. Zunächst wird ein Applet entwickelt, das auf einige beschränkte Ressourcen zugreift. Ohne weitere Vorkehrungen ist es nicht lauffähig, sondern wird mit einer SecurityException abgebrochen.
  2. Anschließend zeigen wir, wie das Applet mit Hilfe des in Abschnitt 47.1.6 erzeugten Schlüsselpaars signiert wird.
  3. Danach demonstrieren wir die Weitergabe von Zertifikaten.
  4. Schließlich zeigen wir, wie die Sicherheitseinstellungen so angepaßt werden, daß das Applet Zugriff auf die gewünschten Ressourcen erhält.

47.3.1 Ein »unerlaubtes« Applet

Wir wollen uns die Aufgabe stellen, ein Applet zu schreiben, das einige Sicherheitsverstöße begeht. Zunächst soll es eine Datei auf dem lokalen Computer erzeugen und einen Zeitstempel hineinschreiben. Zusätzlich soll es auf einige geschützte System-Properties zugreifen und deren Inhalt in die Datei schreiben. Das Applet hat folgenden Aufbau:

001 /* TrustedApplet.java */
002 
003 import java.awt.*;
004 import java.applet.*;
005 import java.util.*;
006 import java.io.*;
007 
008 public class TrustedApplet
009 extends Applet
010 {
011   static final String ALLOWED_DIR = "c:\\tmp\\applets\\";
012   static final String FNAME       = "TrustedApplet.log";
013   static final String LOGMSG      = "Erzeugt von Applet: ";
014   String msg;
015 
016   public void init()
017   {
018     msg = "Uninitialisiert";
019     FileWriter out = null;
020     try {
021       //Ausgabedatei erzeugen
022       out = new FileWriter(ALLOWED_DIR + FNAME);
023       //Logmessage schreiben
024       out.write(LOGMSG);
025       //Zeitstempel schreiben
026       GregorianCalendar cal = new GregorianCalendar();
027       out.write(cal.get(Calendar.DATE) + ".");
028       out.write((cal.get(Calendar.MONTH) + 1) + ".");
029       out.write(cal.get(Calendar.YEAR) + " ");
030       out.write(cal.get(Calendar.HOUR_OF_DAY) + ":");
031       out.write(cal.get(Calendar.MINUTE) + ":");
032       out.write(cal.get(Calendar.SECOND) + "");
033       out.write(System.getProperty("line.separator"));
034       //System-Properties lesen und in Datei schreiben
035       out.write(getProp("user.name"));
036       out.write(getProp("user.home"));
037       out.write(getProp("user.dir"));
038       //Datei schließen
039       msg = "Alle Sicherheitshuerden ueberwunden!";
040     } catch (Exception e) {
041       msg = e.toString();
042     } finally {
043       if (out != null) {
044         try {
045           out.close();
046         } catch (IOException e) {
047           //silently ignore
048         }
049       }
050     }
051   }
052 
053   public void paint(Graphics g)
054   {
055     g.drawString(msg, 20, 20);
056   }
057 
058   private String getProp(String prop)
059   {
060     return prop + "=" +
061            System.getProperty(prop) +
062            System.getProperty("line.separator");
063   }
064 }
TrustedApplet.java
Listing 47.7: Ein "unerlaubtes" Applet

Es versucht zunächst eine Datei c:\tmp\applets\TrustedApplet.log zu erzeugen. Wenn dies gelingt, instanziert es ein aktuelles GregorianCalendar-Objekt und schreibt dessen Werte in die Datei. Schließlich versucht es, die System-Properties "user.name", "user.home" und "user.dir" zu lesen und deren Werte ebenfalls in die Datei zu schreiben. Sind all diese Versuche erfolgreich, gibt das Applet die Meldung "Alle Sicherheitshuerden ueberwunden!" aus. Tritt eine Ausnahme auf, wird deren Text ausgegeben.

Um das Schreiben der Datei zu ermöglichen, ist ein Verzeichnis c:\tmp\applets anzulegen. Andernfalls gibt es eine IOException - selbst wenn alle Sicherheitshürden genommen sind.

 Hinweis 

Das Applet kann mit folgender HTML-Datei gestartet werden:

001 <html>
002 <head>
003 <title>TrustedApplet Demo</title>
004 </head>
005 
006 <body>
007 <h1>TrustedApplet Demo</h1>
008 
009 <applet
010   code="TrustedApplet.class"
011   width=600
012   height=200
013 >
014 TrustedApplet Demo
015 </applet>
016 
017 </body>
018 </html>
Listing 47.8: Vorläufige HTML-Datei zum Aufruf des unerlaubten Applets

Das Applet kann nun beispielsweise mit dem appletviewer gestartet werden:

appletviewer TrustedApplet.html

Wird es ohne weitere Vorkehrungen gestartet, scheitert es bereits am Erzeugen der Ausgabedatei und gibt eine SecurityException auf dem Bildschirm aus.

47.3.2 Signieren des Applets

Zum Signieren ist es zunächst erforderlich, alle für den Betrieb des Applets nötigen Dateien in ein jar-File zu packen. Signiert wird also nicht eine einzelne .class-Datei, sondern alle Dateien innerhalb des jar-Archivs. Dazu verwenden wir folgendes Kommando:

jar cvf trapp.jar TrustedApplet.class

Jetzt wird ein jar-Archiv trapp.jar erstellt, das die Klassendatei TrustedApplet.class enthält. Dieses Archiv muß nun signiert werden. Dazu steht im JDK das Hilfsprogramm jarsigner zur Verfügung. Es arbeitet kommandozeilenbasiert und wird folgendermaßen aufgerufen:

jarsigner -signedjar outjar injar alias

Die einzelnen Elemente haben folgende Bedeutung:

Nachdem wir in Abschnitt 47.1.6 bereits ein Schlüsselpaar mit dem Alias "hjp3" erzeugt und in der Schlüsseldatenbank abgelegt haben, muß der Aufruf von jarsigner so erfolgen:

C:\--->jarsigner -signedjar strapp.jar trapp.jar hjp3
Enter Passphrase for keystore: hjp3ks
Enter key password for hjp3: hjp3key

Die beiden Paßwörter zum Zugriff auf die Schlüsseldatenbank und den Schlüssel werden auf Nachfrage in der Kommandozeile angegeben. Nachdem das Programm einige Zeit gerechnet hat, erzeugt es das signierte Archiv mit dem Namen strapp.jar.

jarsigner bietet neben den hier erwähnten noch weitere Optionen, auf die wir nicht weiter eingehen wollen. Das Programm wird zusammen mit den anderen Hilfsprogrammen in der Tool-Dokumentation des JDK ausführlich beschrieben.

 Hinweis 

Bisher wurde das Applet direkt aus der .class-Datei geladen. Um es aus einem jar-Archiv zu laden, muß das APPLET-Tag der HTML-Datei um das archive-Argument erweitert werden:

001 <-- TrustedApplet.html -->
002 
003 <html>
004 <head>
005 <title>TrustedApplet Demo</title>
006 </head>
007 
008 <body>
009 <h1>TrustedApplet Demo</h1>
010 
011 <applet
012   archive="strapp.jar"
013   code="TrustedApplet.class"
014   width=600
015   height=200
016 >
017 TrustedApplet Demo
018 </applet>
019 
020 </body>
021 </html>
TrustedApplet.html
Listing 47.9: Aufruf des signierten Applets

Wir könnten das Applet jetzt wie zuvor starten, würden aber immer noch dieselbe SecurityException erhalten. Es ist zwar signiert und das Zertifikat ist auf diesem Rechner bekannt (denn hier wurde es erstellt). Die Policy-Datei ist aber noch nicht angepaßt, und daher lehnt der SecurityManager des JDK die Ausführung der gefährlichen Operationen nach wie vor ab.

 Hinweis 

47.3.3 Ex- und Import von Zertifikaten

Soll das signierte Applet auf anderen Arbeitsplätzen laufen, ist es erforderlich, das Zertifikat des Schlüssels, mit dem es signiert wurde, dort zu installieren. Soll es dagegen nur auf dem Arbeitsplatz laufen, auf dem auch der Schlüssel generiert wurde, ist das nicht erforderlich. Bei der Generierung des Schlüssels wurde ja auch ein (selbstsigniertes) Zertifikat erzeugt.

Um das Zertifikat weitergeben zu können, muß es zunächst unter Verwendung der Option -export von keytool aus der lokalen Schlüsseldatenbank exportiert werden:

keytool -export -alias hjp3 -file hjp3.cert

Es liegt nun in der Datei hjp3.cert und kann auf das Zielsystem kopiert werden. Mit der -import-Option von keytool kann es dort in die Schlüsseldatenbank aufgenommen werden:

C:\--->keytool -import -alias hjp3 -file hjp3.cert
Enter keystore password:  hjp3ks
Owner: CN=Guido Krueger, O=Computer Books, C=de
Issuer: CN=Guido Krueger, O=Computer Books, C=de
Serial number: 38663e2d
Valid from: Sun Dec 26 17:11:25 GMT+01:00 1999 until: Sat Mar 25 17:11:25 GMT+01
:00 2000
Certificate fingerprints:
         MD5:  D5:73:AB:06:25:16:7F:36:27:DF:CF:9D:C9:DE:AD:35
         SHA1: E0:A4:39:65:60:06:48:61:82:5E:8C:47:8A:2B:04:A4:6D:43:56:05
Trust this certificate? [no]:  y
Certificate was added to keystore

Nach dem Aufruf muß zunächst das Paßwort der Schlüsseldatenbank angegeben werden. Dann zeigt das Programm die Eigenschaften des Zertifikats an und erwartet, daß die Informationen bestätigt werden. Anschließend wird das Zertifikat in die Schlüsseldatenbank aufgenommen.

47.3.4 Anpassen der Policy-Datei

Policy-Dateien

Der letzte Schritt besteht darin, die Sicherheitseinstellungen auf dem Zielsystem anzupassen. Applets, die mit dem Zertifikat verifiziert werden können, das unter dem Alias "hjp3" in der Schlüsseldatenbank abgelegt wurde, sollen Dateien im Verzeichnis c:\tmp\applets lesen und schreiben und auf die System-Properties "user.name", "user.home" und "user.dir" zugreifen können.

Die Sicherheitseinstellungen des JDK werden mit Hilfe von Policy-Dateien definiert. Es gibt zwei Stellen im Dateisystem, von denen das JDK sie standardmäßig einliest:

Policy-Dateien können auch an beliebigen anderen Stellen im Dateisystem liegen. In diesem Fall muß beim Aufruf des Java-Interpreters das System-Property "java.security.policy" mit dem Namen der zu verwendenen Policy-Datei angegeben werden. Wäre beispielsweise hjp3policy die zu verwendende Policy-Datei, so müßte der Appletviewer mit der Option "-J-Djava.security.policy=hjp3policy" aufgerufen werden.

 Tip 

Das Format einer Policy-Datei

Policy-Dateien sind zeilenorientierte Textdateien, die mit einem gewöhnlichen Texteditor bearbeitet werden können. Alternativ stellt das JDK ein einfaches GUI-basiertes Hilfsprogramm mit dem Namen policytool zur Verfügung, mit dem Policy-Dateien erstellt und bearbeitet werden können. Auf seine Verwendung wollen wir aber nicht weiter eingehen.

Eine Policy-Datei enthält zwei Arten von Einträgen. Beide sind optional:

Alle Einträge werden mit einem Semikolon beendet. Kommentare können an beliebiger Stelle durch // oder /* ... */ angelegt werden.

Der "keystore"-Eintrag erwartet als Argument einen URL, der auf die Schlüsseldatenbank verweist. Auf einem Windows-98-Einzelplatzsystem sieht er beispielsweise so aus:

keystore "file:/c:/windows/.keystore";

Die "grant"-Einträge haben folgende Syntax:

grant [SignedBy "signer"] [, CodeBase "URL"] {
  permission permission_class [ "target" ] [, "action"]
             [, SignedBy "signers"];
  ...
};

Eine Berechtigung kann wahlweise an einen Unterzeichner oder eine Ladeadresse (oder beide) vergeben werden. Die Option "SignedBy" führt eine Liste von Aliasnamen auf, deren Zertifikate vorhanden sein müssen, damit die Berechtigung gewährt wird. Die Option "CodeBase" spezifiziert die Adresse, von der ein Applet geladen werden darf, um die Berechtigung zu halten. Fehlt die CodeBase-Klausel, wird nur die Unterzeichnerliste verwendet; fehlt die SignedBy-Klausel, ist es nur die Ladeadresse.

Nach einer öffnenden geschweiften Klammer folgen beliebig viele Berechtigungen, die jeweils durch das Schlüsselwort "permission" eingeleitet werden. Anschließend wird der Eintrag mit einer schließenden geschweiften Klammer abgeschlossen. Der zuvor spezifizierte Berechtigte erhält alle Berechtigungen, die zwischen den beiden Klammern angegeben sind.

Jede Berechtigung muß als erstes Argument den Namen einer Berechtigungsklasse angeben. Daneben kann sie zwei weitere Argumente target und action haben, mit denen Details spezifiziert werden. Tabelle 47.1 listet die gebräuchlichsten Berechtigungen und ihre Argumente auf. Details können (und müssen) in dem Dokument "Java Security Architecture" nachgelesen werden, das Bestandteil der JDK-Dokumentation ist.

Klasse Zugriff auf Target Action
java.io.FilePermission Dateien und Verzeichnisse Datei- oder Verzeichnisnamen. Wird als letztes Zeichen ein "*" angegeben, so gilt die Berechtigung für das komplette Verzeichnis. Steht dort ein "-", so gilt sie zusätzlich für alle Unterverzeichnisse. Wird "<<ALL FILES>>" angegeben, gilt die Berechtigung für alle Dateien in allen Verzeichnissen! read, write, delete, execute
java.net.SocketPermission TCP/IP-Verbindungen Hostname oder IP-Adresse, gefolgt von Portnummern-Bereich accept, connect, listen, resolve
java.util.PropertyPermission System-Properties Names des Properties read, write
java.lang.RuntimePermission Die Klasse Runtime exitVM, stopThread, loadLibrary, queuePrintJob, ... -
java.awt.AWTPermission Window-Ressourcen accessClipboard, showWindowWithoutWarningBanner, ... -
java.security.AllPermission Alle Ressourcen - -

Tabelle 47.1: Wichtige Permission-Klassen

Erstellen der Policy-Datei

Nach diesen Vorbemerkungen können wir die Policy-Datei \windows\.java.policy erstellen. Sie hat folgenden Inhalt:

keystore "file:/c:/windows/.keystore";

grant SignedBy "hjp3" {
  permission java.io.FilePermission "c:\\tmp\\applets\\*", "read,write";

  permission java.util.PropertyPermission "user.name", "read";
  permission java.util.PropertyPermission "user.home", "read";
  permission java.util.PropertyPermission "user.dir", "read";
};

Im ersten Eintrag geben wir die Position der Schlüsseldatenbank an, sie liegt im Verzeichnis c:\windows. Anschließend definieren wir die Berechtigungen für alle Applets, die mit dem Zertifikat "hjp3" signiert wurden. Zunächst erhalten sie Schreib- und Leseberechtigung im Verzeichnis c:\tmp\applets. Dort können sie ohne weitere Einschränkungen Dateien anlegen, überschreiben und lesen. Zusätzlich erlauben wir den so signierten Applets, die drei System-Properties "user.name", "user.home" und "user.dir" zu lesen.

Nun läßt sich unser signiertes Applet ohne SecurityException aufrufen und gibt die erlösende Meldung "Alle Sicherheitshuerden ueberwunden" aus. Im Verzeichnis c:\tmp\applets sollte sich anschließend eine Datei TrustedApplet.log befinden und etwa folgenden Inhalt haben:

Erzeugt von Applet: 30.12.1999 20:50:40
user.name=Guido Krüger
user.home=C:\WINDOWS
user.dir=C:\arc\doku\hjp3\misc

47.3.5 Die Klasse SecurityManager

Die Prüfung der Zugriffsberechtigungen wird mit Hilfe der Klasse SecurityManager aus dem Paket java.lang vorgenommen. Der SecurityManager ist ein Objekt, das entweder gar nicht oder genau einmal im laufenden Java-Programm vorhanden ist. Nach der ersten Instanzierung kann es nicht mehr geändert oder entfernt werden.

Zugriffe auf den SecurityManager sind an den Stellen der Laufzeitbibliothek eingebaut, an denen auf gefährliche Ressourcen zugegriffen wird. Ein Beispiel aus der Klasse FileInputStream sieht etwa so aus:

...
SecurityManager security = System.getSecurityManager();
if (security != null) {
  security.checkRead(name);
}
//Gefährlicher Code
...

Zunächst wird geprüft, ob ein SecurityManager installiert wurde. Ist das nicht der Fall, fährt das Programm ohne Einschränkung fort. Gibt es dagegen einen SecurityManager, wird dessen checkRead-Methode aufgerufen. Sie löst eine SecurityException aus, wenn die gewünschte Berechtigung fehlt. Der Code hinter dem checkRead wird in diesem Fall nicht mehr erreicht. Ist die Berechtigung dagegen vorhanden, wird checkRead ohne weitere Aktionen beendet und der dahinter liegende Code ausgeführt.

Applets besitzen grundsätzlich einen SecurityManager. Der AppletViewer bzw. Web-Browser sorgen während der Initialisierung für dessen Instanzierung. Applikationen dagegen haben normalerweise keinen SecurityManager (das ist der Grund, weshalb in Applikationen alle gefährlichen Operationen erlaubt sind). Soll eine Applikation einen SecurityManager erhalten, so kann sie entweder mit der Option "-Djava.security.manager" gestartet werden, oder der SecurityManager kann im Programm selbst installiert werden:

...
System.setSecurityManager(new SecurityManager());
...

Das folgende Beispielprogramm gibt den Inhalt des System-Properties "user.name" aus. Normalerweise kann es ohne Fehler ausgeführt werden:

001 /* SecuMgrTest.java */
002 
003 public class SecuMgrTest
004 {
005   public static void main(String[] args)
006   {
007     System.out.println(
008       "user.name is " + System.getProperty("user.name")
009     );
010   }
011 }
SecuMgrTest.java
Listing 47.10: Ausgeben des System-Properties "user.name"

Läuft es dagegen unter Kontrolle eines SecurityManagers, so führt der Aufruf zu einer SecurityException:

C:\--->java -Djava.security.manager SecuMgrTest
Exception in thread "main"
  java.security.AccessControlException: access denied
  (java.util.PropertyPermission user.name read)
        at java.security.AccessControlContext.checkPermission(
          AccessControlContext.java:276)
        at java.security.AccessController.checkPermission(
          AccessController.java:403)
        at java.lang.SecurityManager.checkPermission(
          SecurityManager.java:549)
        at java.lang.SecurityManager.checkPropertyAccess(
          SecurityManager.java:1242)
        at java.lang.System.getProperty(System.java:555)
        at Test1.main(SecuMgrTest.java:7)

Bei Bedarf kann man Applikationen auf diese Weise mit denselben Sicherheitsmechanismen ausstatten wie Applets. jar-Dateien, aus denen Applikationen geladen werden, lassen sich ebenso signieren wie solche, aus denen Applets geladen werden. Die in der Policy-Datei definierten Rechte gelten dann für die daraus gestartete Applikation.


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0.1
 <<    <     >    >>   API  © 1998-2003 Guido Krüger, http://www.javabuch.de