Wir ändern das RSA-Beispiel D02 RSA Signatur mit einer Datei in Richtung DSA (= Nutzung des Diskreten Logarithmus) als Rechenverfahren ab. Aus Darstellungsgründen habe ich als Schlüssellänge 512 Bit gewählt, welche allerdings in der realen Welt niemals verwendet werden darf, da sie als unsicher einzustufen ist.
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 | DSA |
Langname | DSA Signatur |
Art der Chiffre | Kompletter Plaintext |
Blocklänge (Byte) | – |
Schlüssellänge (Byte/Bit) | 64/512, 128/1024, 256/2048, 512/4096, 1024/8192 |
Padding genutzt | Nein |
Sicherheit | nur ab 2048 Bit Schlüssellänge |
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 sehr 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 |
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): 09.01.2019 * Funktion: signiert und verifiziert eine Datei mittels DSA (Asymmetrisch) * Function: signs and verifies a file using DSA (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 ! */ 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.KeyFactory; 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.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 javax.xml.bind.DatatypeConverter; public class D03_DsaSignatureFile { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, InvalidKeySpecException, NoSuchProviderException { System.out.println("D03 DSA Signatur mit einer Datei"); String messageFilenameString = "d02_message.txt"; // KeyPair generieren int dsaKeyLengthInt = 512; // 512, 1024, 2048, 4096, 9192 bit String dsaHashverfahrenString = "SHA256withDSA"; // SHA256withDSA, SHA384withDSA, SHA512withDSA String dsaPrivateKeyFilenameString = "dsa_privateKey_" + dsaKeyLengthInt + ".privatekey"; String dsaPublicKeyFilenameString = "dsa_publicKey_" + dsaKeyLengthInt + ".publickey"; String dsaSignatureFilenameString = "dsa_Signature.dat"; KeyPair keyPair = generateDsaKeyPair(dsaKeyLengthInt); PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); // 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 savePrivateKeyAsBytearray(privateKey, dsaPrivateKeyFilenameString); savePublicKeyAsBytearray(publicKey, dsaPublicKeyFilenameString); System.out.println("Der privateKey und publicKey wurden gespeichert:" + dsaPrivateKeyFilenameString + "/" + dsaPublicKeyFilenameString); // 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:" + DatatypeConverter.printHexBinary(messageByte)); // die signatur erfolgt mit dem privaten schlüssel, der jetzt geladen wird PrivateKey privateKeyLoad = loadDsaPrivateKeyAsBytearray(dsaPrivateKeyFilenameString); System.out.println("\nDer privateKey wurde zur Signatur geladen:\"" + dsaPrivateKeyFilenameString); byte[] signatureByte = signPrivateKey(privateKeyLoad, dsaHashverfahrenString, messageByte); System.out.println( "\nsignatureByte Länge:" + signatureByte.length + " Data:\n" + byteArrayPrint(signatureByte, 33)); // speicherung der signatur writeBytesToFileNio(signatureByte, dsaSignatureFilenameString); System.out.println("Die dsaSignatur wurde gespeichert:" + dsaSignatureFilenameString); // die überprüfung der signatur erfolgt mit dem öffentlichen schlüssel, der // jetzt geladen wird PublicKey publicKeyLoad = loadDsaPublicKeyAsBytearray(dsaPublicKeyFilenameString); System.out.println("\nDer publicKey wurde zur Verifizierung geladen:" + dsaPublicKeyFilenameString); byte[] messageLoadByte = calcSHA256BufferedFile(messageFilenameString); System.out.println("Die message wurde gelesen:" + messageFilenameString + " und der SHA256-Hashwert erzeugt:" + DatatypeConverter.printHexBinary(messageLoadByte)); byte[] signatureLoadByte = readBytesFromFileNio(dsaSignatureFilenameString); System.out.println("Die Signature wurde gelesen:" + dsaSignatureFilenameString); boolean signatureIsCorrectBoolean = verifyPublicKey(publicKeyLoad, dsaHashverfahrenString, 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 = verifyPublicKey(publicKeyLoad, dsaHashverfahrenString, messageByte, signatureByte); System.out.println("Überprüfung der Signatur mit dem publicKey: die Signatur ist korrekt:" + signatureIsCorrectBoolean); } public static KeyPair generateDsaKeyPair(int keylengthInt) throws NoSuchAlgorithmException, NoSuchProviderException { KeyPairGenerator keypairGenerator = KeyPairGenerator.getInstance("DSA", "SUN"); SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN"); keypairGenerator.initialize(keylengthInt, random); return keypairGenerator.generateKeyPair(); } 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); } private static void savePrivateKeyAsBytearray(PrivateKey key, String filenameString) throws IOException { PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key.getEncoded()); FileOutputStream fos = new FileOutputStream(filenameString); fos.write(pkcs8EncodedKeySpec.getEncoded()); fos.close(); } private static void savePublicKeyAsBytearray(PublicKey key, String filenameString) throws IOException { X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key.getEncoded()); FileOutputStream fos = new FileOutputStream(filenameString); fos.write((x509EncodedKeySpec).getEncoded()); fos.close(); } private static PrivateKey loadDsaPrivateKeyAsBytearray(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(); KeyFactory keyFactory = KeyFactory.getInstance("DSA", "SUN"); PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey); PrivateKey privateKeyRead = keyFactory.generatePrivate(privateKeySpec); return privateKeyRead; } private static PublicKey loadDsaPublicKeyAsBytearray(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(); KeyFactory keyFactory = KeyFactory.getInstance("DSA", "SUN"); X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey); PublicKey publicKeyRead = keyFactory.generatePublic(publicKeySpec); return publicKeyRead; } 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 = DatatypeConverter.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; } } |
Die Ausgabe auf der Konsole sieht so aus:
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 |
D03 DSA Signatur mit einer Datei private Key Länge:201 Data: 30 81 C6 02 01 00 30 81 A8 06 07 2A 86 48 CE 38 04 01 30 81 9C 02 41 00 FC A6 82 CE 8E 12 CA BA 26 EF CC F7 11 0E 52 6D B0 78 B0 5E DE CB CD 1E B4 A2 08 F3 AE 16 17 AE 01 F3 5B 91 A4 7E 6D F6 34 13 C5 E1 2E D0 89 9B CD 13 2A CD 50 D9 91 51 BD C4 3E E7 37 59 2E 17 02 15 00 96 2E DD CC 36 9C BA 8E BB 26 0E E6 B6 A1 26 D9 34 6E 38 C5 02 40 67 84 71 B2 7A 9C F4 4E E9 1A 49 C5 14 7D B1 A9 AA F2 44 F0 5A 43 4D 64 86 93 1D 2D 14 27 1B 9E 35 03 0B 71 FD 73 DA 17 90 69 B3 2E 29 35 63 0E 1C 20 62 35 4D 0D A2 0A 6C 41 6E 50 BE 79 4C A4 04 16 02 14 0D 02 8F 99 DF 52 1E 35 AF 7B DE 6B 37 82 73 DC 38 28 A3 3C public Key Länge: 243 Data: 30 81 F0 30 81 A8 06 07 2A 86 48 CE 38 04 01 30 81 9C 02 41 00 FC A6 82 CE 8E 12 CA BA 26 EF CC F7 11 0E 52 6D B0 78 B0 5E DE CB CD 1E B4 A2 08 F3 AE 16 17 AE 01 F3 5B 91 A4 7E 6D F6 34 13 C5 E1 2E D0 89 9B CD 13 2A CD 50 D9 91 51 BD C4 3E E7 37 59 2E 17 02 15 00 96 2E DD CC 36 9C BA 8E BB 26 0E E6 B6 A1 26 D9 34 6E 38 C5 02 40 67 84 71 B2 7A 9C F4 4E E9 1A 49 C5 14 7D B1 A9 AA F2 44 F0 5A 43 4D 64 86 93 1D 2D 14 27 1B 9E 35 03 0B 71 FD 73 DA 17 90 69 B3 2E 29 35 63 0E 1C 20 62 35 4D 0D A2 0A 6C 41 6E 50 BE 79 4C A4 03 43 00 02 40 58 E7 24 73 41 2B B4 54 0C 57 9C B2 83 AE E1 F1 A6 3D C0 A2 77 AB 3F D0 F0 6B 1B FA B2 8A 01 0C E5 4A 58 F7 1D 74 55 CE E8 A8 6C 67 E3 79 8E 41 35 94 45 20 DF 13 5A 54 7B 8D FC 06 2C 3C 7E 81 Public Key: Sun DSA Public Key Parameters: p: fca682ce 8e12caba 26efccf7 110e526d b078b05e decbcd1e b4a208f3 ae1617ae 01f35b91 a47e6df6 3413c5e1 2ed0899b cd132acd 50d99151 bdc43ee7 37592e17 q: 962eddcc 369cba8e bb260ee6 b6a126d9 346e38c5 g: 678471b2 7a9cf44e e91a49c5 147db1a9 aaf244f0 5a434d64 86931d2d 14271b9e 35030b71 fd73da17 9069b32e 2935630e 1c206235 4d0da20a 6c416e50 be794ca4 y: 58e72473 412bb454 0c579cb2 83aee1f1 a63dc0a2 77ab3fd0 f06b1bfa b28a010c e54a58f7 1d7455ce e8a86c67 e3798e41 35944520 df135a54 7b8dfc06 2c3c7e81 Der privateKey und publicKey wurden gespeichert:dsa_privateKey_512.privatekey/dsa_publicKey_512.publickey Die Datei wurde gelesen:d02_message.txt und der SHA256-Hashwert erzeugt:446965736520446174656920656E7468E46C742064656E207A75207369676E696572656E64656E20546578742E0D0A546869732066696C6520636F6E7461696E7320746865206461746120666F72207369676E61747572652E Der privateKey wurde zur Signatur geladen:"dsa_privateKey_512.privatekey signatureByte Länge:48 Data: 30 2E 02 15 00 8A 3A F7 7D D2 2A E6 78 CE F5 33 D7 CB ED 41 2E FA 7F 3B E4 02 15 00 93 E6 46 93 D8 CA 27 7E 89 B6 35 8A A0 82 AC 53 B6 26 27 56 Die dsaSignatur wurde gespeichert:dsa_Signature.dat Der publicKey wurde zur Verifizierung geladen:dsa_publicKey_512.publickey Die message wurde gelesen:d02_message.txt und der SHA256-Hashwert erzeugt:446965736520446174656920656E7468E46C742064656E207A75207369676E696572656E64656E20546578742E0D0A546869732066696C6520636F6E7461696E7320746865206461746120666F72207369676E61747572652E Die Signature wurde gelesen:dsa_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.