Hashfunktionen in Java - MessageDigest

Status
Nicht offen für weitere Antworten.
B

Beni

Gast
von mic_checker

mic_checker hat gesagt.:
1. Einführung

Bevor ich mit dem eigentlichen Text anfange, vorab einige allgemeine Worte zur Kryptographie in Java.

Die kryptograpischen Methoden werden in der sogenannten Java Cryptography Architecture (JCA) aufbewahrt.
Die JCA bildet das fundamentale Framework für den Zugang zur Kryptographie API und soll unabhängig von den
einzelnen Algorithmen und Implementierungen sein.

Zu diesem Zweck werden sogenannte Cryptography Package Provider (Provider) eingesetzt. Ein solcher
Provider stellt in der Regel Klassen zur Verfügung, die Algorithmen für z.B. Message Authenication Codes (MAC),
MessageDigests,Digitale Unterschriften etc. implementieren.

The Java Cryptography Architecture introduced the notion of a Cryptographic Service Provider (used interchangeably
with "provider" in this document). This term refers to a package (or a set of packages) that supplies a concrete
implementation of a subset of the cryptography aspects of the Security API.

Siehe JavaTM Cryptography Architecture API Specification & Reference

Zusätzlich zur JCA gibt es noch die Java Cryptography Extension (JCE).

Wie der Name schon sagt ist die JCE eine Erweiterung der JCA und bietet ein Framework und die Implementierungen für Verschlüsselung, Schlüssel-Erzeugung etc .

Zusammen bieten die JCE und die JCA eine nahezu komplette und plattformunabhängige Kryptographie API.

Dies soll in diesem Rahmen erstmal als kleine Einführung reichen, damit ihr best. Begriffe besser verstehen und einordnen könnt.


2. Hashfunktionen in Java

Zur Berechnung eines Hashwertes / einer Prüfsumme kann in Java die Klasse MessageDigest verwendet werden.

Im Folgenden möchte ich nun die einzelnen Schritte , die notwendig sind um Prüfsummen zu berechnen, kurz erläutern.

1) Erzeugung einer Instanz

Um eine Instanz zu erzeugen müsst ihr nicht, wie vielleicht sonst gewohnt, mit new MessageDigest(...) den Konstruktor
aufrufen, sondern ihr müsst eine der statischen getInstance() Methoden verwenden, die ein Objekt vom Typ MessageDigest zurückliefern.

Code:
static MessageDigest 	getInstance(String algorithm)

static MessageDigest 	getInstance(String algorithm, Provider provider)

static MessageDigest 	getInstance(String algorithm, String provider)

Durch den ersten Parameter gebt ihr den Algorithmus an, anhand dessen die Prüfsumme berechnet werden soll.

Momentan werden folgende Algorithmen unterstützt:

- MD2
- MD5
- SHA-1
- SHA-256
- SHA-384
- SHA-512

In der Regel wird entweder "MD5" oder "SHA-1" für die meisten Zwecke ausreichend sein. MD5 erstellt einen Hashwert der 128 Bit lang ist. Der Secure-Hash-Algorithm (SHA-1) gewährleistet mit einem 160 Bit Hashwert größere Sicherheit gegen Brute Force Attacken.

Der einzige Unterschied zwischen den einzelnen getInstance() Methoden besteht darin, dass ihr in der zweiten und dritten Methode noch den Provider spezifizieren könnt.


2) Spezifizierung der Daten

Nachdem wir nun unser Objekt vom Typ MessageDigest haben, können wir mit Hilfe der sog. update() Methoden
spezifizieren von welchen Daten wir die Prüfsumme berechnen wollen. Im Gegensatz zum Schritt 3 (der endgültigen
Berechnung der Prüfsumme) können wir die update() Methoden "beliebig" oft aufrufen und somit "beliebig" viele Daten in die Prüfsummenberechnung mit einbeziehen.

Code:
void 	update(byte input) 

void 	update(byte[] input) 

void 	update(byte[] input, int offset, int len) 

void 	update(ByteBuffer input)

Die Methoden sollten eigentlich selbsterklärend sein, da jeweils nur ein byte, bzw. byte Array zu den bisherigen Daten
hinzugefügt wird.
Die letzte update() Methode gibt es erst seit Java 5 und macht Gebrauch von einem sog. ByteBuffer. An dieser Stelle
verweise ich auf diesen Artikel, der eine Einführung in Java NIO gibt.

3) Berechnung der Prüfsumme

Abschließend könnt ihr nun zur endgültigen Berechnung der Prüfsumme eine der digest() Methoden aufrufen:

Code:
byte[] 	digest() 

byte[] 	digest(byte[] input) 

int 	digest(byte[] buf, int offset, int len)

Solltet ihr zuvor mit Hilfe von einer der update() Methoden die Daten zur Prüfsummenberechnung bereits
spezifiziert haben, so könnt ihr nun die erste digest() Methode aufrufen.

Die zweite Methode ruft intern zunächst die zweite update() Methode auf und anschließend ebenfalls
die erste digest() Methode. In diesem Fall könnt ihr euch also den manuellen Aufruf von update() sparen,
wenn ihr nur von diesem byte-Array die Prüfsumme berechnen wollt.

Die dritte Methode gibt im Gegensatz zu den ersten beiden kein byte-Array sondern ein int zurück.
Das liegt daran das es kein neues Array erzeugt , sondern den Wert in das bestehende Array
"byte[] buf" kopiert.
Zusätzlich dazu könnt ihr noch einen Offset angeben, so dass versucht wird "len" Bytes ab Position "Offset" in das
Array zu kopieren. Als Rückgabewert erhaltet ihr dann die letztendliche Anzahl an kopierten Bytes.

Wichtig:
Nachdem eine der drei digest() Methoden aufgerufen wurde, wird das MessageDigest Objekt neu initialisiert.
Solltet ihr also mehrere aufeinander aufbauende Prüfsummenberechnungen durchführen wollen, so müsst ihr dran denken zuvor die MessageDigest Objekte zu clonen (mit Hilfe der Methode clone()).



3. Beispiel:

Abschließend noch ein kleines Beispiel, dass die einzelnen Schritte kurz demonstriert.

Code:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class MD5Calc {
	private MessageDigest md5;
	private byte[] digest;
	
	public MD5Calc(String algorithm) {
		try {
			md5 = MessageDigest.getInstance(algorithm);
		} catch(NoSuchAlgorithmException nsae) {
			nsae.printStackTrace();
		}
	}
			
  	public String toHexString(byte b) {
   		int value = (b & 0x7F) + (b < 0 ? 128 : 0);
	   
    		String ret = (value < 16 ? "0" : "");
    		ret += Integer.toHexString(value).toUpperCase();
	    
		return ret;
  	}
	  
	public String do_checksum(String data)
	{
		StringBuffer strbuf = new StringBuffer();
		
      		md5.update(data.getBytes(), 0, data.length());      
      		digest = md5.digest();      
    
      		for (int i = 0; i < digest.length; i++) {
      			strbuf.append(toHexString(digest[i]));
		}
		   
		return strbuf.toString();      
 	}
	
	public static void main(String[] args)
  	{
	  	MD5Calc md5 = new MD5Calc("MD5");
		  
	  	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int num = 0;
		String input = "";
		
		if(args.length == 1) {
			try {
				num = Integer.parseInt(args[0]);
			} catch(NumberFormatException nfe) {
				nfe.printStackTrace();
			}
			
			try {	
	 			while(num-- > 0) {
	 				input = br.readLine();
					
	 				System.out.println(input+"\t-\t"+md5.do_checksum(input)+"\n");
	 			}
	 		}
	 		catch(IOException ioe) {
	 			ioe.printStackTrace();
	 		}   
		}         
   }
}

Hier noch kurz ein Beispiel - als Algorithmus wurde wie im Source angegeben "MD5" benutzt. Allerdings könnt ihr
natürlich auch mal die anderen Verfahren einsetzen und schauen wie sich das auf die Prüfsumme auswirkt.

> java MD5Calc 2
Java ist auch eine Insel
Java ist auch eine Insel - ABAC19481F4367DB4AE592B463E002C3CDD47906

Java Handbuch
Java Handbuch - B6435B24CB7466F387B8C5381829F37507B74903

4. Links
Wie immer solltet ihr bei ersten Fragen in die API schauen.

Außerdem lohnt sich ein Blick in die JavaTM Cryptography Architecture API Specification & Reference und in den JavaTM Cryptography Extension (JCE)
Reference Guide
.
 
S

stev.glasow

Gast
Auch ganz praktisch:
Code:
			 byte[] hash = md.digest(keyword) ;
			 
			 StringBuffer sb = new StringBuffer();
			 for (int i = 0; i < hash.length; ++i) {
			     sb.append(
			         Integer.toHexString(
			             (hash[i] & 0xFF) | 0x100
			         ).toLowerCase().substring(1,3)
			     );
			 }
bei MD5:
Java ist auch eine Insel : 34ba237270cdc45e55706f785d9df7fc
Java Handbuch : 20f5c0fac2f7296f6923587d27f3953b

Ist mehr die übliche Darstellung.
 
Status
Nicht offen für weitere Antworten.

Oben