區塊鏈的原理與應用? -1

讀完了 <區塊鏈 領導幹部讀本> , 以及看了一部分<區塊鏈原理,設計與應用>,
通過對區塊鏈的一些瞭解跟隨網絡博客完成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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章