Der EC Kurve 25519 Schlüsselaustausch basiert auf dem Diffie-Hellmann Schlüsselaustausch und wurde um die elliptische Kurven-Kryptographie erweitert. Auch dieser Algorithmus ist mit wenigen Zeilen Programmcode nutzbar und ist ein vollwertiger Schlüsselaustausch-Code. Die zu Grunde liegende Kurve 25519 wurde vom Team Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe und Bo-Yin Yang fand die Kurve 25519, welche seitdem gerne genutzt wird. Dieses Beispiel erweitert den reinen EC-Schlüsselaustausch um eine Unterschriftsmöglichkeit, d.h. das jeweilige Private-/Public Keypaar wird zusätzlich für die digitale Signatur genutzt.
Auffallend ist der vergleichsweise kleine private bzw. öffentliche Schlüssel von 32 Byte Länge, dadurch eignet sich der Schlüsselaustausch und die digitale Signatur insbesondere für den Einsatz auf Karten.
Die aktuellen Java 8- und Java 11-Versionen enthalten keine Implementierung der Kurve und Ihr benötigt eine Dritt-Bibliothek wie, um die Kurve nutzen zu können. Ich zeige Euch hier die Implementierung mit der Bibliothek curve25519, welche Ihr unter diesem Link herunterladen könnt: http://central.maven.org/maven2/org/whispersystems/curve25519-java/0.5.0/curve25519-java-0.5.0.jar.
Bindet die Bibliothek in Eclipse analog zu BouncyCastle ein (beispielhaft gezeigt in http://javacrypto.bplaced.net/a06-einbindung-und-nutzung-von-bouncy-castle/).
Hier noch einmal in Kurzform die Vorgehensweise: beide Partner erzeugen ihre Schlüsselpaare (KeyPairs) und tauschen dann ihre öffentlichen Schlüssel (public keys) aus. Im Programm geschieht das einfach durch „Vertauschen“ in der Ansicht, in der Praxis werden die öffentlichen Schlüssel z.B. auf einer persönlichen Webseite veröffentlicht. Dann erzeugen beide Partner den gemeinsamen geheimen Schlüssel (shared secret key), der dann z.B. für eine symmetrische Verschlüsselung genutzt werden kann. In diesem Beispiel ist der gemeinsame geheime Schlüssel zwar 32 Byte lang und damit „OK“ für AES-Verschlüsselungen, aber bei mittels SHA-256-Hashfunktion auf die Länge von 32 Byte gebrachten Schlüssel sind dadurch nicht mehr zurück rechenbar.
Schlüsselaustauschsteckbrief | |
Name des Verfahrens | EC 25519 |
Langname | Elliptische Kurve 25519 Schlüsselaustausch |
Basis | Elliptische Kurve |
Blocklänge (Byte) | – |
Schlüssellänge (Byte/Bit) | 32/256 |
Padding genutzt | Nein |
Sicherheit | es existiert nur eine 32 Byte Schlüssellänge |
Besonderes | – |
Der nachfolgende Quellcode ist durch die Ausgaberoutinen deutlich länger als der „eigentliche“ Programmcode.
Bitte die nachfolgende Routine nicht für den Echteinsatz nutzen, da sie aus kryptographischer Sicht 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 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 |
package net.bplaced.javacrypto.keyexchange; /* * 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 * getestet mit/tested with: Java Runtime Environment 11.0.1 x64 * Datum/Date (dd.mm.jjjj): 18.01.2019 * Funktion: elektronischer Schlüsselaustausch und Signatur mittels ECDH * Function: digital key exchange and signature using ECDH * * 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 ! */ // benötigt: curve25519-java-0.5.0.jar // source: https://github.com/signalapp/curve25519-java // jar: http://central.maven.org/maven2/org/whispersystems/curve25519-java/0.5.0/curve25519-java-0.5.0.jar import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SignatureException; import org.whispersystems.curve25519.Curve25519; import org.whispersystems.curve25519.Curve25519KeyPair; public class E04_EcCurve25519KeyexchangeSignature { public static void main(String[] args) throws Exception { System.out.println("E04 EC Kurve 25519 Schlüsselaustausch mit Signatur"); // jeder der beiden benutzer erzeugt ein eigenes schluessel-paar // variablen fuer den benutzer a Curve25519KeyPair aKeyPair = generateEcCurve25519KeyPair(); byte[] aPrivateKeyByte = aKeyPair.getPrivateKey(); // der private schluessel von benutzer a byte[] aPublicKeyByte = aKeyPair.getPublicKey(); // der public schluessel von benutzer a byte[] aSharedSecretByte = null; // variablen fuer den benutzer b Curve25519KeyPair bKeyPair = generateEcCurve25519KeyPair(); byte[] bPrivateKeyByte = bKeyPair.getPrivateKey(); // der private schluessel von benutzer b byte[] bPublicKeyByte = bKeyPair.getPublicKey(); // der public schluessel von benutzer b byte[] bSharedSecretByte = null; // ausgabe der schluessel fuer jeden benutzer System.out.println("\n= = = Erzeugung der Schlüssel von Benutzer A = = ="); System.out.println("Benutzer A PrivateKey (Hex):" + printHexBinary(aPrivateKeyByte)); System.out.println("Benutzer A PublicKey (Hex) :" + printHexBinary(aPublicKeyByte)); System.out.println("\n= = = Erzeugung der Schlüssel von Benutzer B = = ="); System.out.println("Benutzer B PrivateKey (Hex):" + printHexBinary(bPrivateKeyByte)); System.out.println("Benutzer B PublicKey (Hex) :" + printHexBinary(bPublicKeyByte)); // nun werden die public keys untereinander getauscht // in der realen welt wird der public key zB per mail oder einer webseite // verteilt // hier werden die beiden public keys "nur" beim jeweils anderen benutzer // angezeigt System.out.println("\n= = = Schlüssel bei Benutzer A = = ="); System.out.println("Benutzer A PrivateKey (Hex):" + printHexBinary(aPrivateKeyByte)); System.out.println("Benutzer B PublicKey (Hex) :" + printHexBinary(bPublicKeyByte)); System.out.println("\n= = = Schlüssel bei Benutzer B = = ="); System.out.println("Benutzer B PrivateKey (Hex):" + printHexBinary(bPrivateKeyByte)); System.out.println("Benutzer A PublicKey (Hex) :" + printHexBinary(aPublicKeyByte)); // erzeugung des shared secret keys bei jedem benutzer // benutzer a aSharedSecretByte = createEcCurve25519SharedSecret(aPrivateKeyByte, bPublicKeyByte); // benutzer b bSharedSecretByte = createEcCurve25519SharedSecret(bPrivateKeyByte, aPublicKeyByte); // ausgabe der shared keys fuer jeden benutzer System.out.println("\n= = = Gemeinsame Schlüssel (SharedSecred) bei den Benutzern = = ="); System.out.println("Benutzer A SharedSecret (Hex):" + printHexBinary(aSharedSecretByte)); System.out.println("Benutzer B SharedSecret (Hex):" + printHexBinary(bSharedSecretByte)); // die laenge des schluessels duerfte zu gross fuer viele aes-verfahren sein, // daher kuerzen wir den schluessel mittels eines hashes System.out.println("\nSchlüssel-Länge des SharedSecretByte :" + aSharedSecretByte.length + " Byte/" + (aSharedSecretByte.length * 8) + " Bit"); // hashing der ausgabe um einen 32 byte = 256 bit schluessel zu erhalten MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] sharedSecret32Byte = digest.digest(aSharedSecretByte); System.out.println( "= = = Der gemeinsame Schlüssel wird per SHA-256 Hash auf eine Länge von 32 Byte gebracht = = ="); System.out.println("SharedSecretByte nach SHA-256Hash (Hex) :" + printHexBinary(sharedSecret32Byte)); System.out.println("Schlüssel-Länge des SharedSecret32Byte :" + sharedSecret32Byte.length + " Byte/" + (sharedSecret32Byte.length * 8) + " Bit"); // erzeugung der digitalen signatur System.out.println("\nDie Nachricht wird mit dem privateKey signiert"); byte[] messageByte = "Das ist die zu signierende Nachricht".getBytes("utf-8"); byte[] signatureByte = signCurve25519PrivateKey(aPrivateKeyByte, messageByte); System.out.println( "signatureByte Länge:" + signatureByte.length + " Data:\n" + byteArrayPrint(signatureByte, 33)); // verifizierung der digitalen signatur System.out.println("\nDie Nachricht wird mit dem publicKey verifiziert"); Boolean signatureIsCorrectBoolean = verifyCurve25519PublicKey(aPublicKeyByte, messageByte, signatureByte); System.out.println( "Überprüfung der Signatur mit dem publicKey: die Signatur ist korrekt:" + signatureIsCorrectBoolean); } public static Curve25519KeyPair generateEcCurve25519KeyPair() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { Curve25519 cipher = Curve25519.getInstance(Curve25519.BEST); return Curve25519.getInstance(Curve25519.BEST).generateKeyPair(); } public static byte[] createEcCurve25519SharedSecret(byte[] aPrivateKeyByte, byte[] bPublicKeyByte) throws NoSuchAlgorithmException, InvalidKeyException { Curve25519 cipher = Curve25519.getInstance(Curve25519.BEST); return cipher.calculateAgreement(bPublicKeyByte, aPrivateKeyByte); } public static byte[] signCurve25519PrivateKey(byte[] privateKeyByte, byte[] messageByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { Curve25519 cipher = Curve25519.getInstance(Curve25519.BEST); return cipher.calculateSignature(privateKeyByte, messageByte); } public static Boolean verifyCurve25519PublicKey(byte[] publicKeyByte, byte[] messageByte, byte[] signatureByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { Curve25519 cipher = Curve25519.getInstance(Curve25519.BEST); return cipher.verifySignature(publicKeyByte, messageByte, signatureByte); } public static String byteArrayPrint(byte[] byteData, int numberPerRow) { String returnString = ""; String rawString = printHexBinary(byteData); int rawLength = rawString.length(); int i = 0; int j = 1; int z = 0; for (i = 0; i < rawLength; i++) { z++; returnString = returnString + rawString.charAt(i); if (j == 2) { returnString = returnString + " "; j = 0; } j++; if (z == (numberPerRow * 2)) { returnString = returnString + "\n"; z = 0; } } return returnString; } 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); } } |
Die Ausgabe auf der Konsole ähnelt dem Beispiel E03 ECDH Schlüsselaustausch mit Signatur sehr:
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 |
E04 EC Kurve 25519 Schlüsselaustausch mit Signatur = = = Erzeugung der Schlüssel von Benutzer A = = = Benutzer A PrivateKey (Hex):D8C9D6FFC6D6292AC282F9601FFA34F6CF6300A1CAB204B9E91729F66257F061 Benutzer A PublicKey (Hex) :1230B8BEF704873AA72A51FF32EE49E6AB36987B34FB2310909F04EE863B3A21 = = = Erzeugung der Schlüssel von Benutzer B = = = Benutzer B PrivateKey (Hex):A050DD7E914FBF78AEB2657D13A3D6E7A60AB68C71CBFB5DCAB4A9638D499F62 Benutzer B PublicKey (Hex) :8D1F2EB8212FD128D8A32EBAAE1464EE65A61A276C31F18007649ADFBC744079 = = = Schlüssel bei Benutzer A = = = Benutzer A PrivateKey (Hex):D8C9D6FFC6D6292AC282F9601FFA34F6CF6300A1CAB204B9E91729F66257F061 Benutzer B PublicKey (Hex) :8D1F2EB8212FD128D8A32EBAAE1464EE65A61A276C31F18007649ADFBC744079 = = = Schlüssel bei Benutzer B = = = Benutzer B PrivateKey (Hex):A050DD7E914FBF78AEB2657D13A3D6E7A60AB68C71CBFB5DCAB4A9638D499F62 Benutzer A PublicKey (Hex) :1230B8BEF704873AA72A51FF32EE49E6AB36987B34FB2310909F04EE863B3A21 = = = Gemeinsame Schlüssel (SharedSecred) bei den Benutzern = = = Benutzer A SharedSecret (Hex):AE9D1C1458ECEC8B73BC655CA04DA64AD09E5AEC2DCE05BB2C5322FEE7BE8B60 Benutzer B SharedSecret (Hex):AE9D1C1458ECEC8B73BC655CA04DA64AD09E5AEC2DCE05BB2C5322FEE7BE8B60 Schlüssel-Länge des SharedSecretByte :32 Byte/256 Bit = = = Der gemeinsame Schlüssel wird per SHA-256 Hash auf eine Länge von 32 Byte gebracht = = = SharedSecretByte nach SHA-256Hash (Hex) :1649CA919EB052CC4E7E77DF50F930915C7AECE2EBF7A6D4384AC69CC451C252 Schlüssel-Länge des SharedSecret32Byte :32 Byte/256 Bit Die Nachricht wird mit dem privateKey signiert signatureByte Länge:64 Data: 97 92 10 DE 65 74 06 5D DC 20 1F 77 B5 3D 0C A3 10 1B C1 5D A1 F6 59 2E 74 10 1A DF 6D CE 45 D6 48 1C 42 B1 9A E1 25 AD 45 A9 17 0C 3A 72 1E 7D 60 9E B0 E1 B6 FC 4E 65 77 6E BD CD D3 00 83 03 Die Nachricht wird mit dem publicKey verifiziert Überprüfung der Signatur mit dem publicKey: die Signatur ist korrekt:true |
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 18.01.2019