讀完了 <區塊鏈 領導幹部讀本> , 以及看了一部分<區塊鏈原理,設計與應用>,
通過對區塊鏈的一些瞭解跟隨網絡博客完成demo
創建區塊鏈
區塊鏈就是一串或者是一系列區塊的集合,類似於鏈表的概念,每個區塊都指向於後面一個區塊,然後順序的連接在一起. 那麼每個區塊中內容是? 區塊鏈中的每一個區塊都存放了很多有價值的信息, 只要包括3個部分 : 自己的數字簽名, 上一個區塊的數字簽名, 還有一切需要加密的數據 ( 這些數據在比特幣中就相當於是交易的信息,它是加密貨幣的本質). 每個數字簽名不但證明了自己是特有的一個區塊, 而且指向了前一個區塊的來源,讓所有的區塊在鏈條中可以串起來,而數據就是一些特定的信息, 你可以按照業務邏輯來保存業務數據.
block
這裏的hash指的就是數字簽名
所以每一個區塊不僅包含前一個區塊的hash值, 同時包含自身的一個hash值, 自身的hash值是通過之前的hash值和數據data通過hash計算出來的. 如果前一個區塊的數據一旦被篡改了, 那麼前一個區塊的hash值也會同樣發生變化 (因爲數據也被計算在內) ,這樣也就導致了所有後續的區塊中的hash值, 所以計算和對比hash值會讓我們檢查到當前的區塊鏈是否是有效的, 也就避免了數據被惡意篡改的可能性, 因爲篡改數據就會改變hash值並破壞整個區塊鏈.
定義區塊鏈的類Block:
package com.sha256.sha256.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
Data
@NoArgsConstructor
@AllArgsConstructor
public class Block {
private String hash; // our signature
private String previousHash; // the hash of previous block
private String data; //our data will be a simple message.
private long timeStamp; //as number of milliseconds since 1/1/1970.
//Block Constructor
public Block(String data,String previousHash){
this.data = data;
this.previousHash = previousHash;
this.timeStamp = new Date().getTime();
}
}
String hash是我們的數字簽名, 變量previousHash保存前一個區塊的hash值, String data是保存我們區塊的數據(比如交易轉賬信息).
創建數字簽名
熟悉加密算法的朋友,Java方式可以實現的加密方式很多, 例如BASE, MD, RSA ,SHA 等等, 我在這裏選用了SHA256這種加密方式, SHA ( Secure Hash Algorithm ) 安全散列算法, 這種算法的特點是數據的少量更改會在Hash值中產生不可預知的大量更改, hash值用作表示大量數據的固定大小的唯一值, 而SHA256算法的hash值大小爲256位. 之所以選用SHA256是因爲它的大小正合適, 一方面產生重複hash值的可能性很小, 另一方面在區塊鏈實際應用過程中, 有可能會產生大量的區塊, 而使得信息量很大, 那麼256位的大小就比較恰當了.
密碼學與安全技術
節選<區塊鏈原理,設計與應用>
測試SHA256加密:
package com.sha256.sha256.test;
import com.sha256.sha256.utils.SHA256Util;
public class TestSHA256 {
public static void main(String[] args) {
String message0 = "我是要被加密的信息";
String message1 = "我是要被加密的信息";
String message2 = "我是要被加密的信息.";
String encryptionMessage0 = SHA256Util.applySha256(message0);
String encryptionMessage1 = SHA256Util.applySha256(message1);
String encryptionMessage2 = SHA256Util.applySha256(message2);
System.out.println(encryptionMessage0);
System.out.println(encryptionMessage1);
System.out.println(encryptionMessage2);
}
}
輸出:
2d7641299aba44f11e8b567dc55f9a45c5218e20bdb65d1306020bfb09fe2f31
2d7641299aba44f11e8b567dc55f9a45c5218e20bdb65d1306020bfb09fe2f31
2a6588b9fd3b412176b4cf499c23f1aa06b35843e6082ca0ab2227f4129bc805
Hash算法與數字摘要:
Hash定義:
Hash(哈希或散列)算法是非常基礎也非常重要的計算機算法,它能將任意長度的二進制明文串映射爲較短的(通常是固定長度的)二進制串(Hash值), 並且不同的明文很難映射爲相同的Hash值.
這意味着對於某個文件,無需查看其內容,只要其SHA-256 Hash計算後結果同樣爲:
2d7641299aba44f11e8b567dc55f9a45c5218e20bdb65d1306020bfb09fe2f31,
則說明文件內容極大概率上就是 –> 我是要被加密的信息 幾個字.
Hash值在應用中又常被稱爲指紋(fingerprint)或摘要(digest). Hash算法的核心思想也經常被應用到基於內容的編址或命名算法中.
一個優秀的Hash算法將能實現如下功能:
正向快速 : 給定明文和Hash算法, 在有限時間和有限資源內能計算得到Hash值.
逆向困難 : 給定(若干)Hash值, 在有限時間內很難(基本不可能)逆推出明文;
輸入敏感 : 原始輸入信息發生任何改變,新產生的Hahs值都應該出現很大不同;(見上面的三個字符串的比較)
衝突避免 : 很難找到兩端內容不同的明文,使得它們的Hash值一致(發生碰撞).
衝突避免有時候又稱爲”抗碰撞性”, 分爲”弱抗碰撞性”和”強抗碰撞性”. 如果給定明文前提下, 無法找到與之碰撞的其他明文, 則算法具有”弱抗碰撞性”, 如果無法找到任意兩個發生Hash碰撞的明文, 則稱算法具有”強抗碰撞性”.
很多場景下,也往往要求算法對於任意長的輸入內容,可以輸入定長的Hash值結果.
常見算法
Hash算法和MD5等
加密安全
提示: MD5是一個經典的Hash算法,和SHA-1算法一起都被認爲安全性已不足應用於商業場景.
調用工具類的SHA256算法:
package com.sha256.sha256.utils;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class SHA256Util {
//Applies SHA256 to a string and returns the result
//SHA256 encryption
public static String applySha256(String input){
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
//Applies sha256 to our input
byte[] hash = digest.digest(input.getBytes("UTF-8"));
//This will contain hash as hexidecimal
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 (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
}
強化Block實體類:
對hash值進行賦值:
package com.sha256.sha256.bean;
import com.sha256.sha256.utils.SHA256Util;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Block {
private String hash; // our signature
private String previousHash; // the hash of previous block
private String data; //our data will be a simple message.
private long timeStamp; //as number of milliseconds since 1/1/1970.
//Block Constructor
public Block(String data,String previousHash){
this.data = data;
this.previousHash = previousHash;
this.timeStamp = new Date().getTime();
this.hash = SHA256Util.calculateHash(this); //Making sure we do this after we set the other values.
}
}
同時在工具類中:
加入新的方法 calculateHash:
package com.sha256.sha256.utils;
import com.sha256.sha256.bean.Block;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class SHA256Util {
//Applies SHA256 to a string and returns the result
//SHA256 encryption
public static String applySha256(String input){
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
//Applies sha256 to our input
byte[] hash = digest.digest(input.getBytes("UTF-8"));
//This will contain hash as hexidecimal
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 (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
//calculate the hash use previoushash , timestamp , data
public static String calculateHash(Block block){
String calculateHash = SHA256Util.applySha256(block.getPreviousHash() + Long.toString(block.getTimeStamp()) + block.getData());
return calculateHash;
}
}
測試:
package com.sha256.sha256.test;
import com.sha256.sha256.bean.Block;
import com.sha256.sha256.utils.SHA256Util;
public class TestSHA256 {
public static void main(String[] args) {
//test1 測試三個被加密字符串 加密後的hash值的差別
/**
* 雖然第三條信息僅僅多一個".",但加密後的數據hash相差極大
*/
String message0 = "我是要被加密的信息";
String message1 = "我是要被加密的信息";
String message2 = "我是要被加密的信息.";
String encryptionMessage0 = SHA256Util.applySha256(message0);
String encryptionMessage1 = SHA256Util.applySha256(message1);
String encryptionMessage2 = SHA256Util.applySha256(message2);
System.out.println(encryptionMessage0);
System.out.println(encryptionMessage1);
System.out.println(encryptionMessage2);
//創建區塊鏈邏輯, 因爲第一個塊沒有上一個塊的hash頭部值,所以輸入0 作爲前一個塊的previous hash
/**
* 由於在{@link SHA256Util#calculateHash(Block)}
* 中對同時產生的new Date().getTime() (timestamp)
* 也加入進行了hash加密,所以固有的message (data)及
* previoushash之和進行了加密.
*/
Block genesisBlock = new Block("這是第一個區塊中的要被加密的信息和交易信息","0");
String hash1 = genesisBlock.getHash();
System.out.println("Hash for block 1 : "+hash1);
Block secondBlock = new Block("這是第二個區塊,以及其中信息!!!它的前區塊頭部hash我們拿上一個的來使用",hash1);
String hash2 = secondBlock.getHash(); //
System.out.println("Hash for block 2 : "+hash2);
Block thirdBlock = new Block("這是第三個區塊,它的hash應該已經被前兩個的信息納入進來了,它的hash如果對不上,那麼說明前面的信息被改動過了",hash2);
String hash3 = thirdBlock.getHash();
System.out.println("Hash for block 3 : "+hash3);
}
}
運行結果:
- 由於在{@link SHA256Util#calculateHash(Block)}
- 中對同時產生的new Date().getTime() (timestamp)
- 也加入進行了hash加密,所以固有的message (data)及
- previoushash之和進行了加密.
2d7641299aba44f11e8b567dc55f9a45c5218e20bdb65d1306020bfb09fe2f31
2d7641299aba44f11e8b567dc55f9a45c5218e20bdb65d1306020bfb09fe2f31
2a6588b9fd3b412176b4cf499c23f1aa06b35843e6082ca0ab2227f4129bc805
Hash for block 1 : cdb1bb85e8f2394f3cee57d82800f5413848fa6c981fefa0fd204497f853c8b4
Hash for block 2 : fad4bc33a9b9f5fc5053fe3583b6bf366be9ea518936ce37d58b916e2c4699be
Hash for block 3 : 558ff9aac60aea20da1936a78a863195cbe23748f08fa34219bb3abc66078b65
注意: 每次 Hash for block * 的產生的值是不同的,因爲每次對timestamp進行了計算
每一個區塊都必須要有自己的數據簽名即hash值,這個hash值依賴於自身的信息(data)和上一個區塊的數字簽名(previousHash), 但這個還不是區塊鏈, 下面讓我們存儲區塊到數組中, 這裏我會引入gson包, 目的是可以用json方式查看整個一條區塊鏈結構.
看test3
package com.sha256.sha256.test;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.GsonBuilder;
import com.sha256.sha256.bean.Block;
import com.sha256.sha256.utils.SHA256Util;
import java.util.ArrayList;
public class TestSHA256 {
//聲明一個區塊鏈,用於添加Block實體
public static ArrayList<Block> blockChain = new ArrayList<>();
public static void main(String[] args) {
//test1 測試三個被加密字符串 加密後的hash值的差別
/**
* 雖然第三條信息僅僅多一個".",但加密後的數據hash相差極大
*/
String message0 = "我是要被加密的信息";
String message1 = "我是要被加密的信息";
String message2 = "我是要被加密的信息.";
String encryptionMessage0 = SHA256Util.applySha256(message0);
String encryptionMessage1 = SHA256Util.applySha256(message1);
String encryptionMessage2 = SHA256Util.applySha256(message2);
System.out.println(encryptionMessage0);
System.out.println(encryptionMessage1);
System.out.println(encryptionMessage2);
//test2 創建區塊鏈邏輯, 因爲第一個塊沒有上一個塊的hash頭部值,所以輸入0 作爲前一個塊的previous hash
/**
* 由於在{@link SHA256Util#calculateHash(Block)}
* 中對同時產生的new Date().getTime() (timestamp)
* 也加入進行了hash加密,所以固有的message (data)及
* previoushash之和進行了加密.
*/
Block genesisBlock = new Block("這是第一個區塊中的要被加密的信息和交易信息","0");
String hash1 = genesisBlock.getHash();
System.out.println("Hash for block 1 : "+hash1);
Block secondBlock = new Block("這是第二個區塊,以及其中信息!!!它的前區塊頭部hash我們拿上一個的來使用",hash1);
String hash2 = secondBlock.getHash(); //
System.out.println("Hash for block 2 : "+hash2);
Block thirdBlock = new Block("這是第三個區塊,它的hash應該已經被前兩個的信息納入進來了,它的hash如果對不上,那麼說明前面的信息被改動過了",hash2);
String hash3 = thirdBlock.getHash();
System.out.println("Hash for block 3 : "+hash3);
//test3 add our blocks to the blockchain ArrayList :
blockChain.add(new Block("區塊鏈上第一小節","0"));
blockChain.add(new Block("區塊鏈第二小節",blockChain.get(blockChain.size()-1).getHash()));
blockChain.add(new Block("區塊鏈第三小節",blockChain.get(blockChain.size()-1).getHash()));
// JSONArray blockChainJson1 = (JSONArray)JSONArray.toJSON(blockChain); //JSONArray是不排版的
// System.out.println(blockChainJson1);
String blockChainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockChain);
System.out.println(blockChainJson);
}
}