In Beispiel D05 EC Signatur mit einer Datei habe ich Euch die Nutzung der JRE-Routinen gezeigt, um eine Signatur auf Basis von Elliptischen Kurven zu erzeugen.
Da es Gerüchte im Internet gibt, das die den Kurven zu Grunde liegenden Parameter von der amerikanischen NSA beeinflusst worden ist, haben sich Krypto Wissenschaftler auf die Suche nach alternativen Kurven gemacht. Das Team Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe, und Bo-Yin Yang fand die Kurve ED25519, welche seitdem Einzug in die Krypto-Welt gehalten hat.
Die aktuelle Java 8-Version enthält keine Implementierung der Kurve und Ihr benötigt eine Dritt-Bibliothek wie BouncyCastle oder EDDSA, um die Kurve nutzen zu können. Da BouncyCastle sicherlich die umfangreichste, damit aber auch die größte Krypto-Bibliothek ist, zeige ich Euch hier die Implementierung mit der Bibliothek EDDSA, welche Ihr unter diesem Link herunterladen könnt: http://central.maven.org/maven2/net/i2p/crypto/eddsa/0.3.0/eddsa-0.3.0.jar. Die EDDSA-Bibliothek ist gerade mal 62 Kilobyte groß, hingegen benötigt BouncyCastle rd. 4 Megabyte Platz. Noch ein Hinweis: erst die noch in der Entwicklung befindliche Version 12 von Java wird die Nutzung der Kurve ED25519 direkt unterstützen.
Bindet die Bibliothek in Eclipse analog zu BouncyCastle ein (beispielhaft gezeigt in http://javacrypto.bplaced.net/a06-einbindung-und-nutzung-von-bouncy-castle/).
Ganz wichtig ist es, sich den Einsatz des Public und des Private Keys in Erinnerung zu rufen, da an dieser Stelle immer wieder Fehler gemacht werden. Bei der asymmetrischen Signatur veröffentlicht der Absender seinen Public (öffentlichen) Schlüssel, z.B. auf seiner Webseite. Der Absender der Nachricht benutzt seinen Private Key für die Erzeugung der Signatur und sendet die Nachricht (ciphertext) zusammen mit der Signatur an den Empfänger. Der Empfänger prüft die Signatur mit dem Public Keys des Absenders.
Hier noch einmal die Kurzform: signieren mit dem Private Key des Absenders, prüfen der Signatur (Verifizierung) mit dem Public Key des Absenders.
Signatursteckbrief | |
Name des Verfahrens | EC Kurve ED25519 |
Langname | Elliptische Kurven Signatur mit Kurve ED25519 |
Art der Chiffre | Kompletter Plaintext |
Blocklänge (Byte) | – |
Schlüssellänge (Byte/Bit) | 48/384 |
Padding genutzt | Nein |
Sicherheit | – |
Besonderes | – |
Der Quellcode ist deutlich länger, dafür ist dieses Beispiel auch gleich in der Praxis einsetzbar, wenn der nachfolgende Sicherheitshinweis beachtet wird. Die erzeugten Schlüssel werden für eine spätere Benutzung in Form von Byte Arrays gespeichert und beim Einlesen mittels geeigneter Routinen wieder als Private und Public Key „regeneriert“. Die zu signierende Nachricht befindet sich in einer Datei und es wird nicht die komplette Nachricht zur Signaturerzeugung genutzt, sondern es wird ein Hashwert der Datei erzeugt und dieser Hashwert wird dann signiert – das verringert die Bearbeitungszeit bei den rechenintensiven Operationen. Die Signatur wird ebenfalls als Byte Array gespeichert und zur (späteren) Verifizierung beim Empfänger wieder eingelesen. Der Empfänger bildet ebenfalls den Hashwert der Nachrichten-Datei und überprüft den Hashwert mit dem Public Key anhand der Signatur.
Zum Test solltet Ihr eine einfache Textdatei mit dem Namen „d02_message.txt“ anlegen, welche Ihr z.B. hier herunterladen könnt. Im Beispiel B05 ist beschrieben, wo die Datei in Eclipse liegen sollte.
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 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 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
package net.bplaced.javacrypto.signature; /* * 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): 13.01.2019 * Funktion: signiert und verifiziert eine Datei mittels EC Kurve ED25519 (Asymmetrisch) * Function: signs and verifies a file using EC curve ED25519 (asymmetric) * * 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 ! */ /* * Wichtiger Hinweis / important notice * Du benötigst eine externe Jar-Bibliothek namens eddsa-0.3.0.jar * You need an external jar-library eddsa-0.3.0.jar * Die Bibliothek ist hier downloadbar / the library can get obtained here: * http://central.maven.org/maven2/net/i2p/crypto/eddsa/0.3.0/eddsa-0.3.0.jar * Die Nutzung der Bibliothek steht unter dieser Lizenz: * Please keep in mind that this library is available under this licence: * Creative Commons Legal Code CC0 1.0 Universal */ //diese version erzeugt 32-byte lange private und public keys //die damit mit bc vergleichbar und austauschbar sind //ebenso werden 32 byte keys eingelesen und in eddsa keys zurück gewandelt //zusätzlich kann das programm aus einem privateKey wieder einen public key erzeugen //diese version kann die 32-byte keys speichern und einlesen import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import net.i2p.crypto.eddsa.EdDSAEngine; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; import net.i2p.crypto.eddsa.KeyPairGenerator; import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec; import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; public class D06_EcCurveEd25519SignatureFile_32byte { public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, NoSuchProviderException { System.out.println("D06 EC Kurve ED25519 (Bibliothek eddsa-0.3.0.jar) Signatur mit einer Datei"); String messageFilenameString = "d02_message.txt"; // KeyPair generieren KeyPair keyPair = generateEcEd25519KeyPair(); PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); String ecEd25519PrivateKeyFilenameString = "ec_privateKey_" + "ED25519" + "_32byte.privatekey"; String ecEd25519PublicKeyFilenameString = "ec_publicKey_" + "ED25519" + "_32byte.publickey"; String ecEd25519SignatureFilenameString = "ec_ed25519_Signature.dat"; // ausgabe der schlüsseldaten System.out.println("\nprivate Key Länge:" + privateKey.getEncoded().length + " Data:\n" + byteArrayPrint(privateKey.getEncoded(), 33)); System.out.println("\npublic Key Länge: " + publicKey.getEncoded().length + " Data:\n" + byteArrayPrint(publicKey.getEncoded(), 33)); System.out.println("\nPublic Key: " + publicKey.toString()); // speicherung der beiden keys saveEd25519_32byte_PrivateKeyAsBytearray(privateKey, ecEd25519PrivateKeyFilenameString); saveEd25519_32byte_PublicKeyAsBytearray(publicKey, ecEd25519PublicKeyFilenameString); System.out.println("Der privateKey und publicKey wurden gespeichert:" + ecEd25519PrivateKeyFilenameString + "/" + ecEd25519PublicKeyFilenameString); // diese nachricht soll signiert werden // hier wird der hashwert der datei signiert um die datenmenge innerhalb der // signatur gering zu halten: byte[] messageByte = calcSHA256BufferedFile(messageFilenameString); System.out.println("\nDie Datei wurde gelesen:" + messageFilenameString + " und der SHA256-Hashwert erzeugt:" + printHexBinary(messageByte)); // die signatur erfolgt mit dem privaten schlüssel, der jetzt geladen wird PrivateKey privateKeyLoad = loadEcEd25519_32byte_PrivateKeyAsBytearray(ecEd25519PrivateKeyFilenameString); System.out.println("\nDer privateKey wurde zur Signatur geladen:\"" + ecEd25519PrivateKeyFilenameString); byte[] signatureByte = signEcEd25519PrivateKey(privateKeyLoad, messageByte); System.out.println( "\nsignatureByte Länge:" + signatureByte.length + " Data:\n" + byteArrayPrint(signatureByte, 33)); // speicherung der signatur writeBytesToFileNio(signatureByte, ecEd25519SignatureFilenameString); System.out.println("Die ecSignatur wurde gespeichert:" + ecEd25519SignatureFilenameString); // die überprüfung der signatur erfolgt mit dem öffentlichen schlüssel, der // jetzt geladen wird PublicKey publicKeyLoad = loadEcEd25519_32byte_PublicKeyAsBytearray(ecEd25519PublicKeyFilenameString); System.out.println("\nDer publicKey wurde zur Verifizierung geladen:" + ecEd25519PublicKeyFilenameString); byte[] messageLoadByte = calcSHA256BufferedFile(messageFilenameString); System.out.println("Die message wurde gelesen:" + messageFilenameString + " und der SHA256-Hashwert erzeugt:" + printHexBinary(messageLoadByte)); byte[] signatureLoadByte = readBytesFromFileNio(ecEd25519SignatureFilenameString); System.out.println("Die Signature wurde gelesen:" + ecEd25519SignatureFilenameString); boolean signatureIsCorrectBoolean = verifyEcEd25519PublicKey(publicKeyLoad, messageLoadByte, signatureLoadByte); System.out.println( "\nÜberprüfung der Signatur mit dem publicKey: die Signatur ist korrekt:" + signatureIsCorrectBoolean); // veränderung der nachricht System.out.println("\nVeränderung der Nachricht"); messageByte = "Nachricht fuer Signatur2".getBytes("utf-8"); System.out.println("Veränderte-Nachricht hex :" + byteArrayPrint(messageByte, 33)); signatureIsCorrectBoolean = verifyEcEd25519PublicKey(publicKeyLoad, messageByte, signatureLoadByte); System.out.println( "\nÜberprüfung der Signatur mit dem publicKey: die Signatur ist korrekt:" + signatureIsCorrectBoolean); } public static KeyPair generateEcEd25519KeyPair() { SecureRandom secureRandom = new SecureRandom(); KeyPairGenerator keyPairGenerator = new KeyPairGenerator(); keyPairGenerator.initialize(256, secureRandom); return keyPairGenerator.generateKeyPair(); } public static byte[] signEcEd25519PrivateKey(PrivateKey privateKey, byte[] messageByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); Signature signature = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm())); signature.initSign(privateKey); signature.update(messageByte); return signature.sign(); } public static Boolean verifyEcEd25519PublicKey(PublicKey publicKey, byte[] messageByte, byte[] signatureByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); Signature signature = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm())); signature.initVerify(publicKey); signature.update(messageByte); return signature.verify(signatureByte); } private static void saveEd25519_32byte_PrivateKeyAsBytearray(PrivateKey key, String filenameString) throws IOException, InvalidKeySpecException { PKCS8EncodedKeySpec priEncoded = new PKCS8EncodedKeySpec(key.getEncoded()); EdDSAPrivateKey eddsaPrivateKey = new EdDSAPrivateKey(priEncoded); byte[] priSeedByte = eddsaPrivateKey.getSeed(); FileOutputStream fos = new FileOutputStream(filenameString); fos.write(priSeedByte); fos.close(); } private static void saveEd25519_32byte_PublicKeyAsBytearray(PublicKey key, String filenameString) throws IOException, InvalidKeySpecException { X509EncodedKeySpec pubEncoded = new X509EncodedKeySpec(key.getEncoded()); EdDSAPublicKey eddsaPublicKey = new EdDSAPublicKey(pubEncoded); byte[] pubSeedByte = eddsaPublicKey.getA().toByteArray(); FileOutputStream fos = new FileOutputStream(filenameString); fos.write(pubSeedByte); fos.close(); } private static PrivateKey loadEcEd25519_32byte_PrivateKeyAsBytearray(String filenameString) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { File filenameKeyString = new File(filenameString); FileInputStream fis = new FileInputStream(filenameKeyString); byte[] encodedPrivateKey = new byte[(int) filenameKeyString.length()]; fis.read(encodedPrivateKey); fis.close(); EdDSAParameterSpec ed25519Spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); EdDSAPrivateKeySpec privateKeyRebuildSpec = new EdDSAPrivateKeySpec(encodedPrivateKey, ed25519Spec); return new EdDSAPrivateKey(privateKeyRebuildSpec); } private static PublicKey loadEcEd25519_32byte_PublicKeyAsBytearray(String filenameString) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { File filenameKeyString = new File(filenameString); FileInputStream fis = new FileInputStream(filenameKeyString); byte[] encodedPublicKey = new byte[(int) filenameKeyString.length()]; fis.read(encodedPublicKey); fis.close(); EdDSAParameterSpec ed25519Spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); EdDSAPublicKeySpec publicKeyRebuildSpec = new EdDSAPublicKeySpec(encodedPublicKey, ed25519Spec); return new EdDSAPublicKey(publicKeyRebuildSpec); } private static byte[] readBytesFromFileNio(String filenameString) { byte[] byteFromFileByte = null; try { byteFromFileByte = Files.readAllBytes(Paths.get(filenameString)); } catch (IOException e) { e.printStackTrace(); } return byteFromFileByte; } public static byte[] calcSHA256BufferedFile(String filenameString) throws IOException, NoSuchAlgorithmException { // liest die datei über einen buffer ein - sehr viel geringere speichernutzung byte[] buffer = new byte[8192]; int count; MessageDigest digest = MessageDigest.getInstance("SHA-256"); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filenameString)); while ((count = bis.read(buffer)) > 0) { digest.update(buffer, 0, count); } bis.close(); byte[] hash = digest.digest(); return hash; } private static void writeBytesToFileNio(byte[] byteToFileByte, String filenameString) { try { Path path = Paths.get(filenameString); Files.write(path, byteToFileByte); } catch (IOException e) { e.printStackTrace(); } } 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 Konsole informiert Euch wie folgt:
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 |
D06 EC Kurve ED25519 (Bibliothek eddsa-0.3.0.jar) Signatur mit einer Datei private Key Länge:48 Data: 30 2E 02 01 00 30 05 06 03 2B 65 70 04 22 04 20 1A F8 A8 1D 40 BF A0 E1 C3 18 A4 82 D0 8A 83 6B 9D 45 90 0B 5D AC 8D 02 5A 48 9C FC 56 B1 BA 31 public Key Länge: 44 Data: 30 2A 30 05 06 03 2B 65 70 03 21 00 8B C9 BC E0 4F 53 FC 24 79 57 97 8E B4 01 8C 8F 37 03 CE CD 60 53 55 C6 81 68 C0 0E 84 4E 3E 9E Public Key: net.i2p.crypto.eddsa.EdDSAPublicKey@de93ff62 Der privateKey und publicKey wurden gespeichert:ec_privateKey_ED25519_32byte.privatekey/ec_publicKey_ED25519_32byte.publickey Die Datei wurde gelesen:d02_message.txt und der SHA256-Hashwert erzeugt:B11A0F35B132509FA779A5A1C87F69FDD75ADC635654C834CF3F686976732C52 Der privateKey wurde zur Signatur geladen:"ec_privateKey_ED25519_32byte.privatekey signatureByte Länge:64 Data: 87 18 35 95 E7 F1 66 34 33 B1 68 7B E3 E2 B6 85 37 EF 55 09 12 38 77 13 D4 81 DC B9 67 C7 7B 32 D9 9D 19 B3 83 D8 95 87 DC 54 EB CC C5 61 89 5F C3 C9 84 8F E1 A3 1B D4 40 13 4A EC 88 10 86 0F Die ecSignatur wurde gespeichert:ec_ed25519_Signature.dat Der publicKey wurde zur Verifizierung geladen:ec_publicKey_ED25519_32byte.publickey Die message wurde gelesen:d02_message.txt und der SHA256-Hashwert erzeugt:B11A0F35B132509FA779A5A1C87F69FDD75ADC635654C834CF3F686976732C52 Die Signature wurde gelesen:ec_ed25519_Signature.dat Überprüfung der Signatur mit dem publicKey: die Signatur ist korrekt:true Veränderung der Nachricht Veränderte-Nachricht hex :4E 61 63 68 72 69 63 68 74 20 66 75 65 72 20 53 69 67 6E 61 74 75 72 32 Überprüfung der Signatur mit dem publicKey: die Signatur ist korrekt:false |
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 13.01.2019