Der hier vorgestellte Betriebsmodus GCM (Galois Counter Mode) führt nicht nur eine Verschlüsselung aus, sondern schützt die verschlüsselten Daten (ciphertext) auch vor einer Veränderung. Damit ist der GCM-Modus eine Kombination des AES CTR-Betriebsmodus mit einem „Keyed-Hash Message Authentication Code“ (MAC), also einer Art Prüfsumme.
Dieser Modus sieht in der Implementierung etwas schwieriger aus, aber der Mehraufwand lohnt sich, denn er sichert die Übertragung vor einer beabsichtigten oder unbeabsichtigten Veränderung der geheimen Daten.
Hierzu nutzt der Modus einen zusätzlichen „Hash-Tag“, der die Datenmenge geringfügig erhöht. Der GCM-Modus bietet zusätzlich noch die Verwendung von ergänzenden Daten an – dieses zeige ich Euch im Beispiel B14.
Wichtig ist es, den Zufalls-GCM Nonce zusammen mit den verschlüsselten Daten an den Empfänger zu übermitteln, denn nur so kann er die Daten wieder entschlüsseln.
Auch wenn ich angebe, das hier kein Padding genutzt wird, so kann dennoch eine beliebige Anzahl an Daten verschlüsselt werden. Der Grund liegt im CTR-Modus (als einem Teil des GCM Modus) – er arbeitet als Stream-Cipher und damit unabhängig von einer festen Blockgröße.
Hier der Steckbrief des Verfahrens:
Verschlüsselungssteckbrief | |
Name des Verfahrens | AES/GCM/NOPADDING |
Langname | GCM Galois_Counter Mode |
Art der Chiffre | Blockchiffre, auch Stream-Chiffre |
Blocklänge (Byte) | 16 |
Schlüssellänge (Byte/Bit) | 16/128, 24/192, 32/256 |
Padding genutzt | Nein |
Sicherheit | sicher bei Nutzung von unterschiedlichen GCM Nonces |
Besonderes | Benötigt einen GCM Nonce, der als Zufallszahl erzeugt wird |
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 |
package net.bplaced.javacrypto.symmetricencryption; /* * 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: verschlüsselt einen Text im AESs GCM Modus kein Padding * Function: encrypts a text string using AES GCM modus with no 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.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; public class B12_AesGcmNoPaddingRandomString { public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { System.out.println("B12 AES im Betriebsmodus GCM Kein Padding mit Zufalls-GCM Nonce mit einem String"); // es werden ein paar variablen benötigt: String plaintextString = "Dieses ist der zu verschlüsselnde Text"; byte[] plaintextByte = plaintextString.getBytes("UTF-8"); final int GCMNONCELENGTH = 12; // = 96 bit String decryptedtextString = ""; // enthält später den entschlüsselten text // diese konstanten und variablen benötigen wir zur ver- und entschlüsselung // der schlüssel ist exakt 32 zeichen lang und bestimmt die stärke der // verschlüsselung. mögliche schlüssellängen sind 16 byte (128 bit), // 24 byte (192 bit) und 32 byte (256 bit) final byte[] keyByte = "12345678901234567890123456789012".getBytes("UTF-8"); // 32 byte // GENERATE random nonce (number used once) final byte[] gcmNonceByte = new byte[GCMNONCELENGTH]; SecureRandom secureRandomGcm = new SecureRandom(); secureRandomGcm.nextBytes(gcmNonceByte); // der verschluesselte (encrypted) text kommt in diese variable in form eines // byte arrays byte[] ciphertextByte = null; // die länge steht noch nicht fest, da sie von der größe des plaintextes abhängt // der entschlüsselte (decrypted) text kommt in dieses byte array, welches // später in einen string umkodiert wird byte[] decryptedtextByte = null; // die länge steht noch nicht fest, da sie von der größe des plaintextes // abhängt // ab hier arbeiten wir nun im verschlüsselungsmodus // umwandlung des klartextes in ein byte array plaintextByte = plaintextString.getBytes("UTF-8"); // hier erfolgt nun die verschlüsselung des plaintextes ciphertextByte = AesGcmNoPaddingEncrypt(plaintextByte, keyByte, gcmNonceByte); System.out.println(""); System.out.println("Klartextdaten einlesen und als verschlüsselte Daten ausgeben"); System.out.println("keyByte (hex) :" + DatatypeConverter.printHexBinary(keyByte)); System.out.println("gcmNonceByte (hex) :" + DatatypeConverter.printHexBinary(gcmNonceByte)); System.out.println("plaintextByte (hex) :" + DatatypeConverter.printHexBinary(plaintextByte)); System.out.println("plaintextString :" + plaintextString); System.out.println("= = = Verschlüsselung = = ="); System.out.println("ciphertextByte (hex) :" + DatatypeConverter.printHexBinary(ciphertextByte)); // ab hier arbeiten wir nun im entschlüsselungsmodus // nun wird der ciphertext wieder entschlüsselt decryptedtextByte = AesGcmNoPaddingDecrypt(ciphertextByte, keyByte, gcmNonceByte); // zurück-kodierung des byte array in text decryptedtextString = new String(decryptedtextByte, "UTF-8"); // ausgabe der variablen System.out.println(""); System.out.println("Verschlüsselte Daten einlesen und als Klartext-Daten ausgeben"); System.out.println("keyByte (hex) :" + DatatypeConverter.printHexBinary(keyByte)); System.out.println("gcmNonceByte (hex) :" + DatatypeConverter.printHexBinary(gcmNonceByte)); System.out.println("ciphertextByte (hex) :" + DatatypeConverter.printHexBinary(ciphertextByte)); System.out.println("= = = Entschlüsselung = = ="); System.out.println("decryptedtextByte (hex):" + DatatypeConverter.printHexBinary(decryptedtextByte)); System.out.println("decryptedtextString :" + decryptedtextString); } public static byte[] AesGcmNoPaddingEncrypt(byte[] plaintextByte, byte[] keyByte, byte[] gcmNonceByte) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { final int GCM_TAG_LENGTH = 128; byte[] ciphertextByte = null; // der schlüssel wird in die richtige form gebracht SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES"); // statt eines initvectors wird ein gcm parameter benoetigt GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, gcmNonceByte); // die verschlüsselungsroutine wird mit dem gewünschten parameter erstellt Cipher aesCipherEnc = Cipher.getInstance("AES/GCM/NoPadding"); // nun wird die routine mit dem schlüssel initialisiert aesCipherEnc.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec); // hier erfolgt nun die verschlüsselung des plaintextes ciphertextByte = aesCipherEnc.doFinal(plaintextByte); return ciphertextByte; } public static byte[] AesGcmNoPaddingDecrypt(byte[] ciphertextByte, byte[] keyByte, byte[] gcmNonceByte) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { final int GCM_TAG_LENGTH = 128; byte[] decryptedtextByte = null; // der schlüssel wird in die richtige form gebracht SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES"); // statt eines initvectors wird ein gcm parameter benoetigt GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, gcmNonceByte); // die verschlüsselungsroutine wird mit dem gewünschten parameter erstellt Cipher aesCipherDec = Cipher.getInstance("AES/GCM/NoPadding"); // nun wird die routine mit dem schlüssel initialisiert aesCipherDec.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec); // hier erfolgt nun die verschlüsselung des plaintextes decryptedtextByte = aesCipherDec.doFinal(ciphertextByte); return decryptedtextByte; } } |
Die Konsolenausgabe sieht so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
B12 AES im Betriebsmodus GCM Kein Padding mit Zufalls-GCM Nonce mit einem String Klartextdaten einlesen und als verschlüsselte Daten ausgeben keyByte (hex) :3132333435363738393031323334353637383930313233343536373839303132 gcmNonceByte (hex) :42FEBE6081090531ED64D177 plaintextByte (hex) :4469657365732069737420646572207A75207665727363686CC3BC7373656C6E64652054657874 plaintextString :Dieses ist der zu verschlüsselnde Text = = = Verschlüsselung = = = ciphertextByte (hex) :7E0E53713A84141E5B5286B3EAD6E252CEB471D6C054F7E58B212919B3D598F6311D6EFE15C6192AC56690D6DE256E710023A30B6AFDC8 keyByte (hex) :3132333435363738393031323334353637383930313233343536373839303132 gcmNonceByte (hex) :42FEBE6081090531ED64D177 ciphertextByte (hex) :7E0E53713A84141E5B5286B3EAD6E252CEB471D6C054F7E58B212919B3D598F6311D6EFE15C6192AC56690D6DE256E710023A30B6AFDC8 = = = Entschlüsselung = = = decryptedtextByte (hex):4469657365732069737420646572207A75207665727363686CC3BC7373656C6E64652054657874 decryptedtextString :Dieses ist der zu verschlüsselnde Text |
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 15.11.2018