天天看點

區塊鍊的原理與應用? -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

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之和進行了加密.

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);
    }
}           

繼續閱讀