Die Blockchain in 9 Schritten – Teil 6: Nutzung von Transaktionen
Wir nähern uns unaufhaltsam dem eigenen Geld und da wir nun eine elektronische Geldbörse erstellen können, werden wir sie auch befüllen und Teile davon in eine zweite Geldbörse transferieren. Für den Transfer benötigen wir eine weitere Klasse mit dem Namen „transaction“ (oder auf Deutsch: Transaktion).
Die Klasse wird in den folgenden Teilen noch stark erweitert, enthält aber schon alle Basisfelder für unsere Überweisung. Wie bei einer klassischen Banküberweisung erfassen wir das Auftraggeber Konto („sender“) und das Empfängerkonto („recipient“) sowie den Betrag („value“). Bei einer Online-Überweisung käme noch eine Transaktionsnummer („TAN“) hinzu, hier nutzen wir stilgerecht eine elektronische Unterschrift („signature“).
Damit wir die digitale Signatur auch nutzen können, enthält die Klasse noch 2 Methoden für die Erzeugung und Überprüfung der Unterschrift:
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 |
package net.bplaced.javacrypto.blockchain.step6; /* * Diese Klasse gehört zu BlockChain.java * This class belongs to BlockChain.java */ import java.security.PrivateKey; import java.security.PublicKey; public class Transaction { public PublicKey sender; public PublicKey recipient; public float value; public byte[] signature; public Transaction(PublicKey from, PublicKey to, float value) { this.sender = from; this.recipient = to; this.value = value; } public boolean processTransaction() { if (verifySignature() == false) { System.out.println("#Transaction Signature failed to verify"); return false; } return true; } public void generateSignature(PrivateKey privateKey) { String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(recipient) + Float.toString(value); signature = StringUtil.generateECDSASig(privateKey, data); } public boolean verifySignature() { String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(recipient) + Float.toString(value); return StringUtil.verifyECDSASig(sender, data, signature); } } |
Die Transaktionsklasse wird im Hauptprogramm in Verbindung mit der Wallet-Klasse benutzt:
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 |
package net.bplaced.javacrypto.blockchain.step6; /* * 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. * Lizenztext/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): 29.05.2019 * Projekt/Project: Blockchain * Funktion: Schritt 6: Erstellung einer Transaktion * Function: Step 6: creation of a transaction * * 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 ! * * Das Programm benötigt die nachfolgende Bibliothek: * The programm uses this external library: * jar-Datei: https://mvnrepository.com/artifact/com.google.code.gson/gson * * Das Projekt basiert auf dem nachfolgenden Artikel: * The project is based on this article: * https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa */ import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Date; public class BlockChain { public static ArrayList<Block> blockchain = new ArrayList<Block>(); public static int difficulty = 4; public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException { System.out.println("I06 BlockChain für Anfänger Stufe 06"); System.out.println("\nErzeuge walletA"); Wallet walletA = new Wallet(); System.out.println("walletA privateKey:" + walletA.privateKey); System.out.println("walletA publicKey :" + walletA.publicKey); System.out.println("\nErzeuge walletB"); Wallet walletB = new Wallet(); System.out.println("walletB privateKey:" + walletB.privateKey); System.out.println("walletB publicKey :" + walletB.publicKey); System.out.println("\nErzeuge Transactionen"); Transaction transaction1 = new Transaction(walletA.publicKey, walletB.publicKey, 10); Transaction transaction2 = new Transaction(walletB.publicKey, walletA.publicKey, 5); // System.out.println("transaction1:" + StringUtil.getJson(transaction1)); // System.out.println("transaction2:" + StringUtil.getJson(transaction2)); System.out.println("\nSigniere Transactionen"); transaction1.generateSignature(walletA.privateKey); transaction2.generateSignature(walletB.privateKey); // System.out.println("transaction1:" + StringUtil.getJson(transaction1)); System.out.println("\nVerifiziere Transactionen"); Boolean verifyBool = transaction1.verifySignature(); System.out.println("transaction1 gültig:" + verifyBool); verifyBool = transaction2.verifySignature(); System.out.println("transaction2 gültig:" + verifyBool); } public static void addBlock(Block newBlock) { newBlock.mineBlock(difficulty); blockchain.add(newBlock); } public static Boolean blockchainIsValid() { Block currentBlock; Block previousBlock; for (int i = 1; i < blockchain.size(); i++) { currentBlock = blockchain.get(i); previousBlock = blockchain.get(i - 1); if (!currentBlock.hash.equals(currentBlock.calculateHash())) { return false; } if (!previousBlock.hash.equals(currentBlock.previousHash)) { return false; } } return true; } public static String createDemonstationData(int number) { Date date = new Date(); return "Dataset:" + number + " " + new Timestamp(date.getTime()); } } |
Die Erzeugung und Überprüfung der Signaturen erfolgt ausgelagert in der StringUtil-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 |
package net.bplaced.javacrypto.blockchain.step6; /* * Diese Klasse gehört zu BlockChain.java * This class belongs to BlockChain.java */ import java.security.InvalidKeyException; import java.security.Key; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.util.Base64; import com.google.gson.GsonBuilder; public class StringUtil { public static String generateSha256(String input) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(input.getBytes("UTF-8")); StringBuffer hexString = new StringBuffer(); for (int i = 0; i < hash.length; i++) { String hex = Integer.toHexString(0xff & hash[i]); if (hex.length() == 1) hexString.append('0'); hexString.append(hex); } return hexString.toString(); } catch (Exception e) { throw new RuntimeException(e); } } public static String getJson(Object o) { return new GsonBuilder().setPrettyPrinting().create().toJson(o); } public static String getDifficultyString(int difficulty) { return new String(new char[difficulty]).replace('\0', '0'); } public static byte[] generateECDSASig(PrivateKey privateKey, String input) { Signature dsa; byte[] output = new byte[0]; try { dsa = Signature.getInstance("SHA256withECDSA"); dsa.initSign(privateKey); byte[] strByte = input.getBytes(); dsa.update(strByte); byte[] realSig = dsa.sign(); output = realSig; } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException e) { throw new RuntimeException(e); } return output; } public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) { try { Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA"); ecdsaVerify.initVerify(publicKey); ecdsaVerify.update(data.getBytes()); return ecdsaVerify.verify(signature); } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException e) { throw new RuntimeException(e); } } public static String getStringFromKey(Key key) { return Base64.getEncoder().encodeToString(key.getEncoded()); } } |
Das Hauptprogramm nutzt wieder nicht die eingebaute Blockchain, sondern erstellt lediglich zwei Geldbörsen (Hinweis: unterschiedliche Schlüsselpaare!) und transferiert Münzen („coins“) zwischen den beiden Geldbörsen. Die Konsolenausgabe ist wieder schmucklos knapp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
I06 BlockChain für Anfänger Stufe 06 Erzeuge walletA walletA privateKey:sun.security.ec.ECPrivateKeyImpl@65cf walletA publicKey :Sun EC public key, 192 bits public x coord: 5284020430638269314005965718833279045563842878296024073710 public y coord: 1158645100834228980421659896529678019487327036674091579269 parameters: secp192r1 [NIST P-192, X9.62 prime192v1] (1.2.840.10045.3.1.1) Erzeuge walletB walletB privateKey:sun.security.ec.ECPrivateKeyImpl@fffff9cb walletB publicKey :Sun EC public key, 192 bits public x coord: 3135462022108360661871930480043121143472684834678600924189 public y coord: 4474352178277595349598279540452086834195907343821563241633 parameters: secp192r1 [NIST P-192, X9.62 prime192v1] (1.2.840.10045.3.1.1) Erzeuge Transactionen Signiere Transactionen Verifiziere Transactionen transaction1 gültig:true transaction2 gültig:true |
Der Vollständigkeit halber hier noch der Quellcode der Block-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 |
package net.bplaced.javacrypto.blockchain.step6; /* * Diese Klasse gehört zu BlockChain.java * This class belongs to BlockChain.java */ import java.util.Date; public class Block { public String hash; public String previousHash; private long timeStamp; private String data; private int nonce; public Block(String previousHash, String data) { this.previousHash = previousHash; this.data = data; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); } public String calculateHash() { String calculatedhash = StringUtil .generateSha256(previousHash + Long.toString(timeStamp) + Integer.toString(nonce) + data); return calculatedhash; } public void mineBlock(int difficulty) { String target = StringUtil.getDifficultyString(difficulty); while (!hash.substring(0, difficulty).equals(target)) { nonce++; hash = calculateHash(); } } } |
Weiter geht es mit Teil 7: I07 Vorbereitung des JavaCryptoCoin.
Alle Quellcodes zur Blockchain findet Ihr zum Download in meinem Github-Repository, welches Ihr über diesen Link erreicht: https://github.com/java-crypto/I-Blockchain. Alle Programme sind sowohl unter Java 8 als auch unter Java 11 lauffähig.
Die Lizenz zum obigen Beispiel findet Ihr auf der eigenen Lizenz-Seite.
Letzte Aktualisierung: 29.05.2019