Kryptographie mit Java


... [Seminar WWW und Java ] ... [ Thema Verschlüsselung im Internet ] ... [ Literaturverzeichnis ] ...


Übersicht : Kryptographie mit Java


JDK 1.2 und JCE

Obwohl die Architektur der kryptographischen Klassen bereits im JDK 1.1 teilweise vorgesehen war, wurden einige Klassen erweitert und einige neue Klassen hinzugefügt. Die hier vorgestellten Konzepte beziehen sich auf das JDK 1.2 beta4. Außerdem sind im JDK aus rechtlichen Gründen (Exportbeschränkungen in den USA) nur MessageDigest und Signatur-Algorithmen enthalten. Zur Verschlüsselung von Daten wird zusätzlich die Java Cryptographic Extension (JCE) benötigt.
Hinweis : Wenn man versucht, die JCE 'runterzuladen wird nachgefragt, ob man in den USA lebt. Die hier geforderte Angabe wird allerdings nicht überprüft (was will uns das nur sagen...?)
Darüberhinaus gibt es auch andere Implementierungen nach den Spezifikationen der JCE, die auch in Europa frei verfügbar sind. Einige dieser Implementierungen sollen auch das JDK 1.1 um die im JDK 1.2 hinzugefügten Klassen erweitern. Dies habe ich allerdings nicht getestet, so daß bei Bedarf die Funktionsfähigkeit dieses Features ausprobiert werden muß....


JCA - Die Architektur

Die Java Cryptography Architecture (JCA) ist ein Bestandteil der Java Security API. In dieser JCA hat Sun eine strikte Trennung von Konzepten (Cipher, MessageDigest und Signature), Algorithmen und den dazugehörigen Implementierungen festgelegt. Trotz der daraus resultierenden Komplexität (aber auch Flexibilität) dieser Architektur ist die Anwendung, wie man noch sehen wird, relativ einfach gehalten.

Service Provider Interfaces - SPI

Klassen, die ein Konzept-spezifisches SPI beerben, implementieren einen beliebigen Algorithmus, der jedoch zum Konzept 'passen' muß. Im JCA ist zu jedem Konzept eine (abstrakte) SPI-Klasse vorgesehen, also (die wichtigsten):

Wenn man also beispielsweise seine eigene Implementierung des DES-Algorithmus programmieren möchte, könnte der Kopf der Klasse etwa so aussehen :
class MyDESImplementation extends CipherSpi {...}

Die Zuordnung der Implementierung zum Namen des Algorithmus erfolgt erst in den Providern.

Provider

Provider erfüllen zwei Funktionen : Zum einen verwalten sie eine oder mehrere Algorithmus-Implementierungen. Dabei ist zu beachten, daß jeder Algorithmus nur einmal in einem Provider implementiert sein darf. Zum anderen erfolgt in einem Provider die Zuordnung zwischen dem Namen eines Algorithmus und der Implementierung. Provider beerben immer die abstrakte Klasse java.security.Provider. In dieser Klasse sind Methoden vorgegeben, die dem Provider bestimmte Implementierungen zuordnen. Ein eigener Provider könnte also etwa so aussehen :
class MyProvider extends java.security.Provider {
MyProvider() {
// Aufruf des super-Konstruktors mit den Parametern (Name, Version, Beschreibung)
super ("MyProvider", 1.0 , "My own Provider");
// Die obige Implementierung diesem Provider zuordnen mit der Methode put(String Konzept.Algorithmus, String Klasse)
put ("Cipher.DES", "MyDESImplementation");
}}

Jeder Provider muß nun noch der JavaVM bekanntgemacht werden. Dazu stellt die Klasse java.security.Security die statischen Methoden addProvider(Provider p), insertProviderAt(Provider p, int i) und removeProvider(Provider p) zur Verfügung. Java.security.Security verwaltet die Provider intern in einer Liste, die bei Bedarf der Reihe nach abgefragt wird. Das Erzeugen einer Instanz der MyDESImplementation Klasse erreicht man mit Hilfe der Konzept-Klassen (engines).

Engines (Konzept-Klassen)

Konzept-Klassen runden die JCA ab. Mit Hilfe dieser Klassen werden 'gebrauchsfertige' Instanzen erzeugt, mit denen man auch etwas machen kann. Ebenso, wie es zu jedem Konzept ein SPI gibt, gibt es zu jedem Konzept die dazugehörige engine. Diese abstrakten engines stellen statische getInstance () Methoden zur Verfügung, die Instanzen des gewünschten Konzeptes und des gewünschten Algorithmus zurückgeben. In der Praxis sieht ein solcher getInstance() Aufruf so aus :
// Aufruf der getInstance() Fabrikmethode mit den Parametern (String Algorithmus, String Provider)
// Die getInstanceMethode ist mehrfach überladen, so daß man auch nur den Algorithmus
// oder auch weitere Paramter übergeben kann
Cipher DESCipher = Cipher.getInstance(DES, MyProvider);

Zusammenhänge

Obwohl die ganze Architektur etwas unübersichtlich wirkt, ist sie für den Benutzer kryptographischer Algorithmen sehr einfach zu verwenden. Die engines sind letztlich die Schnittstellen für den Programmierer, um die darunterliegenden Provider und SPI braucht er sich eigentlich nicht zu kümmern.
Der technische Ablauf im Hintergrund ist etwa wie folgt darzustellen :
1. Die Anwendung stellt eine Anfrage nach einem Algorithmus an die getInstance() Methode der engine;
2. getInstance() reicht diese Anfrage an java.security.Security weiter;
3. java.security.Security fragt nacheinander bei den registrierten Providern nach, ob sie eine Implenmentierung des Algorithmus anzubieten haben;
4. Der erste Provider, der einen Algorithmus anzubieten hat, gibt den Namen der Klasse zurück, die den Algorithmus implementiert;
5. java.security.Security reicht diesen Namen an die engine weiter;
6. Die engine gibt eine Instanz dieser Klasse an die Anwendung zurück;


Praktische Beispiele

Daten verschlüsseln

import javax.crypto.*;
import java.security.*;

try {
KeyGenerator kg=KeyGenerator.getInstance(AlgoString,ProviderString);
kg.init (new SecureRandom());
SecretKey CryptoSecret = kg.generateKey();
Cipher Vorgang = Cipher.getInstance(AlgoString, ProviderString);
Vorgang.init (Cipher.ENCRYPT_MODE, f.CDaten.getSecretKey());
byte[] raw = Vorgang.doFinal(DataBytes);
}
catch (NoSuchProviderException nspe) {System.out.println("Den angegebenen Provider gibt es nicht");}
catch (NoSuchAlgorithmException nsae) {System.out.println("Der Provider kann diesen Algorithmus nicht bereitstellen");}
catch (Exception x) {System.out.println("Hier gehen ganz, ganz andere Dinge vor");}


MD erzeugen

try {
MessageDigest md=MessageDigest.getInstance(AlgoString,ProviderString);
md.update(DataBytes);
byte[] raw = md.digest();
}
catch (NoSuchProviderException nspe) {System.out.println("Den angegebenen Provider gibt es nicht");}
catch (NoSuchAlgorithmException nsae) {System.out.println("Der Provider kann diesen Algorithmus nicht bereitstellen");}
catch (Exception x) {System.out.println("Hier gehen ganz, ganz andere Dinge vor");}


Signaturen

try {
Signature sig=Signature.getInstance("SHA/DSA",ProviderString);
sig.initSign(MyPrivateAsymetricKey());
sig.update(DataBytes);
byte[] raw = sig.sign();
}
catch (NoSuchProviderException nspe) {System.out.println("Den angegebenen Provider gibt es nicht");}
catch (NoSuchAlgorithmException nsae) {System.out.println("Der Provider kann diesen Algorithmus nicht bereitstellen");}
catch (Exception x) {System.out.println("Hier gehen ganz, ganz andere Dinge vor");}


... [Seminar WWW und Java ] ... [ Thema Verschlüsselung im Internet ] ... [Kryptographie mit Java] ... [Literaturverzeichnis ] ...