Hašování retězce pomocí různých algoritmů v Javě

Nejdříve vysvětlení pojmů.

Hašovací funkce je matematická funkce (resp. algoritmus) pro převod vstupních dat do (relativně) malého čísla. Výstup hašovací funkce se označuje výtah, miniatura, otisk, fingerprint či hash (česky též někdy jako haš).
Mezi hlavní vlastnosti této funkce patří:

  • jakékoliv množství vstupních dat poskytuje stejně dlouhý výstup (otisk)
  • malou změnou vstupních dat dosáhneme velké změny na výstupu (tj. výsledný otisk se od původního zásadně na první pohled liší)
  • z hashe je prakticky nemožné rekonstruovat původní text zprávy (což je rozdíl oproti klasickému šifrování)
  • v praxi je vysoce nepravděpodobné, že dvěma různým zprávám odpovídá stejný hash, jinými slovy pomocí hashe lze v praxi identifikovat právě jednu zprávu (ověřit její správnost).

Hašování v základní variantě dovoluje testovat vstupní data na shodu, tedy rovnost. Nezachovává podobnost dat ani uspořádání. wiki

Zdůrazňuji, že jakékoliv množství vstupních dat poskytuje stejně dlouhý výstup a malou změnou vstupních dat dosáhneme velké změny na výstupu.

Pro hashování v Javě existuje třída MessageDigest. Digest v překladu v tomto kontextu znamená suma, souhrn. V rámci standardní Javy jsou k dispozici hašovací algoritmy MD5, SHA-1, SHA-256.

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class App {

	public static void main(String[] args) {
		String message = "Hello World!";
		
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
			MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
			
			byte[] md5Digest = md5.digest(message.getBytes());
			byte[] sha1Digest = sha1.digest(message.getBytes());
			byte[] sha256Digest = sha256.digest(message.getBytes());
			
			System.out.println(formatResult(md5Digest));
			System.out.println(formatResult(sha1Digest));
			System.out.println(formatResult(sha256Digest));
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}
	
	private static String formatResult(byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		sb.append("Délka pole: ");
		sb.append(bytes.length);
		sb.append("\t");
		sb.append("Pole: ");
		sb.append(Arrays.toString(bytes));
		return sb.toString();
	}
}

MessageDigest potřebuje na vstupu pole bytů (message.getBytes()) a vrátí také pole bytů, která má ale předem danou délku (je jedno jak dlouhý text hašujete) a představuje otisk původního pole.

Rozdíl výsledku při změně jediného znaku (z Hello World! na Hallo World!) pro všechny tři algoritmy.

Délka pole: 16	Pole: [-19, 7, 98, -121, 83, 46, -122, 54, 94, -124, 30, -110, -65, -59, 13, -116]
Délka pole: 16	Pole: [98, 119, 56, -33, 57, 107, 57, -72, -69, -54, 87, 79, 122, 72, -72, 50]

Délka pole: 20	Pole: [46, -9, -67, -26, 8, -50, 84, 4, -23, 125, 95, 4, 47, -107, -8, -97, 28, 35, 40, 113]
Délka pole: 20	Pole: [65, 63, 96, -127, -42, 94, -26, 2, -115, 124, -43, -81, -110, 32, 39, 12, -125, 121, 61, -5]

Délka pole: 32	Pole: [127, -125, -79, 101, 127, -15, -4, 83, -71, 45, -63, -127, 72, -95, -42, 93, -4, 45, 75, 31, -93, -42, 119, 40, 74, -35, -46, 0, 18, 109, -112, 105]
Délka pole: 32	Pole: [85, 65, 51, 18, -114, -77, 16, 91, -101, -83, 102, 27, -115, 61, 17, -117, -53, -39, -47, 86, -115, -46, -5, 112, 8, 127, -119, 4, 27, 14, 3, -80]

Je vidět, že drobná změna vede k velmi rozdílnému výsledku každého ze zkoušený hašovacích algoritmů.

Napsat komentář