Der ECDH 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. Auch hier gilt wieder: „glaubst Du den Machern der Kurve“? Falls nicht biete ich Euch im Beispiel E04 EC Kurve25519 Schlüsselaustausch Signatur eine alternative Kurve zur Nutzung an. Dieses Beispiel erweitert den reinen ECDH-Schlüsselaustausch in Beispiel E02 ECDH Schlüsselaustausch um eine Unterschriftsmöglichkeit, d.h. das jeweilige Private-/Public Keypaar wird zusätzlich für die digitale Signatur genutzt.
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 der Nutzung einer „längeren“ Kurve entstehen wieder „zu lange“ Schlüssel – diese werden mittels SHA-256-Hashfunktion auf die Länge von 32 Byte gebracht; alternativ wäre auch die Nutzung von HKDF (Kryptografische Erzeugung von abgeleiteten Schlüsseln) denkbar.
Schlüsselaustauschsteckbrief | |
Name des Verfahrens | ECDH |
Langname | Elliptische Kurven mit Diffie-Hellmann Schlüsselaustausch |
Basis | Elliptische Kurven kombiniert mit dem diskreten Logarithmus |
Blocklänge (Byte) | – |
Schlüssellänge (Byte/Bit) | 256/2048, 384/3072, 521/4168 |
Padding genutzt | Nein |
Sicherheit | ab 2048 Bit 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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
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): 14.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 ! */ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.spec.ECGenParameterSpec; import javax.crypto.KeyAgreement; public class E03_EcdhKeyexchangeSignature { public static void main(String[] args) throws Exception { System.out.println("E03 ECDH Schlüsselaustausch mit Signatur"); String ecdhCurvenameString = "secp256r1"; // standard kurvennamen // secp256r1 [NIST P-256, X9.62 prime256v1] // secp384r1 [NIST P-384] // secp521r1 [NIST P-521] // jeder der beiden benutzer erzeugt ein eigenes schluessel-paar // variablen fuer den benutzer a KeyPair aKeyPair = generateEcdhKeyPair(ecdhCurvenameString); PrivateKey aPrivateKey = aKeyPair.getPrivate(); // der private schluessel von benutzer a PublicKey aPublicKey = aKeyPair.getPublic(); // der public schluessel von benutzer a byte[] aSharedSecretByte = null; // variablen fuer den benutzer b KeyPair bKeyPair = generateEcdhKeyPair(ecdhCurvenameString); PrivateKey bPrivateKey = bKeyPair.getPrivate(); // der private schluessel von benutzer b PublicKey bPublicKey = bKeyPair.getPublic(); // 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(aPrivateKey.getEncoded())); System.out.println("Benutzer A PublicKey (Hex) :" + printHexBinary(aPublicKey.getEncoded())); System.out.println("Benutzer A PublicKey :" + aPublicKey.toString()); System.out.println("\n= = = Erzeugung der Schlüssel von Benutzer B = = ="); System.out.println("Benutzer B PrivateKey (Hex):" + printHexBinary(bPrivateKey.getEncoded())); System.out.println("Benutzer B PublicKey (Hex) :" + printHexBinary(bPublicKey.getEncoded())); System.out.println("Benutzer B PublicKey :" + bPublicKey.toString()); // 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(aPrivateKey.getEncoded())); System.out.println("Benutzer B PublicKey (Hex) :" + printHexBinary(bPublicKey.getEncoded())); System.out.println("\n= = = Schlüssel bei Benutzer B = = ="); System.out.println("Benutzer B PrivateKey (Hex):" + printHexBinary(bPrivateKey.getEncoded())); System.out.println("Benutzer A PublicKey (Hex) :" + printHexBinary(aPublicKey.getEncoded())); // erzeugung des shared secret keys bei jedem benutzer // benutzer a aSharedSecretByte = createEcdhSharedSecret(aPrivateKey, bPublicKey); // benutzer b bSharedSecretByte = createEcdhSharedSecret(bPrivateKey, aPublicKey); // 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"); String ecHashverfahrenString = "SHA256withECDSA"; byte[] signatureByte = signPrivateKey(aPrivateKey, ecHashverfahrenString, 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 = verifyPublicKey(aPublicKey, ecHashverfahrenString, messageByte, signatureByte); System.out.println("Überprüfung der Signatur mit dem publicKey: die Signatur ist korrekt:" + signatureIsCorrectBoolean); } public static KeyPair generateEcdhKeyPair(String curvenameString) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "SunEC"); ECGenParameterSpec ecParameterSpec = new ECGenParameterSpec(curvenameString); keyPairGenerator.initialize(ecParameterSpec); return keyPairGenerator.genKeyPair(); } public static byte[] createEcdhSharedSecret(PrivateKey privateKey, PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException { KeyAgreement keyAgree = KeyAgreement.getInstance("ECDH"); keyAgree.init(privateKey); // initialisierung mit dem private key des benutzers a keyAgree.doPhase(publicKey, true); // ergaenzung mit dem public key des benutzers b return keyAgree.generateSecret(); // erzeugung des shared keys bei benutzer a } public static byte[] signPrivateKey(PrivateKey privateKey, String rsaInstanceString, byte[] messageByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { Signature signature = Signature.getInstance(rsaInstanceString); signature.initSign(privateKey); signature.update(messageByte); return signature.sign(); } public static Boolean verifyPublicKey(PublicKey publicKey, String rsaInstanceString, byte[] messageByte, byte[] signatureByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { Signature publicSignature = Signature.getInstance(rsaInstanceString); publicSignature.initVerify(publicKey); publicSignature.update(messageByte); return publicSignature.verify(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); } } |
Auf der Konsole erscheinen zusätzliche Angaben zur Signatur und Verifizierung:
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 |
E03 ECDH Schlüsselaustausch mit Signatur = = = Erzeugung der Schlüssel von Benutzer A = = = Benutzer A PrivateKey (Hex):3041020100301306072A8648CE3D020106082A8648CE3D03010704273025020101042046BBAFA6495F954F9F4BF3B05AA7B0685418C2205BE80E77A750F8ACA202ED8F Benutzer A PublicKey (Hex) :3059301306072A8648CE3D020106082A8648CE3D03010703420004286BBAD0803E641343161DFA530E63F23D65F2FFED8284171958680BA7F816A66898FADBBA03450B178D82B523EA111DFF91AABC4DDBA16B285FE7CAC7674A11 Benutzer A PublicKey :Sun EC public key, 256 bits public x coord: 18282855925256991939324030475092553266659794772675742826134694624838976804518 public y coord: 47310828366910467882979660570364978684341362913575589120714202547147793451537 parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7) = = = Erzeugung der Schlüssel von Benutzer B = = = Benutzer B PrivateKey (Hex):3041020100301306072A8648CE3D020106082A8648CE3D03010704273025020101042090C97D9521726CA947994347D65E1D3DF86EE42E944554F61D39D1EDA11FBDBF Benutzer B PublicKey (Hex) :3059301306072A8648CE3D020106082A8648CE3D03010703420004E8C08898A9B9AA9577C09F18383C3902EC7FD399F0844AB11B1F6C46C016655A3022920B9771711FDAC3B78D65C09D4A59E4F69E4A88AE0C39935E39983AFFD1 Benutzer B PublicKey :Sun EC public key, 256 bits public x coord: 105276758261044464812917812113468484457734015552544883716611440400535978927450 public y coord: 21772097499674119799745099755837903748298621457412220220532334967920069902289 parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7) = = = Schlüssel bei Benutzer A = = = Benutzer A PrivateKey (Hex):3041020100301306072A8648CE3D020106082A8648CE3D03010704273025020101042046BBAFA6495F954F9F4BF3B05AA7B0685418C2205BE80E77A750F8ACA202ED8F Benutzer B PublicKey (Hex) :3059301306072A8648CE3D020106082A8648CE3D03010703420004E8C08898A9B9AA9577C09F18383C3902EC7FD399F0844AB11B1F6C46C016655A3022920B9771711FDAC3B78D65C09D4A59E4F69E4A88AE0C39935E39983AFFD1 = = = Schlüssel bei Benutzer B = = = Benutzer B PrivateKey (Hex):3041020100301306072A8648CE3D020106082A8648CE3D03010704273025020101042090C97D9521726CA947994347D65E1D3DF86EE42E944554F61D39D1EDA11FBDBF Benutzer A PublicKey (Hex) :3059301306072A8648CE3D020106082A8648CE3D03010703420004286BBAD0803E641343161DFA530E63F23D65F2FFED8284171958680BA7F816A66898FADBBA03450B178D82B523EA111DFF91AABC4DDBA16B285FE7CAC7674A11 = = = Gemeinsame Schlüssel (SharedSecred) bei den Benutzern = = = Benutzer A SharedSecret (Hex):EAC07136A0F5DA78F9BF12602E45716F54BE3CD002D89B3CB1503F7F832E23A3 Benutzer B SharedSecret (Hex):EAC07136A0F5DA78F9BF12602E45716F54BE3CD002D89B3CB1503F7F832E23A3 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) :DD280AAD0B32810359EC6BED259BA95CA5A94054201144D307BB7FC0BE5212BC Schlüssel-Länge des SharedSecret32Byte :32 Byte/256 Bit Die Nachricht wird mit dem privateKey signiert signatureByte Länge:70 Data: 30 44 02 20 00 AF BD 43 C1 C6 52 65 D2 23 62 82 1D 4D 5B E4 10 90 0A DE E2 5A 19 11 E4 F3 A6 A0 79 9B F1 8D 02 20 38 70 A6 F5 0E 8A 2A B0 E3 0D A5 3D 1B 78 38 11 56 C4 63 DF D3 D1 2D 69 D8 80 1D 51 2C D3 F3 38 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