Die Nutzung des PBKDF2-Verfahrens wird Euch bei der ersten Betrachtung als unsinnig erscheinen, später jedoch eine sehr sinnvolle Ergänzung Eurer Programm in der realen Welt sein.
Was macht dieses Verfahren mit einem Passwort ? Fangen wir einen Schritt vorher an – üblicherweise nutzt Ihr eine graphische Oberfläche (GUI) für Euer Programm und dort wird üblicherweise auch ein Passwort-Eingabe-Feld bereitgestellt, welches statt der Klartextanzeige nur Sternchen im Feld anzeigt. Das Element stellt das eingegebene Passwort in einem Char Array zur Verfügung und an dieser Stelle fängt unsere PBKDF2-Routine an.
Zuerst wird ein Zufalls-Salt-Wert erzeugt, welcher dafür sorgt, das der später als Passwort dienende Wert stets unterschiedlich ist und die vorbereitete Passwort-Tabelle (sogenannte „Rainbow-Tables„) nicht nutzbar sind.
Das Passwort wird nun mit dem angegebenen Hashverfahren (hier „PBKDF2WithHmacSHA512“) immer wieder gehasht, und zwar mit der angegebenen Anzahl der Iterationen.
Am Ende steht im Byte Array „passwordHashByte“ das neue Passwort bereit, welches z.B. für eine AES-Verschlüsselung genutzt wird.
Warum sollte ich PBKDF2 nutzen und nicht nur ein einfaches Hash-Verfahren ? Hier kommen wir nun zum entscheidenden Punkt, welcher als Stichpunkt die Entropie hat. Ich werde Euch nicht mit der ausführlichen Erläuterung langweilen sondern gebe Euch die Kurzversion – Entropie bezeichnet die Unterschiedlichkeit. Wenn Ihr z.B. über die Tastatur ein Passwort aus dem vorhandenen 26-Buchstaben und 10-Ziffern Umfang der Tastatur nutzt kommt Ihr auf (26 * 2 [Großbuchstaben] +10) = 62 unterschiedliche Zeichen, wobei jedoch jedes Zeichen im Char-Array aus 256 (unterschiedlichen) Zeichen bestehen kann. Im Klartext bedeutet es für einen Angreifer eine wesentliche Verkleinerung des genutzten Zeichenvorrates und damit eine krasse Verkürzung einer Brute Force Attacke, bei der ein Angreifer alle möglichen Zeichen hintereinander ausprobiert.
Der zweite wichtige Aspekt ist die Geschwindigkeit – eine Hashroutine ist üblicherweise auf eine hohe Geschwindigkeit ausgelegt und auch das spielt einem Brute Force-Angreifer in die Hände. Die beschriebene PBKDF2-Routine verlangsamt das Programm bei Eingabe des Passwortes und verlängert damit die Zeit, in der ein Angreifer alle möglichen Kombinationen ausprobiert. Die Anzahl der Iterationen ist hier der entscheidende Stellfaktor – desto höher desto mehr Iterationen werden ausgeführt und damit dieser Programmteil verlangsamt.
Ein Punkt benötigt aber Eure Aufmerksamkeit: der erzeugte Salt ist zusammen mit dem erzeugten (gehashten) Passwort abzuspeichern oder weiterzugeben, ansonsten klappt es nicht mit der Nutzung beim Empfänger (also der Entschlüsselung).
Kurzum: Ihr solltet die PBKDF2-Routine bei jeder Passwort-basierten Verschlüsselung nutzen !
Kommen wir nun zur Routine:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
package net.bplaced.javacrypto.general; /* * Herkunft/Origin: http://javacrypto.bplaced.net/ * Programmierer/Programmer: Michael Fehr * Copyright/Copyright: frei verwendbares Programm (Public Domain) * Copyright: This is free and unencumbered software released into the public domain. * Lizenttext/Licence: <http://unlicense.org> * getestet mit/tested with: Java Runtime Environment 8 Update 191 x64 * Datum/Date (dd.mm.jjjj): 13.11.2018 * Funktion: Demonstriert das PBKDF2-Hashing * Function: shows the PBKDF2-Hashing * * Sicherheitshinweis/Security notice * Die Programmroutinen dienen nur der Darstellung und haben keinen Anspruch auf eine * korrekte Funktion, insbesondere mit Blick auf die Sicherheit ! * Prüfen Sie die Sicherheit bevor das Programm in der echten Welt eingesetzt wird. * The program routines just show the function but please be aware of the security part - * check yourself before using in the real world ! */ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.xml.bind.DatatypeConverter; public class A08_PBKDF2 { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException { System.out.println("A08 PBKDF2"); // das passwort wird z.b. von einem jPassword-Feld übergeben char[] passwordChar = "geheimes Passwort".toCharArray(); // variablen für pbkdf2 final int PBKDF2_ITERATIONS = 10000; // anzahl der iterationen, höher = besser = langsamer final int SALT_SIZE_BYTE = 128; // grösse des salts, sollte so groß wie der hash sein final int HASH_SIZE_BYTE = 128; // größe das hashes bzw. gehashten passwortes, 128 byte = 512 bit byte[] passwordHashByte = new byte[HASH_SIZE_BYTE]; // das array nimmt das gehashte passwort auf // wir erzeugen einen zufalls salt mit securerandom, nicht mit random SecureRandom secureRandom = new SecureRandom(); byte passwordSaltByte[] = new byte[SALT_SIZE_BYTE]; secureRandom.nextBytes(passwordSaltByte); // erstellung des gehashten passwortes PBEKeySpec spec = new PBEKeySpec(passwordChar, passwordSaltByte, PBKDF2_ITERATIONS, HASH_SIZE_BYTE); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); passwordHashByte = skf.generateSecret(spec).getEncoded(); // das passwordHash wird an die ver- oder entschlüsselungs-routine übergeben System.out.println("passwordChar: " + String.valueOf(passwordChar)); System.out.println("Iterationen : " + PBKDF2_ITERATIONS); System.out.println("passwordSalt: " + DatatypeConverter.printHexBinary(passwordSaltByte)); System.out.println("passwordHash: " + DatatypeConverter.printHexBinary(passwordHashByte)); } } |
Die Ausgabe auf der Konsole findet Ihr nachfolgend (bitte beachtet das Eure Ausgabe wegen der Zufallsroutine andere Werte anzeigt):
1 2 3 4 5 |
A08 PBKDF2 passwordChar: geheimes Passwort Iterationen : 10000 passwordSalt: 3B8F3488EBFBAACA6F8AB50D7C3B692BFB29DB54D9621B7EC2209B9E4DDE8DD080163DAA05B74BB90770691BC76A7DB3F33BFCBB94367089310645AAD86C33788196ECA6B387621451783C4E7EA8C4171D37989A8F7969893387C1105B1CD4FDF1BE36E8BC982906B7C46A2E704B42E72FB5E0BCBE050BFBAC36D4741E74D755 passwordHash: 60C95DB65ED999A1845D9EC7094FEAED |
Den Quellcode zum Programm findet Ihr zum Download in meinem Github-Repository, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/A-Allgemeine-Programme. Dort findet Ihr auch die Versionen für Java 11.
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 26.01.2019