Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


15.2.2

Hashing


Allgemein versteht man unter Hashing die Berechnung einer Zahl, die einen Zustand einer Ansammlung von Daten eindeutig kennzeichnet. Dieser so genannte Hashcode (auch kurz Hash genannt) kann verwendet werden, um Manipulationen an Daten bei während der Lagerung oder des Transports festzustellen. Damit eine Veränderung einer Datei aufgedeckt werden kann, muss der Hash-Algorithmus eine zentrale Forderung erfüllen: Zu einer gegebenen Nachricht muss es sehr schwierig sein, eine andere Nachricht zu konstruieren, die denselben Hashcode erzeugt (sog. Kollisionsfreiheit). Auch darf es nicht möglich sein, aus dem Hash die ursprünglichen Daten wieder zu rekonstruieren. Aus diesem Grund spricht man auch von Einweg-Hash-Funktionen. Die beiden weitverbreitetsten Hash-Funktionen sind MD-5 und SHA-1. Sie erzeugen einen Hash mit einer Länge von 128 bzw. 160 Bit.

Ein Hash-Algorithmus wird in Java mit der Klasse MessageDigest aus dem Paket java.security dargestellt. Exemplare dieser Klasse können mit der statischen Methode getInstance() erzeugt werden, der der Name des gewünschten Algorithmus als String übergeben wird.

Nach der Erzeugung können die Daten, über die der Hash berechnet werden soll, mit der Methode update() an das MessageDigest-Exemplar übergeben werden, das den Hash entsprechend den übergebenen Daten aktualisiert. Am Schluss kann mit digest() der Hash abgerufen werden.

Für Fälle, in denen ein Hash über eine größere Datenmenge berechnet werden soll, stehen die Stream-Klassen DigestInputStream und DigestOutputStream bereit, die bequem in eine Stream-Kaskade eingeklinkt werden können. Das folgende Beispiel zeigt, wie mit einem DigestInputStream, der auf einem FileInputStream aufgesetzt wird, ein Hash über eine Datei berechnet werden kann:
  protected static byte[] calcHash(String algorithm, String fileName)
    throws IOException, NoSuchAlgorithmException {
    MessageDigest md;
    DigestInputStream digestStream;

    // MessageDigest-Exemplar erzeugen
    md = MessageDigest.getInstance(algorithm);
    // Einen Stream mit diesem Exemplar erzeugen
    digestStream =
      new DigestInputStream(new FileInputStream(fileName), md);

    // Datei auslesen und Prüfsumme berechnen
    while(digestStream.read() != -1);
    digestStream.close();

    // Prüfsumme ermitteln und zurückliefern
    return md.digest();
  }

Wenn Hashing für die Integritätssicherung von Daten eingesetzt wird, muss der Hash in irgendeiner Form geschützt werden, wenn er gemeinsam mit den Daten über einen unsicheren Kanal übertragen wird. Ansonsten könnte ein Angreifer die Daten manipulieren und einfach einen neuen Hash über die geänderten Daten berechnen und an die Daten anhängen. Die Lösung dieses Problems ist, dass beide Seiten ein gemeinsames Geheimnis vereinbaren und den Hash damit sichern. Dies kann entweder durch die Verwendung eines Keyed-Hash Message Authentication Codes (HMAC) oder die Verschlüsselung des Hash erfolgen. Letztere Technik wird im Beispiel im nächsten Abschnitt verwendet.

Bei einem HMAC wird der Hash so gesichert, dass in den Hash zusätzlich zur Nachricht ein Schlüssel einberechnet wird, der nur dem Absender und dem Emfänger bekannt ist. Das HMAC-Verfahren funktioniert mit beliebigen Hash-Funktionen. Die gängigsten Implementierungen beruhen aber auf MD-5 und SHA-1 und werden daher auch HMAC-MD-5 bzw. HMAC-SHA-1 genannt. Diese beiden Algorithmen sind auch standardmäßig im J2SDK verfügbar.

Material zum Beispiel


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.