Wie werden in Java Nachrichten signiert und vom Empfänger validiert? Das geht mit ein paar Zeile aus dem Java Package java.security. Wir verwenden die auch in Bitcoin verwendeten Elliptische-Kurven-Kryptografie secp256k1. Was wollen wir zu Entspannung nach Feierabend machen?
Das wird in diesem BPMN-Diagramm dargestellt:
Hier die kommentierte Java Klasse: …
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 |
package de.wenzlaff.twhash; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.spec.ECGenParameterSpec; import java.security.spec.EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; /** * ECDSA Beispiel in vier Schritten mit dem java.security Package. * * Es muss ja nicht immer ein andere Lib sein. * * @author Thomas Wenzlaff */ public class ECCSignatur { public ECCSignatur() throws Exception { // 1. Generieren des public/private Key mit Digital Signature Algorithm (ECDSA) // die Keys können wiederverwendet werden System.out.println("Keys erzeugen"); ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256k1"); KeyPairGenerator g = KeyPairGenerator.getInstance("EC"); g.initialize(ecSpec, new SecureRandom("Optional mit Seed".getBytes())); KeyPair keypair = g.generateKeyPair(); PublicKey publicKey = keypair.getPublic(); viewKey("Public", publicKey); PrivateKey privateKey = keypair.getPrivate(); viewKey("Privater", privateKey); // wir versenden diese Testnachricht: String nachricht = "Das ist toll mit den Signaturen und Digital Signature Algorithm (ECDSA)!"; // 2. Signieren der Nachricht mit privaten Key System.out.println("Signieren"); Signature ecdsaSign = Signature.getInstance("SHA256withECDSA"); ecdsaSign.initSign(privateKey); ecdsaSign.update(nachricht.getBytes("UTF-8")); byte[] signature = ecdsaSign.sign(); String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded()); String signaturBase64 = Base64.getEncoder().encodeToString(signature); // 3. Übermitteln der Nachricht den Public Key und die Signatur der Nachricht an // den Empfänger System.out.println("-------------- das wird an den Empfänger übertragen -------------"); System.out.println("Die Nachricht die signiert wurde: " + nachricht); System.out.println("Der Public Key in Base64 Format: " + publicKeyBase64); System.out.println("Die Signatur der Nachricht im Base64 Format: " + signaturBase64); // 4. Der Empfänger überprüft die Nachricht mit dem Public Key und der Signatur System.out.println("-------------- das läuft beim Empfänger -------------"); empfaenger(nachricht, publicKeyBase64, signaturBase64); } private void empfaenger(String nachricht, String publicKeyBase64, String signaturBase64) throws Exception { EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64)); KeyFactory keyFactory = KeyFactory.getInstance("EC"); PublicKey publicKeySender = keyFactory.generatePublic(publicKeySpec); Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA"); // die gleiche wie der Sender ecdsaVerify.initVerify(publicKeySender); // testweise mal einkommentieren um die Nachricht zu verändern // nachricht = "HACK!" + nachricht; // wurde die Nachricht verändert, kommt eine // Fehlermeldung beim verify ecdsaVerify.update(nachricht.getBytes("UTF-8")); // das eigentliche überprüfen und Ergebnis anzeigen System.out.println("Nachricht mit public Key und Signatur überprüfen. Ergebnis:"); boolean result = ecdsaVerify.verify(Base64.getDecoder().decode(signaturBase64)); if (result) { System.out.println("Die übertragene Nachricht und die Signatur passen zusammen."); System.out.println("Die Nachricht lautet: " + nachricht); } else { System.err.println("Fehler, die Signatur passt nicht zur Nachricht!"); } } private void viewKey(String name, Key key) { String encoded = Base64.getEncoder().encodeToString(key.getEncoded()); System.out.println(name + " Key Base64 Encode: " + encoded); System.out.println(name + " Key: " + key); System.out.println(name + " Key Format: " + key.getFormat()); System.out.println(name + " Key Algorithm: " + key.getAlgorithm()); System.out.println(name + " Key Encoded: " + key.getEncoded()); } public static void main(String[] args) throws Exception { new ECCSignatur(); } } |
Das Ergebniss:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Keys erzeugen Public Key Base64 Encode: MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMV0xMubTru/I78sWHYol6Am7hbPGoDhAyPGkkkebh68RlanrTxv6VDEa027wJDYc+f0UrYm70LEgm2wWCMjWCg== Public Key: Sun EC public key, 256 bits public x coord: 22327985915481677429642520300797029992037926573857920778375831531391096162223 public y coord: 7953751377618762713704442597204637820923434765570159547797650155666921805322 parameters: secp256k1 (1.3.132.0.10) Public Key Format: X.509 Public Key Algorithm: EC Public Key Encoded: [B@2c7b84de Privater Key Base64 Encode: MD4CAQAwEAYHKoZIzj0CAQYFK4EEAAoEJzAlAgEBBCCvqRnRUmavJwZgKY6RBZJmgmjvmC7sNZ19R8ThDVBxhg== Privater Key: sun.security.ec.ECPrivateKeyImpl@507 Privater Key Format: PKCS#8 Privater Key Algorithm: EC Privater Key Encoded: [B@3fee733d Signieren -------------- das wird an den Empfänger übertragen ------------- Die Nachricht die signiert wurde: Das ist toll mit den Signaturen und Digital Signature Algorithm (ECDSA)! Der Public Key in Base64 Format: MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMV0xMubTru/I78sWHYol6Am7hbPGoDhAyPGkkkebh68RlanrTxv6VDEa027wJDYc+f0UrYm70LEgm2wWCMjWCg== Die Signatur der Nachricht im Base64 Format: MEUCIGAngAUASUBOmUHpJDMrTus3HdzRNd98wFJ6vviwUIfTAiEA0dDe8DshzqjrpfDtL5zMFpDqTQMq3KG0NZuOeTk33FQ= -------------- das läuft beim Empfänger ------------- Nachricht mit public Key und Signatur überprüfen. Ergebnis: Die übertragene Nachricht und die Signatur passen zusammen. Die Nachricht lautet: Das ist toll mit den Signaturen und Digital Signature Algorithm (ECDSA)! |
Wer will kann auch die Nachricht mal verändern (einkommentieren der HACK Zeile) um den negativen Fall abzubilden.
Die Public Key Ableitung ist eine Einbahnstrassenfunktion, ebenso wie kryptografische Hashfunktionen. Man kann den private Key nicht aus dem public Key herleiten. Die Sicherheit von digitalen Signaturen hängt wesentlich von dieser Eigenschaft ab. Auch erzeugen mehrere Durchläufe des private Key durch die Ableitungsfunktion stets denselben public Key.
Wer mal mit den Elliptic Curve scalar multiplication spielen will, kann diesen Online Rechner verwenden.