From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100303.html | 1660 ++++++++++++++++++++ 1 file changed, 1660 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100303.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100303.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100303.html b/Master/Reference Architectures and Patterns/hjp5/html/k100303.html new file mode 100644 index 0000000..c5cb46a --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100303.html @@ -0,0 +1,1660 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 48 - Sicherheit und Kryptographie +
+
+ + + + +

48.1 Kryptografische Grundlagen

+
+ +
+ + + + +

48.1.1 Wichtige Begriffe

+ +

+Thema dieses Kapitels ist es, die in Java verfügbaren Sicherheitsmechanismen +vorzustellen. Wir werden dabei zunächst auf allgemeine Konzepte +aus dem Gebiet der Kryptographie und ihre Implementierung in Java +eingehen. Anschließend werden die eingebauten Sicherheitsmechanismen +von Java vorgestellt. Zum Abschluss zeigen wir, wie signierte Applets +erstellt und verwendet werden, und wie mit ihrer Hilfe eine fein differenzierte +Sicherheitspolitik etabliert werden kann. Zunächst sollen allerdings +wichtige Begriffe erläutert werden, die für das Verständnis +der nachfolgenden Abschnitte von Bedeutung sind. + +

+Angenommen, ein Sender will eine Nachricht an einen Empfänger +übermitteln. Soll das geschehen, ohne dass ein Dritter, dem die +Nachricht in die Hände fallen könnte, diese entziffern kann, +könnte sie verschlüsselt +werden. Der ursprüngliche Nachrichtentext (der als Klartext +bezeichnet wird) wird dabei mit Hilfe eines dem Sender bekannten Verfahrens +unkenntlich gemacht. Das als Schlüsseltext +bezeichnete Ergebnis wird an den Empfänger übermittelt und +mit Hilfe eines ihm bekannten Verfahrens wieder in den Klartext zurückverwandelt +(was als entschlüsseln bezeichnet +wird). +

+ + +

+ +

+Abbildung 48.1: Verschlüsseln einer Nachricht

+ +

+Solange der Algorithmus zum Entschlüsseln geheim bleibt, ist +die Nachricht sicher. Selbst wenn sie auf dem Übertragungsweg +entdeckt wird, kann kein Dritter sie entschlüsseln. Wird das +Entschlüsselungsverfahren dagegen entdeckt, kann die Nachricht +(und mit ihr alle anderen Nachrichten, die mit demselben Verfahren +verschlüsselt wurden) entziffert werden. + +

+Um den Schaden durch das Entdecken eines Verschlüsselungsverfahrens +gering zu halten, werden die Verschlüsselungsalgorithmen in aller +Regel parametrisiert. Dazu wird beim Verschlüsseln eine als Schlüssel +bezeichnete Ziffern- oder Zeichenfolge angegeben, mit der die Nachricht +verschlüssel wird. Der Empfänger benötigt dann zusätzlich +zur Kenntnis des Verfahrens noch den vom Sender verwendeten Schlüssel, +um die Nachricht entziffern zu können. + +

+Die Wissenschaft, die sich mit dem Verschlüsseln und Entschlüsseln +von Nachrichten und eng verwandten Themen beschäftigt, wird als +Kryptographie bezeichnet. Liegt der +Schwerpunkt mehr auf dem Entschlüsseln, (insbesondere dem Entziffern +geheimer Botschaften), wird dies als Kryptoanalyse +bezeichnet. Die Kryptologie schließlich +bezeichnet den Zweig der Mathematik, der sich mit den formal-mathematischen +Aspekten der Kryptographie und Kryptoanalyse beschäftigt. + + + + +

48.1.2 Einfache Verschlüsselungen

+ + + + +

Substitution

+ +

+Seit dem Altertum sind einfache Verschlüsselungsverfahren bekannt. +Zu ihnen zählen beispielsweise die Substitutions-Verschlüsselungen, +bei denen einzelne Buchstaben systematisch durch andere ersetzt werden. +Angenommen, Klartexte bestehen nur aus den Buchstaben A bis Z, so +könnte man sie dadurch verschlüsseln, dass jeder Buchstabe +des Klartextes durch den Buchstaben ersetzt wird, der im Alphabet +um eine feste Anzahl Zeichen verschoben ist. Als Schlüssel k +kann beispielsweise die Länge der Verschiebung verwendet werden. +Ist k beispielsweise 3, so würde jedes A durch ein D, +jedes B durch ein E, jedes W durch ein Z, jedes X durch ein A usw. +ersetzt werden. + +

+Dieses einfache Verfahren wurde beispielweise bereits von Julius Cäsar +verwendet, um seinen Generälen geheime Nachrichten zu übermitteln. +Es wird daher auch als Cäsarische Verschlüsselung +bezeichnet. Das folgende Listing zeigt eine einfache Implementierung +dieses Verfahrens, bei dem Schlüssel und Klartext als Argument +übergeben werden müssen: + + +

+ + + + + +
+ +
+001 /* Listing4801.java */
+002 
+003 public class Listing4801
+004 {
+005   public static void main(String[] args)
+006   {
+007     int key = Integer.parseInt(args[0]);
+008     String msg = args[1];
+009     for (int i = 0; i < msg.length(); ++i) {
+010       int c = (msg.charAt(i) - 'A' + key) % 26 + 'A';
+011       System.out.print((char)c);
+012     }
+013   }
+014 }
+
+
+Listing4801.java
+ +Listing 48.1: Verschlüsselung durch Substitution

+ +

+Um die Nachricht zu entschlüsseln, verwendet der Empfänger +dasselbe Verfahren, allerdings mit dem Schlüssel 26 - k: + +

+--->java Test2 3 HALLO
+KDOOR
+--->java Test2 23 KDOOR
+HALLO
+
+ + + + + +

Exklusiv-ODER

+ +

+Ein ähnlich weitverbreitetes Verfahren besteht darin, jedes Zeichen +des Klartexts mit Hilfe des Exklusiv-ODER-Operators mit dem Schlüssel +zu verknüpfen. Durch dessen Anwendung werden alle Bits invertiert, +die zu einem gesetztem Bit im Schlüssel korrespondieren, alle +anderen bleiben unverändert. Das Entschlüsseln erfolgt durch +erneute Anwendung des Verfahrens mit demselben Schlüssel. +

+ + + + + + + + + +
+ +

+Ein Verfahren, bei dem Ver- und Entschlüsselung mit demselben +Algorithmus und Schlüssel durchgeführt werden, wird als +symmetrische Verschlüsselung bezeichnet.

+ + + + +
 Hinweis 
+
+ +

+Eine einfache Implementierung der Exklusiv-ODER-Verschlüsselung +zeigt folgendes Listing: + + +

+ + + + + +
+ +
+001 /* Listing4802.java */
+002 
+003 public class Listing4802
+004 {
+005   public static void main(String[] args)
+006   {
+007     int key = Integer.parseInt(args[0]);
+008     String msg = args[1];
+009     for (int i = 0; i < msg.length(); ++i) {
+010       System.out.print((char)(msg.charAt(i) ^ key));
+011     }
+012   }
+013 }
+
+
+Listing4802.java
+ +Listing 48.2: Verschlüsselung mit Exklusiv-ODER

+ +

+Ein Anwendungsbeispiel könnte so aussehen: + +

+--->java Test2 65 hallo
+) --.
+--->java Test2 65 ") --."
+hallo
+
+ +

+ + + + + + + + + + + +
+ +

+Daß die Rückkonvertierung über die Kommandozeile hier +geklappt hat, liegt daran, dass die Verschlüsselung keine nicht-darstellbaren +Sonderzeichen produziert hat (der Schlüssel 65 kippt lediglich +2 Bits in jedem Zeichen). Im allgemeinen sollte der zu ver- oder entschlüsselnde +Text aus einer Datei gelesen und das Resultat auch wieder in eine +solche geschrieben werden. Dann können alle 256 möglichen +Bitkombinationen je Byte zuverlässig gespeichert und übertragen +werden.

+ + + + +
 Warnung 
+
+ + + + +

Vorsicht!

+ +

+Derart einfache Verschlüsselungen wie die hier vorgestellten +sind zwar weit verbreitet, denn sie sind einfach zu implementieren. +Leider bieten sie aber nicht die geringste Sicherheit gegen ernsthafte +Krypto-Attacken. Einige der in letzter Zeit bekannt gewordenen (und +für die betroffenen Unternehmen meist peinlichen, wenn nicht +gar kostspieligen) Fälle von Einbrüchen in Softwaressysteme +waren darauf zurückzuführen, dass zu einfache Sicherheitssysteme +verwendet wurden. + +

+Wir wollen diesen einfachen Verfahren nun den Rücken zuwenden, +denn seit dem JDK 1.2 gibt es in Java Möglichkeiten, professionelle +Sicherheitskonzepte zu verwenden. Es ist ein großer Vorteil +der Sprache, dass auf verschiedenen Ebenen Sicherheitsmechanismen +fest eingebaut wurden, und eine missbräuchliche Anwendung der +Sprache erschwert wird. In den folgenden Abschnitten werden wir die +wichtigsten dieser Konzepte vorstellen. +

+ + + + + + + + + +
+ +

+Allerdings sollte dieser Abschnitt nicht als umfassende Einführung +in die Grundlagen der Kryptographie missverstanden werden. Das Thema +ist ausgesprochen vielschichtig, mathematisch anspruchsvoll, und es +erfordert in seiner Detailfülle weitaus mehr Raum als hier zur +Verfügung steht. Wir werden neue Begriffe nur soweit einführen, +wie sie für das Verständnis der entsprechenden Abschnitte +erforderlich sind. Für Details sei auf weiterführende Literatur +verwiesen. Ein sehr gelungenes Buch ist »Applied Cryptography« +von Bruce Schneier. Es bietet einen umfassenden +und dennoch verständlichen Einblick in die gesamte Materie und +ist interessant zu lesen. Gleichermaßen unterhaltsam wie lehrreich +ist auch die in »Geheime Botschaften« von Simon Singh dargestellte +Geschichte der Kryptographie.

+ + + + +
 Hinweis 
+
+ + + + +

48.1.3 Message Digests

+ +

+Ein Message Digest ist eine Funktion, die zu einer gegebenen +Nachricht eine Prüfziffer berechnet. Im Gegensatz zu ähnlichen +Verfahren, die keine kryptografische Anwendung haben (siehe z.B. hashCode +in Abschnitt 8.1.2), muss +ein Message Digest zusätzlich folgende Eigenschaften besitzen: +

+ +

+Ein Message Digest wird daher auch als Einweg-Hashfunktion +bezeichnet. Er ist meist 16 oder 20 Byte lang und kann als eine Art +komplizierte mathematische Zusammenfassung der Nachricht angesehen +werden. Message Digests haben Anwendungen im Bereich digitaler Unterschriften +und bei der Authentifizierung. Allgemein gesprochen werden sie dazu +verwendet, sicherzustellen, dass eine Nachricht nicht verändert +wurde. Bevor wir auf diese Anwendungen in den nächsten Abschnitten +zurückkommen, wollen wir uns ihre Implementierung im JDK 1.2 +ansehen. + +

+Praktisch alle wichtigen Sicherheitsfunktionen sind im Paket java.security +oder einem seiner Unterpakete untergebracht. Ein Message Digest wird +durch die Klasse MessageDigest +implementiert. Deren Objekte werden nicht direkt instanziert, sondern +mit der Methode getInstance +erstellt: +

+ + + + + +
+ +
+public static MessageDigest getInstance(String algorithm)
+  throws NoSuchAlgorithmException
+
+
+
+java.security.MessageDigest
+ +

+Als Argument wird dabei die Bezeichnung des gewünschten Algorithmus +angegeben. Im JDK 1.2 sind beispielsweise folgende Angaben möglich: +

+ +

+Nachdem ein MessageDigest-Objekt +erzeugt wurde, bekommt es die Daten, zu denen die Prüfziffer +berechnet werden soll, in einzelnen Bytes oder Byte-Arrays durch fortgesetzten +Aufruf der Methode update +übergeben: +

+ + + + + +
+ +
+public void update(byte input)
+public void update(byte[] input)
+public void update(byte[] input, int offset, int len)
+
+
+
+java.security.MessageDigest
+ +

+Wurden alle Daten übergeben, kann durch Aufruf von digest +das Ergebnis ermittelt werden: +

+ + + + + +
+ +
+public byte[] digest()
+
+
+
+java.security.MessageDigest
+ +

+Zurückgegeben wird ein Array von 16 bzw. 20 Byte (im Falle anderer +Algorithmen möglicherweise auch andere Längen), in dem der +Message Digest untergebracht ist. Ein Aufruf führt zudem dazu, +dass der Message Digest zurückgesetzt, also auf den Anfangszustand +initialisiert, wird. + +

+Das folgende Listing zeigt, wie ein Message Digest zu einer beliebigen +Datei erstellt wird. Sowohl Algorithmus als auch Dateiname werden +als Kommandozeilenargumente übergeben: + + +

+ + + + + +
+ +
+001 /* Listing4803.java */
+002 
+003 import java.io.*;
+004 import java.security.*;
+005 
+006 public class Listing4803
+007 {
+008   /**
+009    * Konvertiert ein Byte in einen Hex-String.
+010    */
+011   public static String toHexString(byte b)
+012   {
+013     int value = (b & 0x7F) + (b < 0 ? 128 : 0);
+014     String ret = (value < 16 ? "0" : "");
+015     ret += Integer.toHexString(value).toUpperCase();
+016     return ret;
+017   }
+018 
+019   public static void main(String[] args)
+020   {
+021     if (args.length < 2) {
+022       System.out.println(
+023         "Usage: java Listing4803 md-algorithm filename"
+024       );
+025       System.exit(0);
+026     }
+027     try {
+028       //MessageDigest erstellen
+029       MessageDigest md = MessageDigest.getInstance(args[0]);
+030       FileInputStream in = new FileInputStream(args[1]);
+031       int len;
+032       byte[] data = new byte[1024];
+033       while ((len = in.read(data)) > 0) {
+034         //MessageDigest updaten
+035         md.update(data, 0, len);
+036       }
+037       in.close();
+038       //MessageDigest berechnen und ausgeben
+039       byte[] result = md.digest();
+040       for (int i = 0; i < result.length; ++i) {
+041         System.out.print(toHexString(result[i]) + " ");
+042       }
+043       System.out.println();
+044     } catch (Exception e) {
+045       System.err.println(e.toString());
+046       System.exit(1);
+047     }
+048   }
+049 }
+
+
+Listing4803.java
+ +Listing 48.3: Erstellen eines Message Digests

+

+ + + + + + + + + +
+ +

+Im Paket java.security +gibt es zwei Klassen, die einen Message Digest mit einem Stream kombinieren. +DigestInputStream +ist ein Eingabe-Stream, der beim Lesen von Bytes parallel deren Message +Digest berechnet; DigestOutputStream +führt diese Funktion beim Schreiben aus. Beide übertragen +die eigentlichen Bytes unverändert und können dazu verwendet +werden, in einer Komposition von Streams »nebenbei« einen +Message Digest zu berechnen.

+ + + + +
 Hinweis 
+
+ + + + +

Authentifizierung

+ +

+Ein wichtiges Anwendungsgebiet von Message Digests ist die Authentifizierung, +d.h. die Überprüfung, ob die Person oder Maschine, mit der +kommuniziert werden soll, tatsächlich »echt« ist (also +die ist, die sie vorgibt zu sein). Eine Variante, bei der ein Anwender +sich mit einem Benutzernamen und Paßwort autorisiert, kann mit +Hilfe eines Message Digests in folgender Weise realisiert werden: +

+ +

+Bemerkenswert daran ist, dass das System nicht die Paßwörter +selbst speichert, auch nicht in verschlüsselter Form. Ein Angriff +auf die Benutzerdatenbank mit dem Versuch, gespeicherte Paßwörter +zu entschlüsseln, ist daher nicht möglich. Eine bekannte +(und leider schon oft erfolgreich praktizierte) Methode des Angriffs +besteht allerdings darin, Message Digests zu allen Einträgen +in großen Wörterbüchern berechnen zu lassen, und sie +mit den Einträgen der Benutzerdatenbank zu vergleichen. Das ist +einer der Gründe dafür, weshalb als Paßwörter +niemals Allerweltsnamen oder einfache, in Wörterbüchern +verzeichnete, Begriffe verwendet werden sollten. + + + + +

»Unwissende« Beweise

+ +

+Eine weitere Anwendung von Message Digests besteht darin, die Existenz +von Geheimnissen oder den Nachweis der Kenntnis bestimmter Sachverhalte +nachzuweisen, ohne deren Inhalt preiszugeben - selbst nicht eigentlich +vertrauenswürdigen Personen. Dies wird in Bruce Schneier's Buch +als Zero-Knowledge Proof bezeichnet +und funktioniert so: +

+ +

+Das Geheimnis ist nicht veröffentlicht, der Nachweis für +seine Existenz zum Zeitpunkt X aber erbracht. Muß A Jahre später +die Existenz dieser Informationen nachweisen, holt es die Diskette +mit dem Geheimnis aus dem Tresor, berechnet den Message Digest erneut +und zeigt dessen Übereinstimmung mit dem seinerzeit in der Zeitung +veröffentlichten. + + + + +

Fingerprints

+ +

+Eine weitere Anwendung von Message Digests besteht im Erstellen von +Fingerprints (also digitalen Fingerabdrücken) zu öffentlichen +Schlüsseln (was das genau ist, wird in Abschnitt 48.1.5 +erklärt). Um die Korrektheit eines öffentlichen Schlüssels +nachzuweisen, wird daraus ein Message Digest berechnet und als digitaler +Fingerabdruck an prominenter Stelle veröffentlicht (beispielsweise +in den Signaturen der E-Mails des Schlüsselinhabers). + +

+Soll vor der Verwendung eines öffentlichen Schlüssel überprüft +werden, ob dieser auch wirklich dem gewünschten Inhaber gehört, +ist lediglich der (durch das Schlüsselverwaltungsprogramm adhoc +berechnete) Fingerprint des öffentlichen Schlüssels mit +dem in der E-Mail veröffentlichten zu vergleichen. Stimmen beide +überein, erhöht sich das Vertrauen in die Authentizität +des öffentlichen Schlüssels und er kann verwendet werden. +Stimmen sie nicht überein, sollte der Schlüssel auf keinen +Fall verwendet werden. Wir werden in Abschnitt 48.1.7 +noch einmal auf diese Problematik zurückkommen. + + + + +

48.1.4 Kryptografische Zufallszahlen

+ +

+Zufallszahlen wurden bereits in Abschnitt 16.1 +vorgestellt. In kryptografischen Anwendungen werden allerdings bessere +Zufallszahlengeneratoren benötigt, als in den meisten Programmiersprachen +implementiert sind. Einerseits sollte die Verteilung der Zufallszahlen +besser sein, andererseits wird eine größere Periodizität +gefordert (das ist die Länge der Zahlensequenz, nach der sich +eine Folge von Zufallszahlen frühestens wiederholt). Zudem muss +die nächste Zahl der Folge praktisch unvorhersagbar sein - selbst +wenn deren Vorgänger bekannt sind. +

+ + + + + + + + + +
+ +

+Es ist bekannt, dass sich mit deterministischen Maschinen (wie Computerprogramme +es beispielsweise sind) keine echten Zufallszahlen erzeugen +lassen. Eigentlich müssten wir daher von Pseudo-Zufallszahlen +sprechen, um darauf hinzuweisen, dass unsere Zufallszahlengeneratoren +stets deterministische Zahlenfolgen erzeugen. Mit der zusätzlichen +Forderung kryptografischer Zufallszahlen, praktisch unvorhersagbare +Zahlenfolgen zu generieren, wird diese Unterscheidung an dieser Stelle +unbedeutend. Tatsächlich besteht der wichtigste Unterschied zu +»echten« Zufallsgeneratoren nur noch darin, dass deren Folgen +nicht zuverlässig reproduziert werden können (was +bei unseren Pseudo-Zufallszahlen sehr wohl der Fall ist). Wir werden +im folgenden daher den Begriff »Zufallszahl« auch dann verwenden, +wenn eigentlich »Pseudo-Zufallszahl« gemeint ist.

+ + + + +
 Hinweis 
+
+ +

+Die Klasse SecureRandom +des Pakets java.security +implementiert einen Generator für kryptografische Zufallszahlen, +der die oben genannten Eigenschaften besitzt. Er wird durch Aufruf +der Methode getInstance +ähnlich instanziert wie ein Message Digest: +

+ + + + + +
+ +
+public static SecureRandom getInstance(String algorithm)
+  throws NoSuchAlgorithmException
+
+
+
+java.security.MessageDigest
+ +

+Als Algorithmus ist beispielsweise »SHA1PRNG« im JDK 1.2 +implementiert. Hierbei entstehen die Zufallszahlen aus der Berechnung +eines Message Digests für eine Pseudonachricht, die aus einer +Kombination aus Initialwert und fortlaufendem Zähler besteht. +Die Klasse SecureRandom +stellt weiterhin die Methoden setSeed +und nextBytes +zur Verfügung: +

+ + + + + +
+ +
+public void setSeed(long seed)
+
+public void nextBytes(byte[] bytes)
+
+
+
+java.security.MessageDigest
+ +

+Mit setSeed +wird der Zufallszahlengenerator initialisiert. Die Methode sollte +nach der Konstruktion einmal aufgerufen werden, um den Initialwert +festzulegen (andernfalls macht es der Generator selbst). Gleiche Initialwerte +führen auch zu gleichen Folgen von Zufallszahlen. Mit nextBytes +wird eine beliebig lange Folge von Zufallszahlen erzeugt und in dem +als Argument übergebenen Byte-Array zurückgegeben. + +

+Das folgende Listing instanziert einen Zufallszahlengenerator und +erzeugt zehn Folgen zu je acht Bytes Zufallszahlen, die dann auf dem +Bildschirm ausgegeben werden: + + +

+ + + + + +
+ +
+001 /* Listing4804.java */
+002 
+003 import java.security.*;
+004 
+005 public class Listing4804
+006 {
+007   /**
+008    * Konvertiert ein Byte in einen Hex-String.
+009    */
+010   public static String toHexString(byte b)
+011   {
+012     int value = (b & 0x7F) + (b < 0 ? 128 : 0);
+013     String ret = (value < 16 ? "0" : "");
+014     ret += Integer.toHexString(value).toUpperCase();
+015     return ret;
+016   }
+017 
+018   public static void main(String[] args)
+019   {
+020     try {
+021       //Zufallszahlengenerator erstellen
+022       SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
+023       byte[] data = new byte[8];
+024       //Startwert initialisieren
+025       rand.setSeed(0x123456789ABCDEF0L);
+026       for (int i = 0; i < 10; ++i) {
+027         //Zufallszahlen berechnen
+028         rand.nextBytes(data);
+029         //Ausgeben
+030         for (int j = 0; j < 8; ++j) {
+031           System.out.print(toHexString(data[j]) + " ");
+032         }
+033         System.out.println();
+034       }
+035     } catch (Exception e) {
+036       System.err.println(e.toString());
+037       System.exit(1);
+038     }
+039   }
+040 }
+
+
+Listing4804.java
+ +Listing 48.4: Erzeugen kryptografischer Zufallszahlen

+ + + + +

48.1.5 Public-Key-Verschlüsselung

+ +

+Eines der Hauptprobleme bei der Anwendung symmetrischer Verschlüsselungen +ist das der Schlüsselübertragung. Eine verschlüsselte +Nachricht kann nämlich nur dann sicher übertragen werden, +wenn der Schlüssel auf einem sicheren Weg vom Sender zum Empfänger +gelangt. Je nach räumlicher, technischer oder organisatorischer +Distanz zwischen beiden Parteien kann das unter Umständen sehr +schwierig sein. + +

+Mit der Erfindung der Public-Key-Kryptosysteme wurde dieses Problem +Mitte der siebziger Jahre entscheidend entschärft. Bei einem +solchen System wird nicht ein einzelner Schlüssel verwendet, +sondern diese treten immer paarweise auf. Einer der Schlüssel +ist öffentlich und dient dazu, Nachrichten zu verschlüsseln. +Der anderen Schlüssel ist privat. Er dient dazu, mit dem öffentlichen +Schlüssel verschlüsselte Nachrichten zu entschlüsseln. + +

+Das Schlüsselübertragungsproblem wird nun dadurch gelöst, +dass ein potentieller Empfänger verschlüsselter Nachrichten +seinen öffentlichen Schlüssel an allgemein zugänglicher +Stelle publiziert. Seinen privaten Schlüssel hält er dagegen +geheim. Will ein Sender eine geheime Nachricht an den Empfänger +übermitteln, verwendet er dessen allgemein bekannten öffentlichen +Schlüssel und überträgt die verschlüsselte Nachricht +an den Empfänger. Nur mit Hilfe seines privaten Schlüssels +kann dieser nun die Nachricht entziffern. + +

+Das Verfahren funktioniert natürlich nur, wenn der öffentliche +Schlüssel nicht dazu taugt, die mit ihm verschlüsselte Nachricht +zu entschlüsseln. Auch darf es nicht möglich sein, mit vertretbarem +Aufwand den privaten Schlüssel aus dem öffentlichen herzuleiten. +Beide Probleme sind aber gelöst, und es gibt sehr leistungsfähige +und sichere Verschlüsselungsverfahren, die auf dem Prinzip der +Public-Key-Kryptographie beruhen. Bekannte Beispiele für solche +Systeme sind RSA (benannt nach ihren +Erfindern Rivest, Shamir und Adleman) und DSA +(Digital Signature Architecture). + +

+Asymmetrische Kryptosysteme haben meist den Nachteil, sehr viel langsamer +zu arbeiten als symmetrische. In der Praxis kombiniert man daher beide +Verfahren und kommt so zu hybriden Kryptosystemen . +Um eine geheime Nachricht von A nach B zu übertragen, wird dabei +in folgenden Schritten vorgegangen: +

+ +

+Fast alle Public-Key-Kryptosysteme arbeiten in dieser Weise als Hybridsysteme. +Andernfalls würde das Ver- und Entschlüsseln bei großen +Nachrichten viel zu lange dauern. Ein bekanntes Beispiel für +ein solches System ist PGP (Pretty +Good Privacy) von Phil Zimmermann. +Es wird vorwiegend beim Versand von E-Mails verwendet und gilt als +sehr sicher. Freie Implementierungen stehen für viele Plattformen +zu Verfügung. +

+ + + + + + + + + +
+ +

+Das Ver- und Entschlüsseln von Daten mit Hilfe von asymmetrischen +Verfahren war bis zur Version 1.3 nicht im JDK enthalten. Zwar gab +es als Erweiterung zum JDK die JCE +(JAVA Cryptography Extension), doch +diese durfte nur in den USA und Kanada verwendet werden. Mit dem JDK +1.4 wurden die JCE, sowie die Java Secure Socket Extension +(JSSE) und der Java Authentication +and Authorization Service (JAAS) +fester Bestandteil des JDK. Dennoch gibt es nach wie vor einige Einschränkungen +in der Leistungsfähigkeit der einzelnen Pakete, die auf US-Exportbeschränkungen +zurückzuführen sind. Details können in der Dokumentation +zum JDK 1.4 oder neueren Versionen nachgelesen werden.

+ + + + +
 Hinweis 
+
+ + + + +

48.1.6 Digitale Unterschriften

+ +

+Ein großer Vorteil der Public-Key-Kryptosysteme ist es, dass +sie Möglichkeiten zum Erstellen und Verifizieren von digitalen +Unterschriften bieten. Eine digitale Unterschrift besitzt folgende +wichtige Eigenschaften: +

+ +

+Beide Eigenschaften sind für den elektronischen Datenverkehr +so fundamental wie die Verschlüsselung selbst. Technisch basieren +sie darauf, dass die Funktionsweise eines Public-Key-Kryptosystems +sich umkehren läßt. Daß es also möglich ist, +Nachrichten, die mit einem privaten Schlüssel verschlüsselt +wurden, mit Hilfe des korrespondierenden öffentlichen Schlüssels +zu entschlüsseln. + +

+Im Prinzip funktioniert eine digitale Unterschrift so: + +

+Will A eine Nachricht signieren, so verschlüsselt er sie mit +seinem privaten Schlüssel. Jeder, der im Besitz des öffentlichen +Schlüssel von A ist, kann sie entschlüsseln. Da nur A seinen +eigenen privaten Schlüssel kennt, muss die Nachricht von +ihm stammen. Da es keinem Dritten möglich ist, die entschlüsselte +Nachricht zu modifizieren und sie erneut mit dem privaten Schlüssel +von A zu verschlüsseln, ist auch die Integrität der Nachricht +sichergestellt. Den Vorgang des Überprüfens der Integrität +und Authentizität bezeichnet man als Verifizieren einer +digitalen Unterschrift. + +

+In der Praxis sind die Dinge wieder einmal etwas komplizierter, denn +die Langsamkeit der asymmetrischen Verfahren erfordert eine etwas +aufwändigere Vorgehensweise. Statt die komplette Nachricht zu +verschlüsseln, berechnet A zunächst einen Message Digest +der Nachricht. Diesen verschlüsselt A mit seinem privaten Schlüssel +und versendet ihn als Anhang zusammen mit der Nachricht. Ein Empfänger +wird die Nachricht lesen, ihren Message Digest bilden, und diesen +dann mit dem (mit Hilfe des öffentlichen Schlüssels von +A entschlüsselten) Original-Message-Digest vergleichen. Stimmen +beide überein, ist die Signatur gültig. Die Nachricht stammt +dann sicher von A und wurde nicht verändert. Stimmen sie nicht +überein, wurde sie ver- oder gefälscht. + +

+Das JDK stellt Klassen zum Erzeugen und Verifizieren digitaler Unterschriften +zur Verfügung. Wir wollen uns beide Verfahren in den folgenden +Abschnitten ansehen. Zuvor wird allerdings ein Schlüsselpaar +benötigt, dessen Generierung im nächsten Abschnitt besprochen +wird. + + + + +

Erzeugen und Verwalten von Schlüsseln mit dem JDK

+ +

+Um digitale Unterschriften erzeugen und verifizieren zu können, +müssen Schlüsselpaare erzeugt und verwaltet werden. Seit +dem JDK 1.2 wird dazu eine Schlüsseldatenbank verwendet, auf +die mit Hilfe des Hilfsprogramms keytool +zugegriffen werden kann. keytool +kann Schlüsselpaare erzeugen, in der Datenbank speichern und +zur Bearbeitung wieder herausgeben. Zudem besitzt es die Fähigkeit, +Zertifikate (siehe Abschnitt 48.1.7) +zu importieren und in der Datenbank zu verwalten. Die Datenbank hat +standardmäßig den Namen ».keystore« +und liegt im Home-Verzeichnis des angemeldeten Benutzers (bzw. im +Verzeichnis \windows eines Windows-95/98-Einzelplatzsystems). + +

+keytool +ist ein kommandozeilenbasiertes Hilfsprogramm, das eine große +Anzahl an Funktionen bietet. Wir wollen hier nur die für den +Umgang mit digitalen Unterschriften benötigten betrachten. Eine +vollständige Beschreibung findet sich in der Tool-Dokumentation +des JDK. +

+ + + + + + + + + + + +
+ +

+Im JDK 1.1 gab es keytool +noch nicht. Statt dessen wurde das Programm javakey +zur Schlüsselverwaltung verwendet. Hier soll nur das Security-API +des JDK 1.2 und darüber betrachtet werden. Wir wollen daher auf +javakey +und andere Eigenschaften der Prä-1.2-JDKs nicht eingehen.

+ + + + +
 JDK1.1-6.0 
+
+ +

+Um ein neues Schlüsselpaar zu erzeugen, ist keytool +mit dem Kommando -genkey aufzurufen. +Zusätzlich müssen weitere Parameter angegeben werden: +

+ +

+Die Optionen für den Schlüssel- und Signaturtyp (-keyalg +und -sigalg) sowie die Schlüssellänge +(-keysize) und die Gültigkeitsdauer +(-validity) sollen unspezifiziert +bleiben (und daher gemäß den eingebauten Voreinstellungen +belegt werden). Zusätzlich besitzt jede Schlüsseldatenbank +ein Zugriffspaßwort, das mit der Option -storepass +(oder alternativ in der Eingabezeile) angegeben wird. Schließlich +besitzt jeder private Schlüssel ein Schlüsselpaßwort, +das mit der Option -keypass +(oder über die Eingabezeile) angegeben wird. + +

+Wir wollen zunächst ein Schlüsselpaar mit dem Aliasnamen +»hjp3« erzeugen und mit dem Paßwort »hjp3key« +vor unberechtigtem Zugriff schützen. Die Schlüsseldatenbank +wird beim Anlegen des ersten Schlüssel automatisch erzeugt und +bekommt das Paßwort »hjp3ks« zugewiesen. Wir verwenden +dazu folgendes Kommando (bitte haben Sie etwas Geduld, das Programm +benötigt eine ganze Weile): + +

+c:\-->keytool -genkey -alias hjp3 -dname
+      "CN=Guido Krueger,O=Computer Books,C=de"
+Enter keystore password:  hjp3ks
+Enter key password for <hjp3>
+        (RETURN if same as keystore password):  hjp3key
+
+ + +

+Nun wird ein DSA-Schlüsselpaar der Länge 1024 mit einer +Gültigkeitsdauer von 90 Tagen erzeugt. Zur Überprüfung +kann das Kommando -list (in +Kombination mit -v) angegeben +werden: + +

+C:\--->keytool -alias hjp3 -list -v
+Enter keystore password:  hjp3ks
+Alias name: hjp3
+Creation date: Sun Dec 26 17:11:36 GMT+01:00 1999
+Entry type: keyEntry
+Certificate chain length: 1
+Certificate[1]:
+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
+
+ + +

+Gleichzeitig wird ein Eigenzertifikat +für den gerade generierten öffentlichen Schlüssel erstellt. +Es kann dazu verwendet werden, digitale Unterschriften zu verifizieren. +Jedes Zertifikat in der Schlüsseldatenbank (und damit jeder eingebettete +öffentliche Schlüssel) gilt im JDK automatisch als vertrauenswürdig. + + + + +

Erstellen einer digitalen Unterschrift

+ +

+Wie erwähnt, entsteht eine digitale Unterschrift zu einer Nachricht +durch das Verschlüsseln des Message Digests der Nachricht mit +dem privaten Schlüssel des Unterzeichnenden. Nachdem wir nun +ein Schlüsselpaar erstellt haben, können wir es nun dazu +verwenden, beliebige Dateien zu signieren. + +

+Dazu wird die Klasse Signature +des Pakets java.security +verwendet. Ihre Programmierschnittstelle ähnelt der der Klasse +MessageDigest: +zunächst wird ein Objekt mit Hilfe einer Factory-Methode beschafft, +dann wird es initialisiert, und schließlich werden die Daten +durch wiederholten Aufruf von update +übergeben. Nachdem alle Daten angegeben wurden, berechnet ein +letzter Methodenaufruf das Resultat. + +

+Ein Signature-Objekt +kann wahlweise zum Signieren oder zum Verifizieren verwendet werden. +Welche der beiden Funktionen aktiviert wird, ist nach der Instanzierung +durch den Aufruf einer Initialisierungsmethode festzulegen. Ein Aufruf +von initSign +initialisiert das Objekt zum Signieren, ein Aufruf von initVerify +zum Verifizieren. +

+ + + + + +
+ +
+public static Signature getInstance(String algorithm)
+  throws NoSuchAlgorithmException
+
+public final void initSign(PrivateKey privateKey)
+  throws InvalidKeyException
+
+public final void initVerify(PublicKey publicKey)
+  throws InvalidKeyException
+
+
+
+java.security.Signature
+

+ + + + + + + + + +
+ +

+Als Argument von getInstance +wird der gewünschte Signier-Algorithmus übergeben. Auch +hier wird - wie an vielen Stellen im Security-API des JDK - eine Strategie +verfolgt, nach der die verfügbaren Algorithmen konfigurier- und +austauschbar sind. Dazu wurde ein Provider-Konzept +entwickelt, mit dessen Hilfe dem API Klassenpakete zur Verfügung +gestellt werden können, die Funktionalitäten des Security-Pakets +teilweise oder ganz austauschen. Falls der Provider beim Aufruf von +getInstance +nicht angegeben wird, benutzt die Methode den Standard-Provider »SUN«, +der zusammen mit dem JDK ausgeliefert wird. Der zu dem von uns generierten +Schlüssel passende Algorithmus ist »SHA/DSA«.

+ + + + +
 Hinweis 
+
+ +

+Die zum Aufruf der init-Methoden benötigten Schlüssel können +aus der Schlüsseldatenbank beschafft werden. Auf sie kann mit +Hilfe der Klasse KeyStore +des Pakets java.security +zugegriffen werden. Dazu wird zunächst ein KeyStore-Objekt +instanziert und durch Aufruf von load +mit den Daten aus der Schlüsseldatenbank gefüllt. Mit getKey +kann auf einen privaten Schlüssel zugegriffen werden, mit getCertificate +auf einen öffentlichen: +

+ + + + + +
+ +
+public static KeyStore getInstance(String type)
+  throws KeyStoreException
+
+public final Key getKey(String alias, char[] password)
+  throws KeyStoreException,
+         NoSuchAlgorithmException,
+         UnrecoverableKeyException
+
+public final Certificate getCertificate(String alias)
+  throws KeyStoreException
+
+
+
+java.security.KeyStore
+

+ + + + + + + + + + + +
+ +

+Das von getCertificate +zurückgegebene Objekt vom Typ Certificate +stammt nicht aus dem Paket java.security, +sondern java.security.cert. +Das in java.security +vorhandene gleichnamige Interface wurde bis zum JDK 1.1 verwendet, +ab 1.2 aber als deprecated +markiert. Wenn nicht mit qualifizierten Klassennamen gearbeitet wird, +muss daher die import-Anweisung +für java.security.cert.Certificate +im Quelltext vor der import-Anweisung +von java.security.Certificate +stehen.

+ + + + +
 Warnung 
+
+ +

+Die Klasse Certificate +besitzt eine Methode getPublicKey, +mit der auf den im Zertifikat enthaltenen öffentlichen Schlüssel +zugegriffen werden kann: +

+ + + + + +
+ +
+public PublicKey getPublicKey()
+
+
+
+java.security.cert.Certificate
+ +

+Ist das Signature-Objekt +initialisiert, wird es durch Aufruf von update +mit Daten versorgt. Nachdem alle Daten übergeben wurden, kann +mit sign +die Signatur abgefragt werden. Wurde das Objekt zum Verifizieren initialisiert, +kann das Ergebnis durch Aufruf von verify +abgefragt werden: +

+ + + + + +
+ +
+public final byte[] sign()
+  throws SignatureException
+
+public final boolean verify(byte[] signature)
+  throws SignatureException
+
+
+
+java.security.Signature
+ +

+Nach diesen Vorüberlegungen können wir uns nun das Programm +zum Erstellen einer digitalen Unterschrift ansehen. Es erwartet zwei +Kommandozeilenargumente: den Namen der zu signierenden Datei und den +Namen der Datei, in den die digitale Unterschrift ausgegeben werden +soll. + + +

+ + + + + +
+ +
+001 /* DigitalSignature.java */
+002 
+003 import java.io.*;
+004 import java.security.cert.Certificate;
+005 import java.security.*;
+006 
+007 public class DigitalSignature
+008 {
+009   static final String KEYSTORE = "c:\\windows\\.keystore";
+010   static final char[] KSPASS   = {'h','j','p','3','k','s'};
+011   static final String ALIAS    = "hjp3";
+012   static final char[] KEYPASS  = {'h','j','p','3','k','e','y'};
+013 
+014   public static void main(String[] args)
+015   {
+016     try {
+017       //Laden der Schlüsseldatenbank
+018       KeyStore ks = KeyStore.getInstance("JKS");
+019       FileInputStream ksin = new FileInputStream(KEYSTORE);
+020       ks.load(ksin, KSPASS);
+021       ksin.close();
+022       //Privaten Schlüssel "hjp3" lesen
+023       Key key = ks.getKey(ALIAS, KEYPASS);
+024       //Signatur-Objekt erstellen
+025       Signature signature = Signature.getInstance("SHA/DSA");
+026       signature.initSign((PrivateKey)key);
+027       //Eingabedatei einlesen
+028       FileInputStream in = new FileInputStream(args[0]);
+029       int len;
+030       byte[] data = new byte[1024];
+031       while ((len = in.read(data)) > 0) {
+032         //Signatur updaten
+033         signature.update(data, 0, len);
+034       }
+035       in.close();
+036       //Signatur berechnen
+037       byte[] result = signature.sign();
+038       //Signatur ausgeben
+039       FileOutputStream out = new FileOutputStream(args[1]);
+040       out.write(result, 0, result.length);
+041       out.close();
+042     } catch (Exception e) {
+043       System.err.println(e.toString());
+044       System.exit(1);
+045     }
+046   }
+047 }
+
+
+DigitalSignature.java
+ +Listing 48.5: Erstellen einer digitalen Unterschrift

+ +

+Will beispielsweise der Benutzer, dessen privater Schlüssel unter +dem Aliasnamen »hjp3« in der Schlüsseldatenbank gespeichert +wurde, die Datei DigitalSignature.java +signieren und das Ergebnis in der Datei ds1.sign +abspeichern, so ist das Programm wie folgt aufzurufen: + +

+C:\--->java DigitalSignature DigitalSignature.java ds1.sign
+
+ +

+ + + + + + + + + +
+ +

+Die Laufzeit des Programms ist beträchtlich. Das Verschlüsseln +des Message Digest kann auf durchschnittlichen Rechnern durchaus etwa +30 Sekunden dauern. Glücklicherweise ist die Laufzeit nicht nennenswert +von der Dateilänge abhängig, denn das Berechnen des Message +Digests erfolgt sehr schnell.

+ + + + +
 Hinweis 
+
+ + + + +

Verifizieren einer digitalen Unterschrift

+ +

+Das Programm zum Verifizieren arbeitet ähnlich wie das vorige. +Statt mit initSign +wird das Signature-Objekt +nun mit initVerify +initialisiert und das Ergebnis wird nicht durch Aufruf von sign, +sondern durch Aufruf von verify +ermittelt. + + +

+ + + + + +
+ +
+001 /* VerifySignature.java */
+002 
+003 import java.io.*;
+004 import java.security.cert.Certificate;
+005 import java.security.*;
+006 
+007 public class VerifySignature
+008 {
+009   static final String KEYSTORE = "c:\\windows\\.keystore";
+010   static final char[] KSPASS   = {'h','j','p','3','k','s'};
+011   static final String ALIAS    = "hjp3";
+012 
+013   public static void main(String[] args)
+014   {
+015     try {
+016       //Laden der Schlüsseldatenbank
+017       KeyStore ks = KeyStore.getInstance("JKS");
+018       FileInputStream ksin = new FileInputStream(KEYSTORE);
+019       ks.load(ksin, KSPASS);
+020       ksin.close();
+021       //Zertifikat "hjp3" lesen
+022       Certificate cert = ks.getCertificate(ALIAS);
+023       //Signature-Objekt erstellen
+024       Signature signature = Signature.getInstance("SHA/DSA");
+025       signature.initVerify(cert.getPublicKey());
+026       //Eingabedatei lesen
+027       FileInputStream in = new FileInputStream(args[0]);
+028       int len;
+029       byte[] data = new byte[1024];
+030       while ((len = in.read(data)) > 0) {
+031         //Signatur updaten
+032         signature.update(data, 0, len);
+033       }
+034       in.close();
+035       //Signaturdatei einlesen
+036       in = new FileInputStream(args[1]);
+037       len = in.read(data);
+038       in.close();
+039       byte[] sign = new byte[len];
+040       System.arraycopy(data, 0, sign, 0, len);
+041       //Signatur ausgeben
+042       boolean result = signature.verify(sign);
+043       System.out.println("verification result: " + result);
+044     } catch (Exception e) {
+045       System.err.println(e.toString());
+046       System.exit(1);
+047     }
+048   }
+049 }
+
+
+VerifySignature.java
+ +Listing 48.6: Verifizieren einer digitalen Unterschrift

+ +

+Soll die Datei DigitalSignature.java +mit der im vorigen Beispiel erstellten Signatur verifiziert werden, +kann das durch folgendes Kommando geschehen: + +

+C:\--->java VerifySignature DigitalSignature.java ds1.sign
+verification result: true
+
+ + +

+Wird nur ein einziges Byte in DigitalSignature.java +verändert, ist die Verifikation negativ und das Programm gibt +false aus. Durch eine erfolgreich +verifizierte digitale Unterschrift können wir sicher sein, dass +die Datei nicht verändert wurde. Zudem können wir sicher +sein, dass sie mit dem privaten Schlüssel von »hjp3« +signiert wurde, denn wir haben sie mit dessen öffentlichen Schlüssel +verifiziert. + + + + +

48.1.7 Zertifikate

+ +

+Ein großes Problem bei der Public-Key-Kryptographie besteht +darin, die Authentizität von öffentlichen Schlüsseln +sicherzustellen. Würde beispielsweise B einen öffentlichen +Schlüssel publizieren, der glaubhaft vorgibt, A zu gehören, +könnte dies zu verschiedenen Unannehmlichkeiten führen: +

+ +

+Einen Schutz gegen derartigen Missbrauch bringen Zertifikate. +Ein Zertifikat ist eine Art Echtheitsbeweis für einen öffentlichen +Schlüssel, das damit ähnliche Aufgaben erfüllt wie +ein Personalausweis oder Reisepass. Ein Zertifikat besteht meist aus +folgenden Teilen: +

+ +

+Die Glaubwürdigkeit des Zertifikats hängt von der Glaubwürdigkeit +des Ausstellers ab. Wird dieser als vertrauenswürdig angesehen, +d.h. kann man seiner digitialen Unterschrift trauen, so wird man auch +dem Zertifikat trauen und den darin enthaltenen öffentlichen +Schlüssel akzeptieren. + +

+Dieses Vertrauen kann einerseits darauf basieren, dass der Aussteller +eine anerkannte Zertifizierungsautorität ist (auch Certification +Authority, kurz CA, +genannt), deren öffentlicher Schlüssel bekannt und deren +Seriösität institutionell manifestiert ist. Mit anderen +Worten: dessen eigenes Zertifikat in der eigenen Schlüsselverwaltung +bekannt und als vertrauenswürdig deklariert ist. Andererseits +kann das Vertrauen in das Zertifikat daher stammen, dass der Aussteller +persönlich bekannt ist, sein öffentlicher Schlüssel +eindeutig nachgewiesen ist, und seiner Unterschrift Glauben geschenkt +wird. + +

+Der erste Ansatz wird beispielsweise bei X.509-Zertifikaten +verfolgt. Institute, die derartige Zertifikate ausstellen, werden +meist staatlich authorisiert und geprüft. Beispiele dafür +sind VeriSign, Thawte +oder das TA Trustcenter. Der zweite +Ansatz liegt beispielsweise den Zertifikaten in PGP zugrunde. Hier +ist es sogar möglich, öffentliche Schlüssel mit mehreren +digitalen Unterschriften unterschiedlicher Personen zu signieren und +so die Glaubwürdigkeit (bzw. ihre Reichweite) zu erhöhen. + +

+Zertifizierungsinstitute stellen meist auch Schlüsseldatenbanken +zur Verfügung, aus denen Zertifikate abgerufen werden können. +Diese dienen auch als Anlaufstelle, um ungültig gewordene oder +unbrauchbare Zertifikate zu registrieren. Lokale Schlüsselverwaltungen +können sich mit diesen Informationen synchronisieren, um ihren +eigenen Schlüsselbestand up-to-date zu halten. +


+ + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 +
 <<  +  <   +  >   + >>  + API  +© 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de +
+ + + -- cgit v1.2.3