Dieses Beispiel ist eine vollwertige symmetrische Stream-Dateiverschlüsselungsroutine, welche die Nutzung von zusätzlichen AAD-Daten unterstützt und diese in der verschlüsselten Datei am Anfang mit abspeichert. Im Gegensatz zum Beispiel H03 Symmetrische Verschlüsselung via Tink mit AAD-Daten und einer Datei kann diese Lösung auch großen Dateien verschlüsseln und wieder entschlüsseln, da es zum einen mit einem „Buffered Reader“ als auch mit Google Tinks Streaming-Aead arbeitet. Die ergänzenden AAD-Daten werden mit in der verschlüsselten Datei im Klartext gespeichert und automatisch wieder ausgelesen. Weiterhin wird der erzeugte Schlüssel in Form einer JSON-formatierten Datei gespeichert und dann wieder eingelesen.
Im Beispielcode habe ich den AES GCM-Modus mit 256 Byte Schlüssellänge eingestellt, probiert aber einmal die übrigen Verfahren aus (einfach im Template-Namen mittels copy/paste den gewünschten Modus reinkopieren).
Ihr benötigt für dieses Beispiel eine „plaintext“-Datei, hierfür nutzt Ihr am einfachsten die mit Beispiel A11 Erzeuge eine Datei mit definierter Größe generierte Datei. Probiert einfach mal deutlich größere Dateien aus und testet, wo die Schmerzgrenze bei Euch ist (immer daran denken: auf einem anderen Rechner oder Handy kann die Grenze deutlich niedriger liegen).
Bitte die nachfolgende Routine nur nach einer gründlichen Überprüfung für den Echteinsatz nutzen. Aus kryptographischer Sicht dürfte sie für viele Einsatzgebiete nutzbar sein.
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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
package net.bplaced.javacrypto.tink; /* * 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. * Lizenztext/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): 26.01.2019 * Funktion: verschlüsselt grosse Dateien und AAD-Daten mit Google Tink Streaming AES GCM * Function: encrypts a large file and aad-data using Google Tink Streaming AES GCM * * 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 ! * * Das Programm benötigt die nachfolgende Bibliotheken: * The programm uses these external libraries: * jar-Datei: https://mvnrepository.com/artifact/com.google.crypto.tink/tink * jar-Datei: https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java * jar-Datei: https://mvnrepository.com/artifact/org.json/json */ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.util.Arrays; import com.google.crypto.tink.CleartextKeysetHandle; import com.google.crypto.tink.JsonKeysetReader; import com.google.crypto.tink.JsonKeysetWriter; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.StreamingAead; import com.google.crypto.tink.config.TinkConfig; import com.google.crypto.tink.streamingaead.StreamingAeadFactory; import com.google.crypto.tink.streamingaead.StreamingAeadKeyTemplates; public final class H04_SymmetricStreamEncryptionTinkSaveKeysLargeFile { public static void main(String[] args) throws Exception { System.out.println( "H04 Symmetrische Stream-Verschlüsselung via Tink, Schlüssel Speicherung, mit AAD-Daten für große Dateien"); TinkConfig.register(); String aadtextString = "Hier stehen die AAD-Daten (additional authenticated data)"; String filenameKeyString = "h03_streamingaesgcm256.txt"; String filenamePlainString = "a11_test_1mb.dat"; String filenameEncString = "h03_test_enc.txt"; String filenameDecString = "h03_test_dec.txt"; byte[] aadtextByte = new byte[0]; // damit das array auf jeden fall gefüllt ist aadtextByte = aadtextString.getBytes("utf-8"); // schlüssel erzeugen System.out.println("\nSchlüsselerzeugung und Speicherung"); // streaming gcm KeysetHandle keysetHandle = KeysetHandle.generateNew(StreamingAeadKeyTemplates.AES256_GCM_HKDF_4KB); // StreamingAeasKeyTemplates. AES128_GCM_HKDF_4KB, AES256_GCM_HKDF_4KB // AES128_CTR_HMAC_SHA256_4KB, AES256_CTR_HMAC_SHA256_4KB // schlüssel speichern saveKeyJsonFormat(keysetHandle, filenameKeyString); System.out.println("Der erzeugte Schlüssel wurde gespeichert:" + filenameKeyString); System.out.println("Der Schlüssel ist im Format:" + keysetHandle.getKeysetInfo().getKeyInfo(0).getTypeUrl()); // falls ein schlüssel bereits vorhanden ist wird er so geladen: // KeysetHandle keysetHandle = loadKeyJsonFormat(filenameKeyString); // ist die datei filenamePlainString existent ? if (FileExistsCheck(filenamePlainString) == false) { System.out.println("Die Datei " + filenamePlainString + " existiert nicht. Das Programm wird beendet."); System.exit(0); } ; // verschlüsselung System.out.println("\n# # # Verschlüsselung einer Datei # # #"); System.out.println("Diese aadtext-Daten sind angefügt:\n" + new String(aadtextByte)); encryptStreamingGcmAadTink(keysetHandle, filenamePlainString, filenameEncString, aadtextByte); System.out.println("Die Datei " + filenamePlainString + " wurde verschlüsselt in " + filenameEncString); Arrays.fill(aadtextByte, (byte) 0); // array löschen // entschlüsselung, einlesen des schlüssels und der verschlüsselten datei System.out.println("\n# # # Entschlüsselung einer Datei # # #"); if (FileExistsCheck(filenameKeyString) == false) { System.out.println("Die Datei " + filenameKeyString + " existiert nicht. Das Programm wird beendet."); System.exit(0); } ; if (FileExistsCheck(filenameEncString) == false) { System.out.println("Die Datei " + filenameEncString + " existiert nicht. Das Programm wird beendet."); System.exit(0); } ; // lesen der schlüsseldatei KeysetHandle keysetHandleRead = loadKeyJsonFormat(filenameKeyString); System.out.println("Der Schlüssel wurde gelesen:" + filenameKeyString); // entschlüsseln der datei filenameEncString byte[] aadtextReadByte = decryptStreamingGcmAadTink(keysetHandleRead, filenameEncString, filenameDecString); System.out.println("Die Datei " + filenameEncString + " wurde entschlüsselt in " + filenameDecString); System.out.println("Diese aadtext-Daten sind angefügt:\n" + new String(aadtextReadByte)); } public static void saveKeyJsonFormat(KeysetHandle keysetHandle, String filenameString) throws IOException { CleartextKeysetHandle.write(keysetHandle, JsonKeysetWriter.withFile(new File(filenameString))); } public static KeysetHandle loadKeyJsonFormat(String filenameString) throws GeneralSecurityException, IOException { return CleartextKeysetHandle.read(JsonKeysetReader.withFile(new File(filenameString))); } public static void encryptStreamingGcmAadTink(KeysetHandle keysetHandle, String filenamePlainString, String filenameEncString, byte[] aadtextByte) throws Exception { // test auf ein nicht gefülltes aadtextByte array if (aadtextByte == null) { System.err.println("No data in aadtextByte, programm halted"); System.exit(0); } try (FileInputStream fis = new FileInputStream(filenamePlainString); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream out = new FileOutputStream(filenameEncString); BufferedOutputStream bos = new BufferedOutputStream(out)) { StreamingAead aead = StreamingAeadFactory.getPrimitive(keysetHandle); OutputStream os = aead.newEncryptingStream(bos, aadtextByte); // aad-data writer out.write(integerToFourBytes(aadtextByte.length)); out.write(aadtextByte); // encryption byte[] buf = new byte[4096]; int numRead = 0; while ((numRead = bis.read(buf)) >= 0) { os.write(buf, 0, numRead); } bis.close(); os.close(); Arrays.fill(buf, (byte) 0); // array löschen } Arrays.fill(aadtextByte, (byte) 0); // array löschen } public static byte[] decryptStreamingGcmAadTink(KeysetHandle keysetHandle, String filenameEncString, String filenameDecString) throws IOException, GeneralSecurityException { byte[] aadtextByte = null; try (FileInputStream fis = new FileInputStream(filenameEncString); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream out = new FileOutputStream(filenameDecString); BufferedOutputStream bos = new BufferedOutputStream(out);) { // aad-data reader byte[] aadtextLengthByte = new byte[4]; @SuppressWarnings("unused") int counter = fis.read(aadtextLengthByte, 0, 4); int aadtextLengthInt = byteArrayToInt(aadtextLengthByte); aadtextByte = new byte[aadtextLengthInt]; counter = fis.read(aadtextByte, 0, aadtextLengthInt); // decryption StreamingAead aead = StreamingAeadFactory.getPrimitive(keysetHandle); InputStream in = aead.newDecryptingStream(bis, aadtextByte); byte[] ibuf = new byte[4096]; int numRead = 0; while ((numRead = in.read(ibuf)) >= 0) { if (ibuf != null) bos.write(ibuf, 0, numRead); } Arrays.fill(ibuf, (byte) 0); // array löschen } return aadtextByte; } private static boolean FileExistsCheck(String dateinameString) { return Files.exists(Paths.get(dateinameString), new LinkOption[] { LinkOption.NOFOLLOW_LINKS }); } 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); } public static int byteArrayToInt(byte[] b) { if (b.length == 4) return b[0] << 24 | (b[1] & 0xff) << 16 | (b[2] & 0xff) << 8 | (b[3] & 0xff); else if (b.length == 2) return 0x00 << 24 | 0x00 << 16 | (b[0] & 0xff) << 8 | (b[1] & 0xff); return 0; } public static final byte[] integerToFourBytes(int value) throws Exception { byte[] result = new byte[4]; if ((value > Math.pow(2, 63)) || (value < 0)) { throw new Exception("Integer value " + value + " is larger than 2^63"); } result[0] = (byte) ((value >>> 24) & 0xFF); result[1] = (byte) ((value >>> 16) & 0xFF); result[2] = (byte) ((value >>> 8) & 0xFF); result[3] = (byte) (value & 0xFF); return result; } } |
Die Konsole informiert Euch über den kompletten Ablauf (Schlüsselerzeugung, Verschlüsselung und Entschlüsselung):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
H04 Symmetrische Stream-Verschlüsselung via Tink, Schlüssel Speicherung, mit AAD-Daten für große Dateien Schlüsselerzeugung und Speicherung Der erzeugte Schlüssel wurde gespeichert:h03_streamingaesgcm256.txt Der Schlüssel ist im Format:type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey # # # Verschlüsselung einer Datei # # # Diese aadtext-Daten sind angefügt: Hier stehen die AAD-Daten (additional authenticated data) Die Datei a11_test_1mb.dat wurde verschlüsselt in h03_test_enc.txt # # # Entschlüsselung einer Datei # # # Der Schlüssel wurde gelesen:h03_streamingaesgcm256.txt Die Datei h03_test_enc.txt wurde entschlüsselt in h03_test_dec.txt Diese aadtext-Daten sind angefügt: Hier stehen die AAD-Daten (additional authenticated data) |
Alle Quellcodes zu Google Tink findet Ihr zum Download in meinem Github-Repository, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/H-Google-Tink. 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: 26.01.2019