Das erste Beispiel in der Rubrik zeigt Euch bildhaft auf, dass eine Verschlüsselung nicht immer bewirkt, das die Daten für Unbefugte verborgen bleiben. Nehmen wir einmal an, Ihr hättet ein bislang unbekanntes Tier entdeckt und verschickt nun ein Foto an einen Kollegen. Natürlich verschlüsselt Ihr das Bild vor dem Versand und nutzt den AES ECB-Modus.
Nachstehend seht Ihr drei Bilder: Das linke (erste) Bild ist Euer Originalbild, das mittlere (zweite) Bild ist im ECB-Modus verschlüsselt und das rechte (dritte) Bild ist im CBC-Modus verschlüsselt. Upps… beim ECB-Bild kann ich zumindest die Umrisse des Tieres erkennen und erahnen, was für ein Tier Du entdeckt hast.
Das Originalbild ist der Linux Pinguin und er stammt von Larry Ewing. Attribution: lewing@isc.tamu.edu Larry Ewing (mailto:lewing@isc.tamu.edu) and The GIMP.
Wenn Ihr Euch den Quellcode anschaut werdet Ihr erst ein wenig stutzen und vielleicht auch meckern „Der verschlüsselt ja gar nicht die komplette Bilddatei“. Da liegt Ihr völlig richtig, denn damit die BMP-Datei von Eurem Bildprogramm angezeigt werden kann benötigt sie einen „richtigen“ Header. Dieser Header würde bei der kompletten Dateiverschlüsselung natürlich „zerstört“ und damit wäre die einfache optische Betrachtung nicht mehr möglich.
Als Originalbild könnt Ihr jede Bitmap-Datei (Dateiendung .bmp) verwenden, den verwendeten Linux-Pinguin könnt Ihr bei mir herunterladen.
Ich greife daher zu dem Kunstgriff, nur die Bilddaten innerhalb der BMP-Datei zu verschlüsseln, um den gewünschten Effekt zu erzielen. Das Fazit aber bleibt: der ECB-Modus ist unsicher und sollte niemals verwendet werden.
Bitte die nachfolgende Routine nicht für den Echteinsatz nutzen, da sie aus kryptographischer Sicht sehr angreifbar ist !
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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
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 * Datum/Date (dd.mm.jjjj): 15.11.2018 * Funktion: liest eine bild datei und verschlüsselt sie im aes ecb modus kein padding * zum vergleich wird dieselbe datei im aes modus cbc kein padding verschlüsselt * Function: encrypts a picture file using aes ecb modus without padding * for comparison reasons the same file gets encrypted using aes modus cbc without padding * * 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.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Paths; import java.io.FileInputStream; import java.io.FileOutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class G01_AesEcbPenguin { public static void main(String[] args) throws Exception { System.out.println("G01 ECB Pinguin"); // es werden ein paar variablen benötigt: String dateinameReadString = "g01_tux.bmp"; // aus der datei wird das plaintextByte eingelesen byte[] ciphertextEcbByte = null; // das byte array nimmt die ecb-verschlüsselten bild-daten auf String dateinameWriteEcbString = "g01_tux_ecb.bmp"; // in diese datei wird das ciphertextByte geschrieben byte[] ciphertextCbcByte = null; // das byte array nimmt die cbv-verschlüsselten bild-daten auf String dateinameWriteCbcString = "g01_tux_cbc.bmp"; // in diese datei wird das ciphertextByte geschrieben // sowohl das keyByte als auch das initvectorByte werden hier fest eingestellt // und sind für die weitere darstellung nicht von belang final byte[] keyByte = "12345678901234567890123456789012".getBytes("UTF-8"); final byte[] initvectorByte = "abcdefghijklmnop".getBytes("UTF-8"); // starten wir nun mit dem programmablauf // zuerst testen wir ob die einzulesende datei existiert if (FileExistsCheck(dateinameReadString) == false) { System.out.println("Die Datei " + dateinameReadString + " existiert nicht. Das Programm wird beendet."); System.exit(0); }; // wir öffnen die original bild datei FileInputStream fileInputStream = new FileInputStream(dateinameReadString); // wir lesen einen teil des headers der bmp-datei ein int HEADER_LENGTH = 14; // 14 byte bmp header byte headerByte[] = new byte[HEADER_LENGTH]; fileInputStream.read(headerByte, 0, HEADER_LENGTH); // nun lesen wir den zweiten teil des bmp-headers ein int INFO_HEADER_LENGTH = 40; // 40-byte bmp info header byte infoheaderByte[] = new byte[INFO_HEADER_LENGTH]; fileInputStream.read(infoheaderByte, 0, INFO_HEADER_LENGTH); // nun wird die eigentlichen bilddaten eingelesen, welche später verschlüsselt // werden byte[] bmpContentByte = new byte[fileInputStream.available()]; fileInputStream.read(bmpContentByte); fileInputStream.close(); // verschlüsselung mittels ecb modus ciphertextEcbByte = new byte[bmpContentByte.length]; ciphertextEcbByte = AesEcbPaddingEncrypt(bmpContentByte, keyByte); // es werden nun die unverschlüsselten headerByte und infoheaderByte // gespeichert, gefolgt vom ecb-verschlüsselten ciphertextEcbByte writeBmpToFile(dateinameWriteEcbString, headerByte, infoheaderByte, ciphertextEcbByte); // verschlüsselung mittels cbc modus ciphertextCbcByte = new byte[bmpContentByte.length]; ciphertextCbcByte = AesCbcNopaddingEncrypt(bmpContentByte, keyByte, initvectorByte); // es werden nun die unverschlüsselten headerByte und infoheaderByte // gespeichert, gefolgt vom cbc-verschlüsselten ciphertextCbcByte writeBmpToFile(dateinameWriteCbcString, headerByte, infoheaderByte, ciphertextCbcByte); System.out.println(""); System.out.println("Dateiname Lesen Original:" + dateinameReadString); System.out.println("Dateiname Schreiben ECB :" + dateinameWriteEcbString); System.out.println("Dateiname Schreiben CBC :" + dateinameWriteCbcString); } private static boolean FileExistsCheck(String dateinameString) { return Files.exists(Paths.get(dateinameString), new LinkOption[] { LinkOption.NOFOLLOW_LINKS }); } public static byte[] AesEcbPaddingEncrypt(byte[] plaintextByte, byte[] keyByte) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { byte[] ciphertextByte = null; // der schlüssel wird in die richtige form gebracht SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES"); // die verschlüsselungsroutine wird mit dem gewünschten parameter erstellt Cipher aesCipherEnc = Cipher.getInstance("AES/ECB/NOPADDING"); // nun wird die routine mit dem schlüssel initialisiert aesCipherEnc.init(Cipher.ENCRYPT_MODE, keySpec); // hier erfolgt nun die verschlüsselung des plaintextes ciphertextByte = aesCipherEnc.doFinal(plaintextByte); return ciphertextByte; } public static byte[] AesCbcNopaddingEncrypt(byte[] plaintextByte, byte[] keyByte, byte [] initvectorByte) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { byte[] ciphertextByte = null; // der schlüssel wird in die richtige form gebracht SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES"); // der initvector wird in die richtige form gebracht IvParameterSpec ivKeySpec = new IvParameterSpec(initvectorByte); // die verschlüsselungsroutine wird mit dem gewünschten parameter erstellt Cipher aesCipherEnc = Cipher.getInstance("AES/CBC/NOPADDING"); // nun wird die routine mit dem schlüssel initialisiert aesCipherEnc.init(Cipher.ENCRYPT_MODE, keySpec, ivKeySpec); // hier erfolgt nun die verschlüsselung des plaintextes ciphertextByte = aesCipherEnc.doFinal(plaintextByte); return ciphertextByte; } public static void writeBmpToFile(String filenameString, byte[] headerByte, byte[] infoheaderByte, byte[] bmpContentByte) throws Exception { FileOutputStream fileOutputStream = new FileOutputStream(filenameString); fileOutputStream.write(headerByte); fileOutputStream.write(infoheaderByte); fileOutputStream.write(bmpContentByte); fileOutputStream.flush(); fileOutputStream.close(); } } |
Die Konsolenausgabe dient nur der Bestätigung der Verschlüsselung – schaut Euch besser die beiden erzeugten (verschlüsselten) Dateien an:
1 2 3 4 5 |
G01 ECB Pinguin Dateiname Lesen Original:g01_tux.bmp Dateiname Schreiben ECB :g01_tux_ecb.bmp Dateiname Schreiben CBC :g01_tux_cbc.bmp |
Wünscht Ihr noch ein Beispiel ? Ladet Euch meine geheime Botschaft in diesem Bild herunter und lasst Sie verschlüsseln – hier das traurige Ergebnis:
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: 15.11.2018