Advanced Java Services | Verschlüsseln und Entschlüsseln |
Wir unterscheiden zwischen symmetrischer und asymmetrischer Verschlüsselung. Bei der symmetrischen Verschlüsselung verwendet man zum Ver- und Entschlüsseln ein- und denselben Schlüssel. Dieser muß also beiden Parteien bekannt sein. Bei der asymmetrischen Verschlüsselung dagegen gibt es zwei Schlüssel, einen öffentlichen und einen privaten. Jeder, der den öffentlichen Schlüssel hat kann damit Daten verschlüsseln, aber nur wer den privaten Schlüssel hat, kann die Daten wieder entschlüsseln. Wie diese Verschlüsselungen im einzelnen arbeiten wird hier nicht besprochen, hier dazu nur soviel: Bei der Verschlüsselung werden schon länger große Primzahlen verwendet. Das Multiplizieren auch von zwei sehr großen Primzahlen ist relativ einfach, das Zerlegen des entstandenen Produkts aber schwer, wenn man die Faktoren nicht kennt. Hierzu ein Beispiel:
Die beiden größten Primzahlen unter 10000 sind 9967 und 9973, ihr Produkt ist die Zahl 99400891, die abgerundete Wurzel daraus 9969. Um den ganzzahligen Teiler zu finden müßte man alle Zahlen von 2 bis 9969 durchprobieren. Natürlich kann man gerade Zahlen und durch 3 teilbare Zahlen überspringen (Teilungsregeln), aber selbst dann bleiben bei diesem einfachen Beispiel noch über 3000 Zahlen übrig die man durchprobieren müßte. Und natürlich kann man noch sehr viel besser filtern. Nimmt man aber genügend große Primzahlen, so ist ein solcher Brute-Force-Angriff technisch zwar möglich, aber unrealistisch, da er viel zu lange dauert - zumindest solange die Technik nicht soweit fortgeschritten ist, daß eine Zerlegung in angemessener Zeit möglich ist. Sobald dies aber möglich ist, ist es auch möglich noch größere Primzahlen zu finden deren Produkt man noch nicht zerlegen kann - und damit beginnt das Katz- und Mausspiel von vorne.
Eine lesenswerte Einführung zu dieser Thematik findet man auf Wikipedia
Name | seit | Blockgröße (bit) | Schlüssellängen (bit) | Entwickler | Lizenz | Verwendung durch, Anwendung in | Quelle |
---|---|---|---|---|---|---|---|
DES (Data Encryption Standard) | 1975 | 64 | 56 | Weiterentwicklung des maßgeblich von Horst Feistel entwickelten Algorithmus "Lucifer" durch ein Team von Entwicklern bei IBM | ? | Geldautomaten | Data Encryption Standard (wikipedia) |
Triple-DES (3DES, DESede) |
1981 | 64 | 168 (effektiv 112) | Walter Tuchman, Ralph Merkle, Martin Hellman u.a. | ? | HBCI (home banking computer interface) | Triple DES (wikipedia) Uni Oldenburg |
IDEA | 1990 | 64 | 128 | ETH Zürich | Ascom Systec | Anwendung in PGP | International Data Encryption Algorithm (wikipedia) |
Blowfish | 1993 | 64 | 32 bis 448 | Bruce Schneider | public domain | OpenSource-Software (Linux) | Blowfish (wikipedia) |
Twofish | 1999 | 128 | 128, 192, 256 | Nachfolger von Blowfish Bruce Schneider, Niels Ferguson, John Kelsey, Doug Whiting, David Wagner, Chris Hall (Schneider empfiewhlt mittlerweile Twofish anzuwenden) |
public domain | Windows | Twofish (wikipedia |
AES | 2000 | 128 | 128, 192, 256 | Joan Daemen und Vincent Rijmen | public domain | in den USA für staatliche Dokumente mit höchster Geheimhaltungsstufe | Advanced Encryption Standard (wikipedia) |
Siehe auch Verschluesselungs-Algorithmen (www.heise.de)
Es gibt in Java eine generelle Einschränkung was die Schlüsselgröße angeht, Java unterstützt per default nur Schlüssel bis 128 bit. Der Grund dafür ist, daß in einigen Ländern Schlüssel mit einer Länge von mehr als 128 bit nicht erlaubt sind (warum wohl... ?). Initialisaiert man also einen KeyGenerator mit mehr als 128 bit, so erhält man die folgende Exception java.security.InvalidKeyException: Illegal key size or default parameters
Will man längere Schlüssel wie etwa Blowfish mit 448 bit, so muß man die Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8 herunterladen. Die Datei mit dem Namen jce_policy-8.zip enthält einen Ordner UnlimitedJCEPolicyJDK8 mit den Archiven local_policy.jar und US_export_policy.jar. Diese müssen ins Vezeichnis ${java.home}/jre/lib/security/ gebracht werden. Diese jar-Dateien enthalten keinen Bytecode sondern Textdateien, die mit grant Rechte vergeben oder einschränken.
Das Verzeichnis enthält bereits diese zwei Dateien jedoch mit teilweise anderem Inhalt. Bei der Installation der JRE muß auf länderspezifische Gegebenheiten Rücksicht genommen werden, da nicht in allen Ländern alle Schlüssellängen erlaubt sind. So sind in der Datei default_local.policy Einschränkungen, damit die JRE in allen Ländern installiert werden kann. Die Standarddatei sieht daher folgendermaßen aus:
// Some countries have import limits on crypto strength. This policy file // is worldwide importable. grant { permission javax.crypto.CryptoPermission "DES", 64; permission javax.crypto.CryptoPermission "DESede", *; permission javax.crypto.CryptoPermission "RC2", 128, "javax.crypto.spec.RC2ParameterSpec", 128; permission javax.crypto.CryptoPermission "RC4", 128; permission javax.crypto.CryptoPermission "RC5", 128, "javax.crypto.spec.RC5ParameterSpec", *, 12, *; permission javax.crypto.CryptoPermission "RSA", *; permission javax.crypto.CryptoPermission *, 128; };
Die von Oracle heruntergeladene Datei default_local.policy vergibt hier deutlich mehr Rechte:
// Country-specific policy file for countries with no limits on crypto strength. grant { // There is no restriction to any algorithms. permission javax.crypto.CryptoAllPermission; };
Wenn man das weiß kann man natürlich auch einfach die vorhandene policy-Datei entsprechend editieren.
Siehe auch stackoverflow - java-security-invalidkeyexception und stackoverflow - java-security-illegal-key-size
Wir zeigen das Vorgehen an Hand des BlowFish-Algorithmus. Für andere symmetrische Algorithmen kann man im Wesentlichen geauso vorgehen.
Man kann das Vorgehen in Java in die folgende Schritte einteilen
Die Umsetzung im Einzelnen
private static void blowFish() { // Verschluesseln mit einem privaten symmetrischen schlüssel try { // Schritt 1: Einen KeyGenerator für eine bestimmte Art der Verschluesselung generieren. (caseINsensitiv) KeyGenerator blowFishKG = KeyGenerator.getInstance("BlowFish"); // NoSuchAlgorithmException // Infos System.out.println("Info KeyGenerator"); // javax.crypto.KeyGenerator@10421f5 System.out.println("Class: " + blowFishKG); // javax.crypto.KeyGenerator@10421f5 System.out.println("Algorithm: " + blowFishKG.getAlgorithm()); // BlowFish System.out.println("Provider: " + blowFishKG.getProvider()); // SunJCE version 1.8 System.out.println(); // Schritt 2: Den KeyGenerator initialisieren. // init kann entfallen, dann wird ein Standardwert genommen, welcher ??? // Keysize must be multiple of 8, and can only range from 32 to 448 (inclusive) // JCE installiert, dann geht es bis 448 // es gibt 5 init()-Methoden, hioer wird die einfachste verwendet blowFishKG.init(448); // InvalidParameterException: // Schritt 3: Key erzeugen SecretKey secretKey = blowFishKG.generateKey(); // Infos System.out.println("Info SecretKey"); // javax.crypto.KeyGenerator@10421f5 System.out.println("Class: " + secretKey.getClass()); // class javax.crypto.spec.SecretKeySpec System.out.println("Algorithm: " + secretKey.getAlgorithm()); // class javax.crypto.spec.SecretKeySpec System.out.println("Format: " + secretKey.getFormat()); // class javax.crypto.spec.SecretKeySpec System.out.println(); // Schritt 4: Cipherinstanz zum gewählten Verschlüsselungsalgorithmus erzeugen Cipher cipher = Cipher.getInstance("BlowFish"); // NoSuchPaddingException //Infos System.out.println("Info Cipher"); // javax.crypto.KeyGenerator@10421f5 System.out.println("Class: " + cipher.getClass()); // System.out.println("Algorithm: " + cipher.getAlgorithm()); System.out.println("BlockSize: " + cipher.getBlockSize()); System.out.println(); // Schritt 5: Verschlüsselungsmodus einschalten und das in Schritt 3 erzeugte Keyobjekt übergeben cipher.init(Cipher.ENCRYPT_MODE, secretKey); // InvalidKeyException // wenn man init wegläßt, dann IllegalStateException: Cipher not initialized // Schritt 6: Daten in ein Bytearray verwandeln und mit der Methode doFinal verschlüsseln System.out.println("Info Data"); String klarText = "Der Mensch kann zwar tun was er will, aber er kann nicht wollen, was er will. [Arthur Schopenhauer (1788 – 1860)]"; System.out.println("Text to encrypt: " + klarText); byte[] arrayToEncrypt = klarText.getBytes(); byte[] encryptedArray = cipher.doFinal(klarText.getBytes()); // IllegalBlockSizeException, BadPaddingException System.out.println("Array to Encrypt length: " + arrayToEncrypt.length); System.out.println("Encrypted Array length: " + encryptedArray.length); // verschlüsselten String ausgeben String encryptedString = new String(encryptedArray); // System.out.println("Encrypted Bytes as String:\n" + encryptedString); System.out.println(); // Schritt 7: Verschlüsseltes Bytearray senden... // Schritt 8: Entschlüsseln cipher.init(Cipher.DECRYPT_MODE, secretKey); // InvalidKeyException byte[] decryptedArray = cipher.doFinal(encryptedArray); // IllegalBlockSizeException, BadPaddingException String decryptedString = new String(decryptedArray); System.out.println("Info Decrypt"); System.out.println("Decrypted Array length: " + decryptedArray.length); System.out.println("Decrypted String: " + decryptedString); } catch(NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) { ex.printStackTrace(); } }
Eine mögliche Ausgabe
Info KeyGenerator Class: javax.crypto.KeyGenerator@10421f5 Algorithm: BlowFish Provider: SunJCE version 1.8 Info SecretKey Class: class javax.crypto.spec.SecretKeySpec Algorithm: Blowfish Format: RAW Info Cipher Class: class javax.crypto.Cipher Algorithm: BlowFish BlockSize: 8 Info Data Text to encrypt: Der Mensch kann zwar tun was er will, aber er kann nicht wollen, was er will. [Arthur Schopenhauer (1788 – 1860)] Array to Encrypt length: 113 Encrypted Array length: 120 Encrypted Bytes as String: gº¤‹R36Ì Ó᱉ĬÙXìmÔ£½´œËðqÃ:‘?ò>׳?u|Ç\í™n0®s?2A†Z£½´œËàFK õµ®e2È/Âs´Î …ÇV—¥ÎæP Info Decrypt Decrypted Array length: 113 Decrypted String: Der Mensch kann zwar tun was er will, aber er kann nicht wollen, was er will. [Arthur Schopenhauer (1788 – 1860)]
Auf den mathematischen Hintergrund wird hier nicht eingegangen. Einiges dazu findet man unter den in der Tabelle angegeben Links zu Wikipedia.
Name | seit | Entwickler | Quelle |
---|---|---|---|
Diffie-Hellman | 1976 | Whitfield Diffie, Martin Hellman, Ralph Merkle. | Diffie-Hellman Schlüsselaustausch (wikipedia) |
RSA | 1977 | Ronald L. Rivest, Adi Shamir und Leonard Adleman | RSA Encryption (wikipedia) |
Merkle-Hellman | 1978 | Ralph Merkle, Martin Hellman u.a. | Merkle-Hellman Encryption (wikipedia) |
McEliece | 1978 | Robert J. McEliece | McEliece Encryption (wikipedia) |
Rabin | 1979 | Michael O. Rabin | Rabin Encryption(wikipedia) |
Chor-Rivest | 1984 | Chor-Rivest Encryption | |
Elgamal | 1985 | Taher Elgamal | Elgamal Encryption (wikipedia) |
Wir zeigen das Vorgehen an Hand des RSA-Algorithmus. Der Unterschied zum ersten Fall ist im wesentlichen, daß nun zwei Schlüssel erzeugt werden, ein öffentlicher und ein privater.
Man kann das Vorgehen in Java in die folgende Schritte einteilen
Die Umsetzung im Einzelnen
private static void rsa() { try { // Schritt 1: Einen KeyPairGenerator für eine bestimmte Art der Verschluesselung generieren. // caseINsensitiv KeyPairGenerator rsaKPG = KeyPairGenerator.getInstance("RSA"); // NoSuchAlgorithmException // Infos System.out.println("Info KeyPairGenerator"); System.out.println("Class: " + rsaKPG); // java.security.KeyPairGenerator$Delegate System.out.println("Algorithm: " + rsaKPG.getAlgorithm()); // RSA System.out.println("Provider: " + rsaKPG.getProvider()); // SunRsaSign version 1.8 System.out.println(); // Schriit 2: Den KeyPairGenerator initialisieren mit der Methode initialize() // es gibt 4 initialize()-Methoden, hier wird die einfachste verwendet // Methode kann entfallen, dann wird als Standardwert 1024 genommen rsaKPG.initialize(2048); // Bitlänge des Modulos // java.security.InvalidParameterException: RSA keys must be at least 512 bits long // Die Größe des Modulos bestimmt die Länge des zu verschlüsselnden Textes // Mit einer Bitlänge von 800 etwa kann man nur Texte bis 102 byte Länge verschlüsseln // Schritt 3a: KeyPair erzeugen KeyPair kp = rsaKPG.generateKeyPair(); // Schritt 3b: PublicKey erzeugen Key publicKey = kp.getPublic(); System.out.println("Info PublicKey"); System.out.println("Class: " + publicKey.getClass()); // sun.security.rsa.RSAPublicKeyImpl System.out.println("toString: " + publicKey); System.out.println("Algorithm: " + publicKey.getAlgorithm()); // RSA System.out.println("Format: " + publicKey.getFormat()); // X.509 System.out.println(); // Schritt 3c: PrivateKey erzeugen Key privateKey = kp.getPrivate(); System.out.println("Info PrivateKey"); System.out.println("Class: " + privateKey); // sun.security.rsa.RSAPrivateCrtKeyImpl System.out.println("Format: " + privateKey.getFormat()); // PKCS#8 System.out.println(); // Schritt 4: Cipherinstanz zum gewählten Verschlüsselungsalgorithmus erzeugen Cipher cipher = Cipher.getInstance("RSA"); // NoSuchPaddingException // Schritt 5: Verschlüsselungsmodus einschalten und das in Schritt 3 erzeugte PublicKey-Objekt übergeben cipher.init(Cipher.ENCRYPT_MODE, publicKey); // InvalidKeyException // wenn man init wegläßt, dann IllegalStateException: Cipher not initialized // Schritt 6: Daten in ein Bytearray verwandeln und mit der Methode doFinal verschlüsseln String klarText = "Der Mensch kann zwar tun was er will, aber er kann nicht wollen, was er will. [Arthur Schopenhauer (1788 – 1860)]"; System.out.println("Text to encrypt: " + klarText); byte[] arrayToEncrypt = klarText.getBytes(); System.out.println("Array to Encrypt length: " + arrayToEncrypt.length); byte[] encryptedArray = cipher.doFinal(klarText.getBytes()); // IllegalBlockSizeException, BadPaddingException System.out.println("Encrypted Array length: " + encryptedArray.length); // verschlüsselten String ausgeben String encryptedString = new String(encryptedArray); // System.out.println("Encrypted Bytes as String: " + encryptedString); System.out.println(); // Schritt 7: verschlüsseltes Bytearray senden... // Schritt 8: Entschlüsseln cipher.init(Cipher.DECRYPT_MODE, privateKey); // InvalidKeyException byte[] decryptedArray = cipher.doFinal(encryptedArray); // IllegalBlockSizeException, BadPaddingException String decryptedString = new String(decryptedArray); System.out.println("Info Decrypt"); System.out.println("Decrypted Array length: " + decryptedArray.length); System.out.println("Decrypted String: " + decryptedString); } catch(NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) { ex.printStackTrace(); } }
Eine mögliche Ausgabe
Info KeyPairGenerator Class: java.security.KeyPairGenerator$Delegate@139a55 Algorithm: RSA Provider: SunRsaSign version 1.8 Info PublicKey Class: class sun.security.rsa.RSAPublicKeyImpl toString: Sun RSA public key, 2048 bits modulus: 2583465803882812016436925079346213543584359728787861396798624130481322147905244512181455917214667104 5377297261986481529401723697704778245537672061378534631213496311201198395324579833978194246741494777 6288823900060794631681491362898516036246404029159316973980261192672349396383432278022040809193788386 8171452012271173419442180809264349123094517891162282880649375597850077760633083138372848698198508432 8345584819405686686373455565242627648975732550000152576669486358027910634460368800739199769074027974 9705646012825420075404566466322336375039793397203082543078798019198406135876254693057424300998631891 59763912388112187 public exponent: 65537 Algorithm: RSA Format: X.509 Info PrivateKey Class: sun.security.rsa.RSAPrivateCrtKeyImpl@fff2e0e5 Format: PKCS#8 Text to encrypt: Der Mensch kann zwar tun was er will, aber er kann nicht wollen, was er will. [Arthur Schopenhauer (1788 – 1860)] Array to Encrypt length: 113 Encrypted Array length: 256 Encrypted Bytes as String: +›ã‹…—x¤?—“þ“Oâ5ºdíËœÝün†í®ö4ëH†£]XÀÖ|ÈÐõJ¨8Ö“‰?s“Ùz¡*‹È7˜÷ÄnÙƒéàh„2%Ù?tz\¨ÈG%7<8‡ÎÁT÷Q¹—˜CýÇ& ©á´ï·²9,…7æ¾ÃÎ?ÊâYC©¶ŠùÕÖ(‘É÷7ã<—‡??PªÚHÒ´»ê0 ¿»œª±ˆ\…iÿ'×|»1½ì®Î9Ö¥T”·¥,Ïs«àcÃã=J)¥F!T‚K Info Decrypt Decrypted Array length: 113 Decrypted String: Der Mensch kann zwar tun was er will, aber er kann nicht wollen, was er will. [Arthur Schopenhauer (1788 – 1860)]
Selbst bei diesen kleinen Beispielprogrammen stützt ein Performancevergleich die Aussagen über die Zeitdauer des Verschlüsselns und Entschlüsselns, in beiden Beispielen wurde ja der gleiche String verschlüsselt. Hier die Zeiten für jeweils fünf Abläufe (in Eclipse)
BlowFish Dauer Verschlüsseln : 312 ms BlowFish Dauer Entschlüsseln : 0 ms BlowFish Dauer Verschlüsseln : 296 ms BlowFish Dauer Entschlüsseln : 0 ms BlowFish Dauer Verschlüsseln : 343 ms BlowFish Dauer Entschlüsseln : 0 nanosec BlowFish Dauer Verschlüsseln : 297 ms BlowFish Dauer Entschlüsseln : 0 nanosec BlowFish Dauer Verschlüsseln : 297 ms BlowFish Dauer Entschlüsseln : 0 nanosec
RSA Dauer Verschlüsseln : 1389 ms RSA Dauer Entschlüsseln : 32 ms Gesamt : 1421 RSA Dauer Verschlüsseln : 1935 ms RSA Dauer Entschlüsseln : 32 ms Gesamt : 1967 RSA Dauer Verschlüsseln : 1154 ms RSA Dauer Entschlüsseln : 31 ms Gesamt : 1185 RSA Dauer Verschlüsseln : 4088 ms RSA Dauer Entschlüsseln : 47 ms Gesamt : 4135 RSA Dauer Verschlüsseln : 983 ms RSA Dauer Entschlüsseln : 31 ms Gesamt : 1014
Man sieht sieht klar, daß RSA deutlich langsamer ist als BlowFish.
Die hybride Verschlüsselung kombiniert die symmetrische und die asymmetrische Verschlüsselung und beseitigt damit die Nachteile beider Verfahren.
Bei der asymmetrischen Verschlüsselung erzeugt man einen öffentliche und einen privaten Schlüssel, den für eine symmetrische Verschlüsselung erzeugte Schlüssel nennen wir den geheimen Schlüssel. Mit dem geheimen Schlüssel werden die Daten verschlüsselt. Das Problem ist nun, daß der geheime Schlüssel dem Partner übermittelt werden muß. Dazu verwendet man das asymmetrische Schlüsselpaar indem man den geheimen Schlüssel asymmetrisch verschlüsselt und so verschickt.
Im folgenden werden zwei Varianten eines Schlüsseltausches beschrieben. Die Ausgangssituation ist in beiden Fällen dieselbe.
Ausgangssituation:
Alice möchte an Bob geheime Daten senden Bob ------------------------------------------------------------------------------>
In der ersten Variante erzeugt Alice den geheimen und Bob den öffentlichen Schlüssel.
Schritt 1:
Alice Bob erzeugt geheimen Schlüssel erzeugt asymmetrisches Schlüsselpaar
Schritt 2:
Alice Bob gibt Alice den öffentlichen Schlüssel Bob <------------------------------------------------------------------------------
Schritt 3:
Alice Bob verschlüsselt ihre Daten mit ihrem geheimen Schlüssel UND den geheimen Schlüssel mit dem öffentlichen Schlüssel
Schritt 4:
Alice schickt verschlüsselte Daten und verschlüsselten Schlüssel an Bob ------------------------------------------------------------------------------>
Schritt 5:
Alice Bob entschlüsselt mit privaten Schlüssel den geheimen Schlüssel und mit diesem die Daten
Nun die zweite Variante. Hier ist es genau umgekehrt, Alice erzeugt den asymmetrischen und Bob den symmetrischjen Schlüssel.
Schritt 1:
Alice Bob erzeugt asymmetrisches Schlüsselpaar erzeugt geheimen Schlüssel
Schritt 2:
Alice Alice gibt Bob den öffentlichen Schlüssel Bob ------------------------------------------------------------------------------>
Schritt 3:
Alice Bob verschlüsselt mit dem öffentlichen Schlüssel den geheimen Schlüssel Bob schickt den verschlüsselten geheimen Schlüssel an Alice <------------------------------------------------------------------------------
Schritt 4:
Alice entschlüsselt den geheimen Schlüssel und verschlüsselt damit ihre Daten
Schritt 5:
Alice schickt verschlüsselte Daten Bob ------------------------------------------------------------------------------> entschlüsselt mit dem geheimen Schlüssel die Daten