Dieses Beispiel basiert auf dem Programm H04 Symmetrische Streamverschlüsselung via Tink mit AAD-Daten für große Dateien und hat als Besonderheit die Buchstaben „CLI“ am Ende stehen. Diese stehen für „Command Line Interface“ oder auf Deutsch „Kommandozeilenversion“.
Mit diesem Programm könnt Ihr Dateien beliebiger Größe auf sehr einfache Weise verschlüsseln und wieder entschlüsseln. Zusätzlich können AAD-Daten genutzt werden – diese werden in der verschlüsselten Datei gespeichert und bei der Entschlüsselung wieder angezeigt.
Eine weitere Programmoption ist die alleinige Anzeige der AAD-Daten (ohne Entschlüsselung), dieses ist sehr nützlich, wenn Ihr als AAD-Daten zum Beispiel das Datum des Backups oder einen Backupstand dokumentieren wollt.
An dieser Stelle steht ein Warnhinweis: die erzeugte oder genutzte Schlüsseldatei (mit dem AES GCM 256-Bit-Schlüssel) muss sehr sicher gespeichert werden, denn ohne diese Datei kann weder das Programm noch jemand anderes eine Entschlüsselung vornehmen. Für den Fall des Schlüsselverlustes gilt: „weg ist unwiederbringlich weg“.
Das Programm ist zum Anderen eine Erweiterung des Google „Hello World“ Beispiels (https://github.com/google/tink/tree/master/examples/helloworld/java), daher habe ich die dort genannte Apache 2.0-Lizenz auch für dieses Beispiel gesetzt. Den englischen Lizenztext der Apache 2.0-Lizenz findet Ihr unter http://www.apache.org/licenses/LICENSE-2.0.
Damit Ihr das Programm direkt einsetzen könnt, habe ich Euch eine fertige JAR-Datei erzeugt, welche Ihr über diesen Link: h05_encryptiontinkdecli.jar herunterladen könnt. Falls Ihr wissen möchtet, wie eine solche ausführbare JAR-Datei mittels Eclipse erzeugt wird, schaut Euch das Beispiel A12 ausführbare JAR-Datei erzeugen an.
Das Programm besteht diesmal aus zwei Klassen, die Ihr hier und im Github-Archiv findet.
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 |
package net.bplaced.javacrypto.tink; /* * Copyright (c) 2017 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ /* * Herkunft/Origin: http://javacrypto.bplaced.net/ * Programmierer/Programmer: Michael Fehr * Copyright: Dieses Programm ist unter der Apache Lizenz Version 2.0 freigegeben * Copyright: This software is released under Apache License Version 2.0. * Lizenztext/Licence: <http://www.apache.org/licenses/LICENSE-2.0> * 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 Dateien und AAD-Daten mit Google Tink Streaming AES GCM 256 Bit * Function: encrypts files and aad-data using Google Tink Streaming AES GCM 256 Bit * * 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/args4j/args4j args4j-2.33.jar * jar-Datei: https://mvnrepository.com/artifact/org.json/json */ import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import com.google.crypto.tink.config.TinkConfig; /** * A command-line tool that can encrypt and decrypt large files with * StreamingAead AES256-GCM. * * <p> * This application uses the <a href="https://github.com/google/tink">Tink<a/> * crypto library. */ public final class H05_EncryptionTinkDeCLI { public static void main(String[] args) throws Exception { // Beispiel für Verschlüsselung: // encrypt --keyfile myOwnEncryption.key --in plaintext.txt --out ciphertext.txt // Beispiel für Verschlüsselung mit AAD-Daten: // encrypt --keyfile myOwnEncryption.key --in plaintext.txt --out ciphertext.txt --aaddata "Das sind meine AAD-Daten" // Beispiel für Entschlüsselung: // decrypt --keyfile myOwnEncryption.key --in ciphertext.txt --out decryptedtext.txt // Beispiel für die Anzeige der AAD-Daten // showaad --in ciphertext.txt --keyfile null --out null TinkConfig.register(); System.out.println("Programm H05 Encryption Tink DE CLI"); System.out.println("Source: http://javacrypto.bplaced.net https://github.com/java-crypto/H-Google-Tink"); // checks for unlimited encryption if (restrictedCryptography() == true) { System.out.println("Ihre Java-Version unterstützt nur 128 Bit Schlüssel (eingeschränkte Kryptographie)."); System.out.println("Das Programm kann nicht ausgeführt werden, bitte lassen Sie die uneingeschränkte\n" + "Kryptographie freischalten.\nDas Programm wird jetzt beendet."); System.exit(1); } H05_CommandsDe commands = new H05_CommandsDe(); CmdLineParser parser = new CmdLineParser(commands); try { parser.parseArgument(args); } catch (CmdLineException e) { System.out.println(e); e.getParser().printUsage(System.out); System.exit(1); } try { commands.command.run(); } catch (GeneralSecurityException e) { System.out.println( "Fehler bei der Ver- oder Entschlüsselung - Cannot encrypt or decrypt, got error: " + e.toString()); System.exit(1); } System.out.println("Programm H05 Encryption Tink CLI beendet"); } /** * Determines if cryptography restrictions apply. Restrictions apply if the * value of {@link Cipher#getMaxAllowedKeyLength(String)} returns a value * smaller than {@link Integer#MAX_VALUE} if there are any restrictions * according to the JavaDoc of the method. This method is used with the * transform <code>"AES/CBC/PKCS5Padding"</code> as this is an often used * algorithm that is <a href= * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#impl">an * implementation requirement for Java SE</a>. * * @return <code>true</code> if restrictions apply, <code>false</code> otherwise */ public static boolean restrictedCryptography() { try { return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding") < Integer.MAX_VALUE; } catch (final NoSuchAlgorithmException e) { throw new IllegalStateException( "The transform \"AES/CBC/PKCS5Padding\" is not available (the availability of this algorithm is mandatory for Java SE implementations)", e); } } } |
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 219 220 221 222 |
package net.bplaced.javacrypto.tink; /* * Copyright (c) 2017 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ 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.security.GeneralSecurityException; import java.util.Arrays; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; import org.kohsuke.args4j.spi.SubCommand; import org.kohsuke.args4j.spi.SubCommandHandler; import org.kohsuke.args4j.spi.SubCommands; 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.streamingaead.StreamingAeadFactory; import com.google.crypto.tink.streamingaead.StreamingAeadKeyTemplates; /** * Defines the different sub-commands and their parameters, for command-line * invocation. */ public final class H05_CommandsDe { /** An interface for a command-line sub-command. */ interface Command { public void run() throws Exception; } static class Options { @Option(name = "--keyfile", required = true, usage = "Der Dateiname inkl. Pfad zur Schluesseldatei, wird generiert falls die Datei nicht existiert") File keyfile; @Option(name = "--in", required = true, usage = "Der Dateiname inkl. Pfad zur Eingabedatei") File inFile; @Option(name = "--out", required = true, usage = "Der Dateiname inkl. Pfad zur Ausgabedatei") File outFile; @Option(name = "--aaddata", required = false, usage = "Ergänzende Authentifizierte Daten (AAD) [optional]") String aadData; } /** * Loads a KeysetHandle from {@code keyfile} or generate a new one if it doesn't * exist. */ private static KeysetHandle getKeysetHandle(File keyfile) throws GeneralSecurityException, IOException { if (keyfile.exists()) { // Read the cleartext keyset from disk. // WARNING: reading cleartext keysets is a bad practice. Tink supports // reading/writing // encrypted keysets, see // https://github.com/google/tink/blob/master/doc/JAVA-HOWTO.md#loading-existing-keysets. return CleartextKeysetHandle.read(JsonKeysetReader.withFile(keyfile)); } KeysetHandle keysetHandle = KeysetHandle.generateNew(StreamingAeadKeyTemplates.AES256_GCM_HKDF_4KB); CleartextKeysetHandle.write(keysetHandle, JsonKeysetWriter.withFile(keyfile)); return keysetHandle; } /** * Reads ciphertext from {@code --in} and shows aad-data if available */ public static class ShowAadCommand extends Options implements Command { @Override public void run() throws Exception { System.out.println("Modus Show AAD-Data"); byte[] aadtextByte = null; try (FileInputStream fis = new FileInputStream(inFile); BufferedInputStream bis = new BufferedInputStream(fis);) { // 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); } if (aadtextByte.length > 0) { System.out.println("AAD-Data:" + new String(aadtextByte)); } else { System.out.println("Keine AAD-Daten verfügbar."); } } } /** * Encrypts a file. */ public static class EncryptCommand extends Options implements Command { @Override public void run() throws Exception { System.out.println("Modus Verschlüsselung"); if (inFile.equals(outFile)) { System.out.println("Die Eingabe- und Ausgabedatei sind gleich.\n" + "Das Programm würde die Eingabedatei überschreiben,\n" + "daher wird das Programm jetzt beendet"); System.exit(1); } byte[] aadtextByte = new byte[0]; if (aadData != null) { aadtextByte = aadData.getBytes("utf-8"); } KeysetHandle keysetHandle = getKeysetHandle(keyfile); try (FileInputStream fis = new FileInputStream(inFile); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream out = new FileOutputStream(outFile); 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); // delete array } Arrays.fill(aadtextByte, (byte) 0); // delete array } } /** * Decrypts a file. */ public static class DecryptCommand extends Options implements Command { @Override public void run() throws Exception { System.out.println("Modus Entschlüsselung"); if (inFile.equals(outFile)) { System.out.println("Die Eingabe- und Ausgabedatei sind gleich.\n" + "Das Programm würde die Eingabedatei überschreiben,\n" + "daher wird das Programm jetzt beendet"); System.exit(1); } KeysetHandle keysetHandle = getKeysetHandle(keyfile); byte[] aadtextByte = null; try (FileInputStream fis = new FileInputStream(inFile); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream out = new FileOutputStream(outFile); 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 } if (aadtextByte.length > 0) { System.out.println("AAD-Data:" + new String(aadtextByte)); } } } /** * Converts an Integer to a four Byte long ByteArray. */ 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; } /** * Converts a (Four Byte long) Byte Array to an Integer. */ 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; } @Argument(metaVar = "encrypt|decrypt|showaad", required = true, handler = SubCommandHandler.class, usage = "--keyfile <schluesseldateiname> --in <eingabedateiname> --out <ausgabedateiname>") @SubCommands({ @SubCommand(name = "encrypt", impl = EncryptCommand.class), @SubCommand(name = "decrypt", impl = DecryptCommand.class), @SubCommand(name = "showaad", impl = ShowAadCommand.class) }) Command command; } |
Diese Befehlsfolgen habe ich verwendet:
1 2 3 4 5 |
java -jar h05_encryptiontinkdecli.jar encrypt --keyfile myOwnKey.txt --in plaintext.txt --out ciphertext.txt --aaddata "Hier stehen die AAD-Daten (additional authenticated data)" java -jar h05_encryptiontinkdecli.jar decrypt --keyfile myOwnKey.txt --in ciphertext.txt --out decryptedtext.txt java -jar h05_encryptiontinkdecli.jar showaad --in ciphertext.txt --keyfile null --out null |
Bei der Ausführung in der Konsole seht Ihr diese Ausgaben (Verschlüsselung mit AAD-Daten, Entschlüsselung mit AAD-Daten und reine Anzeige der AAD-Daten:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Programm H05 Encryption Tink DE CLI Source: http://javacrypto.bplaced.net https://github.com/java-crypto/H-Google-Tink Modus Verschlüsselung Programm H05 Encryption Tink CLI beendet Programm H05 Encryption Tink DE CLI Source: http://javacrypto.bplaced.net https://github.com/java-crypto/H-Google-Tink Modus Entschlüsselung AAD-Data:Das sind meine AAD-Daten Programm H05 Encryption Tink CLI beendet Programm H05 Encryption Tink DE CLI Source: http://javacrypto.bplaced.net https://github.com/java-crypto/H-Google-Tink Modus Show AAD-Data AAD-Data:Das sind meine AAD-Daten Programm H05 Encryption Tink CLI beendet -oder- Programm H05 Encryption Tink DE CLI Source: http://javacrypto.bplaced.net https://github.com/java-crypto/H-Google-Tink Modus Show AAD-Data Keine AAD-Daten verfügbar. Programm H05 Encryption Tink CLI beendet |
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 – da es sich um eine abweichende Apache 2.0 Lizenz handelt – auf der http://www.apache.org/licenses/LICENSE-2.0 Lizenz-Seite.
Letzte Aktualisierung: 28.01.2019