Wenn Ihr den Beitrag zur MD5-Hash-Kollision gelesen und Euch gedacht habt – die SHA-Hashfamilie ist doch viel neuer und sicherer dann werdet Ihr diese Meinung überdenken müssen, zumindest was das sehr beliebte SHA-1 Verfahren angeht.
Vor einigen Jahren wurden theoretische Angriffsmöglichkeiten zum SHA-1 Hashverfahren publik und in der Folge entwickelte Marc Stevens einen praktischen Angriff. Allerdings benötigte er die Unterstützung von Google und deren enormen Rechenpower, um einen realen Angriff auf den SHA-1 Algorithmus zu ermöglichen. Einzelheiten hierzu findet ihr auf der Webseite http://shattered.it/, wo auch die beiden PDF-Dateien zum Download bereitstehen. Eine deutschsprachige Erläuterung findet Ihr auf der Heise-Webseite https://www.heise.de/security/meldung/Todesstoss-Forscher-zerschmettern-SHA-1-3633589.html.
Warum ist diese Kollision denn so gefährlich ? Sehr viele Anwendung zur elektronischen Unterschrift basieren darauf, das NICHT die Unterschrift über die zu signierende Datei berechnet wird sondern statt dessen die Signatur über den Hashwert der Datei erzeugt wird. Im Klartext bedeutet es für unser Beispiel: Eine digitale Signatur des SHA-1-Hashwertes von g08_shattered-1.pdf ist auch für das PDF-Dokument g08_shattered-2.pdf gültig. Stellt Euch vor Ihr kauft mit dem Dokument 1 ein Haus und in Dokument 2 nur einen Teddybär – die elektronische Unterschrift für beide Verträge wäre identisch!
Zum Selbertesten ladet Ihr Euch die beiden PDF-Dateien g08_shattered-1 und g08_shattered-2 herunter und schaut sie Euch an (hier zwei verkleinerte Bilder der PDF-Seite). Der Unterschied ist mehr als deutlich an der Hintergrundfarbe des oberen Bereiches zu erkennen:
g08_shattered-01.pdf:
g08_shattered-02.pdf:
Wenn Ihr für beide Dateien den SHA-1 Hashwert berechnet kommt ihr zu dem Ergebnis, das die Hashwerte für beide Dateien identisch sind (Konsolenausgabe von G08_Sha1Kollision.java):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
G08 SHA-1 Kollision = Unsicherheit Hinweis: nutzen Sie die SHA-1-Funktion niemals in Echtprogrammen da sie unsicher ist ! SHA-1-Hashwert der Datei:g08_shattered-1.pdf hash1Byte Länge:20 Data:38762CF7F55934B34D179AE6A4C80CADCCBB7F0A SHA-1-Hashwert der Datei:g08_shattered-2.pdf hash2Byte Länge:20 Data:38762CF7F55934B34D179AE6A4C80CADCCBB7F0A Die beiden SHA-1-Hashwerte sind gleich:true Alternative Berechnung eines SHA-256-Hashwertes SHA256-Hashwert der Datei:g08_shattered-1.pdf hash1Byte Länge:32 Data:2BB787A73E37352F92383ABE7E2902936D1059AD9F1BA6DAAA9C1E58EE6970D0 SHA256-Hashwert der Datei:g08_shattered-2.pdf hash2Byte Länge:32 Data:D4488775D29BDEF7993367D541064DBDDA50D383F89F0AA13A6FF2E0894BA5FF Die beiden SHA256-Hashwerte sind gleich:false G08 SHA-1 Kollision = Unsicherheit beendet |
Das es sich um unterschiedliche Dateien handelt erkennt ihr am SHA256-Hashwert – diese Werte differieren.
Fazit: nutzt wo immer Ihr könnt weder das MD5- noch den SHA-1- Hashverfahren, sondern nur die deutlich sicheren SHA-256- und SHA-512- Hashverfahren.
Hier der noch Quellcode des Programms:
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
package net.bplaced.javacrypto.unsecure; /* * 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 * getestet mit/tested with: Java Runtime Environment 11.0.1 x64 * Datum/Date (dd.mm.jjjj): 08.12.2019 * Funktion: errechnet die SHA-1-Hashwerte zweier Dateien * Function: calculates the SHA-1-hashes of two files * * 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.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; public class G08_Sha1Kollision { public static void main(String[] args) throws Exception { System.out.println("G08 SHA-1 Kollision = Unsicherheit"); System.out.println("Hinweis: nutzen Sie die SHA-1-Funktion niemals in Echtprogrammen da sie unsicher ist !"); String filename1String = "g08_shattered-1.pdf"; byte[] hash1Byte = generateSha1DoNotUse(filename1String); System.out.println("\nSHA-1-Hashwert der Datei:" + filename1String); System.out.println( "hash1Byte Länge:" + hash1Byte.length + " Data:" + printHexBinary(hash1Byte)); String filename2String = "g08_shattered-2.pdf"; byte[] hash2Byte = generateSha1DoNotUse(filename2String); System.out.println("SHA-1-Hashwert der Datei:" + filename2String); System.out.println( "hash2Byte Länge:" + hash2Byte.length + " Data:" + printHexBinary(hash2Byte)); System.out.println("Die beiden SHA-1-Hashwerte sind gleich:" + Arrays.equals(hash1Byte, hash2Byte)); System.out.println("\nAlternative Berechnung eines SHA-256-Hashwertes"); hash1Byte = generateSha256(filename1String); System.out.println("\nSHA256-Hashwert der Datei:" + filename1String); System.out.println( "hash1Byte Länge:" + hash1Byte.length + " Data:" + printHexBinary(hash1Byte)); hash2Byte = generateSha256(filename2String); System.out.println("SHA256-Hashwert der Datei:" + filename2String); System.out.println( "hash2Byte Länge:" + hash2Byte.length + " Data:" + printHexBinary(hash2Byte)); System.out.println("Die beiden SHA256-Hashwerte sind gleich:" + Arrays.equals(hash1Byte, hash2Byte)); System.out.println("\nG08 SHA-1 Kollision = Unsicherheit beendet"); } public static byte[] generateSha1DoNotUse(String filenameString) throws IOException, NoSuchAlgorithmException { // bitte diese hashfunktion niemals in echtprogrammen nutzen // die hashfunktion ist unsicher // do not use this hashroutine in production // the hashroutine is unsecure File file = new File(filenameString); byte[] bufferByte = new byte[(int) file.length()]; FileInputStream fis = new FileInputStream(file); fis.read(bufferByte); fis.close(); MessageDigest md = MessageDigest.getInstance("SHA1"); md.update(bufferByte); return md.digest(); } public static byte[] generateSha256(String filenameString) throws IOException, NoSuchAlgorithmException { File file = new File(filenameString); byte[] bufferByte = new byte[(int) file.length()]; FileInputStream fis = new FileInputStream(file); fis.read(bufferByte); fis.close(); MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(bufferByte); return md.digest(); } public static String printHexBinary(byte[] bytes) { final char[] hexArray = "0123456789ABCDEF".toCharArray(); char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } } |
Alle Quellcodes zu den Unsicherheiten findet Ihr zum Download in meinem Github-Repository, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/G-Unsicherheit. Alle Programme sind sowohl unter Java 8 als auch unter Java 11 lauffähig.
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 09.12.2019