准备一个钱包
在加密货币中,在区块链作为交易时,货币所有权可以进行转移,每个参与者都有一个自己私有的地址来发送或者是收取货币。,钱包可以存储这些地址。因此钱包就是可以在区块链上进行新交易的软件。
Don’t worry about the information on the transaction, that will be explained soon
让我们创建一个钱包类来保存公钥和私钥:
package noobchain;
import java.security.*;
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey;
}
公钥和私钥究竟是起到什么作用呢,其实公钥的作用就是地址,你可以分享你的公钥给别人以此来获取付款,而你的私钥的作用是为了对交易进行签名,这样其他人就不可以花费你的金额除非它拥有你的私钥,所以对于每个人而言我们必须保护好我们的私钥,不能透露我们的私钥信息给其他人。同时在我们进行交易的时候我们也会同时发送我们的公钥由此来验证我们的签名是有效的而且没有数据被篡改。
我们在密钥对KeyPair生成私有和公钥。我们将使用椭圆曲线加密来生成我们的密钥对KeyPair。让我们将generateKeyPair()方法添加到我们的钱包类中,并在构造函数中调用它:
私钥用于签署我们不想被篡改的数据。公钥用于验证签名。
package noobchain;
import java.security.*;
public class Wallet {
public PrivateKey privateKey;
public PublicKey publicKey;
public Wallet(){
generateKeyPair();
}
public void generateKeyPair() {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA","BC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime192v1");
// Initialize the key generator and generate a KeyPair
keyGen.initialize(ecSpec, random); //256 bytes provides an acceptable security level
KeyPair keyPair = keyGen.generateKeyPair();
// Set the public and private keys from the keyPair
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
}catch(Exception e) {
throw new RuntimeException(e);
}
}
}
你不需要完全理解椭圆曲线加密算法的核心逻辑究竟是什么,你只需要它是用来创建公钥和私钥,以及公钥和私钥分别所起到的作用是什么就可以了。
现在我们已经又了钱包类的大概框架,下面我们再来看一下交易类。
交易和数字签名
每笔交易将携带一定以下信息:
- 资金付款人的公匙信息。
- 资金收款人的公匙信息。
- 被转移资金的金额。
- 输入,它是对以前的交易的引用,证明发送者有资金发送。
- 输出,显示交易中收款方相关地址数量。(这些输出被引用为新交易的输入)
- 一个加密签名,证明该交易是由地址的发送者是发送的,并且数据没有被更改。(阻止第三方机构更改发送的数量)
让我们创建这个新的交易类:
import java.security.*;
import java.util.ArrayList;
public class Transaction {
public String transactionId; // this is also the hash of the transaction.
public PublicKey sender; // senders address/public key.
public PublicKey reciepient; // Recipients address/public key.
public float value;
public byte[] signature; // this is to prevent anybody else from spending funds in our wallet.
public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();
public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();
private static int sequence = 0; // a rough count of how many transactions have been generated.
// Constructor:
public Transaction(PublicKey from, PublicKey to, float value, ArrayList<TransactionInput> inputs) {
this.sender = from;
this.reciepient = to;
this.value = value;
this.inputs = inputs;
}
// This Calculates the transaction hash (which will be used as its Id)
private String calulateHash() {
sequence++; //increase the sequence to avoid 2 identical transactions having the same hash
return StringUtil.applySha256(
StringUtil.getStringFromKey(sender) +
StringUtil.getStringFromKey(reciepient) +
Float.toString(value) + sequence
);
}
}
我们还应该创建空的transactioninput和transactionoutput类,不要担心我们可以在后面填写它们。
我们的交易类还将包含生成/验证签名和验证交易的相关方法,那么签名的意图是什么?签名在我们的区块链中执行了两个非常重要的任务:第一,签名用来保证只有货币的拥有者才可以用来发送自己的货币,第二,签名用来阻止其他人试图篡改提交的交易。
即私钥被用来签名数据,而公钥用来验证其完整性。
举个例子:Bob 想要发送2个加密货币给Sally,他们用各自的钱包创建了交易,并提交到全网的区块链中作为一个新的区块,一个挖矿者试图篡改接受者把2个加密货币给John,但是幸运的事,Bob在交易数据中已经用私钥进行了签名,这就允许了全网中的任何节点使用小明的公匙进行验证数据是否已经被篡改(因为没有其他人的公钥可以用来验证小明发出的这笔交易)。
从前面的代码中我们可以看到我们的签名将是一堆字符串,所以让我们创建一个方法来生成它们。首先我们在StringUtil类中创建产生签名的方法。
public static byte[] applyECDSASig(PrivateKey privateKey, String input) {
Signature dsa;
byte[] output = new byte[0];
try {
dsa = Signature.getInstance("ECDSA", "BC");
dsa.initSign(privateKey);
byte[] strByte = input.getBytes();
dsa.update(strByte);
byte[] realSig = dsa.sign();
output = realSig;
} catch (Exception e) {
throw new RuntimeException(e);
}
return output;
}
//Verifies a String signature
public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) {
try {
Signature ecdsaVerify = Signature.getInstance("ECDSA", "BC");
ecdsaVerify.initVerify(publicKey);
ecdsaVerify.update(data.getBytes());
return ecdsaVerify.verify(signature);
}catch(Exception e) {
throw new RuntimeException(e);
}
}
public static String getStringFromKey(Key key) {
return Base64.getEncoder().encodeToString(key.getEncoded());
}
不用担心你不能理解这些方法的内容,你所需要知道的就是applyECDSASig方法的输入参数为付款人的私钥和需要加密的数据信息,签名后返回字节数组。而verifyECDSASig方法的输入参数为公钥、加密的数据和签名,调用该方法后返回true或false来说明签名是否是有效的。getStringFromKey返回任何key的编码字符串
让我们使用签名的方法在交易类中,增加generatesiganature() 和 varifiysignature()方法:
//Signs all the data we dont wish to be tampered with.
public void generateSignature(PrivateKey privateKey) {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Float.toString(value) ;
signature = StringUtil.applyECDSASig(privateKey,data);
}
//Verifies the data we signed hasnt been tampered with
public boolean verifiySignature() {
String data = StringUtil.getStringFromKey(sender) + StringUtil.getStringFromKey(reciepient) + Float