區塊鏈基礎之密碼學及安全技術

架構師老狼發表於2023-06-24

1.2 密碼學及安全技術

i區塊鏈中的密碼學與安全技術

1.2.1 密碼學知識

1.2.1.1 Hash函式
  • Hash(雜湊)
    雜湊函式是一類數學函式,可以在有限合理的時間內,將任意長度的訊息壓縮為
    固定長度的輸出值,並且是不可逆的。其輸出值稱為雜湊值,也稱為雜湊值。
    hash演算法

  • 雜湊演算法的應用:
    訊息認證:確保收到的訊息和傳送的訊息都是未被篡改的。
    數字簽名:對訊息摘要進行數字簽名與對訊息本身進行數字簽名等效。
    口令的安全性:僅將口令的雜湊值進行儲存,進行口令檢驗時僅需對比雜湊值即可,即使攻擊者獲取了口令的雜湊值,也無法計算出口令。
    資料完整性:具有抗資料篡改的能力。

  • Hash函式在區塊鏈中的應用
    在區塊鏈系統中,雜湊演算法得到了廣泛的使用。
    在區塊鏈系統中,區塊之間的連結就是透過區塊的雜湊值串聯起來的。除此以外,還有梅克爾樹的生成計算,交易事務的雜湊值計算等。
    區塊鏈是一個使用雜湊指標構建的連結串列
    區塊鏈

  • Merkle tree
    Merkle(默克爾)樹,又叫雜湊樹,是一種典型的二叉樹結構,由一個根節點、一組中間節點和一組葉節點組成
    應用場景:
    快速比較大量資料
    快速定位修改
    Merkle tree

1.2.1.2橢圓曲線加密演算法

即:Elliptic Curve Cryptography,簡稱ECC,是基於橢圓曲線數學理論實現的一種非對稱加密演算法。 相比RSA,ECC優勢是可以使用更短的金鑰,來實現與RSA相當或更高的安全。據研究,160位ECC加密安全性相當於1024位RSA加密,210位ECC加密安全性相當於2048位RSA加密。

1.2.2 安全技術

1.2.2.1 數字簽名

用於防止訊息篡改和抵賴的場景
數字簽名基於非對稱加密,既可以用於證實內容的完整性,又同時可以確 認來源(或不可抵賴,Non-Repudiation)。
數字簽名的全過程分兩大部分,即簽名與驗證。一側為簽名,一側為驗證 過程。

1.2.2.2 數字證照

數字證照

1.2.2.3 PKI體系

PKI體系

1.2.2.4 同態加密

本質上,同態加密是指這樣一種加密函式,對明文進行環上的加法和乘法運算再加密,與加密後對密文進行相應的運算,結果是等價的。由於這個良好的性質,人們可以委託第三方對資料進行處理而不洩露資訊。具有同態性質的加密函式是指兩個明文a、b滿足Dec(En(a)⊙En(b))=a⊕b的加密函式,其中En是加密運算,Dec是解密運算,⊙、⊕分別對應明文和密文域上的運算。當⊕代表加法時,稱該加密為加同態加密:當⊕代表乘法時,稱該加密為乘同態加密。

全同態加密是指同時滿足加同態和乘同態性質,可以進行任意多次加和乘運算的加密函式。用數學公式來表達,即Dec(f(En(m1),En(m2),…,En(mk)))=f(m1,m2,…,mk),或寫成:f(En(m1),En(m2),…,En(mk))=En(f(m1,m2,…,mk)),如果f是任意函式,稱為全同態加密。

1.2.2.5 布隆過濾器
class BloomHash {

	/**
	 * Hash工具類返回的hashcode的最大長度<br>
	 * maxLength為2的n次方,返回的hashcode為[0,2^n-1]
	 */
	public int maxLength;

	// Hash函式生成雜湊碼的關鍵字
	public int seed;

	public BloomHash(int maxLength, int seed) {
		this.maxLength = maxLength;
		this.seed = seed;
	}

	/**
	 * 返回字串string的hashcode,大小為[0,maxLength-1]
	 * 
	 * @param string
	 * @return
	 */
	public int hashCode(String string) {
		int result = 0;
		// 這個構建hashcode的方式類似於java的string的hashcode方法
		// 只是我這裡是可以設定的seed,它那裡是31
		for (int i = 0; i < string.length(); i++) {
			char a = string.charAt(i);
			int b = seed * a; // 隱式的把字元轉換為整數(ASSIC碼)
			result = result + b;
		}
		/**
		 * public static int indexFor(int m, int n){ return m & (n - 1); } public static
		 * void main(String[] args) { System.out.println("19 與 16 求餘 = "+ indexFor(19,
		 * 16) ); System.out.println("19 與 16 求餘 = "+ 19 % 16 ); }
		 * 此方法中n為2的指數值,則其二進位制形式的表示中只存在一個1,其餘位都為0, 例如: 0000 1000、0100 0000、0010
		 * 0000等等。則n-1的二進位制形式就為1的位數變為0, 其右邊位全變為1,例如16的二進位制  0001 0000 -1 = 0000
		 * 1111測試m為19的二進位制 0001 0011 & 0000 1111 = 0000 0011 = 3,地位保留的結果便是餘數。此位運算也是
		 * HashMap中確定元素鍵(key)值所在雜湊陣列下標位置的核心方法,此位運算(hash & (length - 1)) 的效率極高於hash %
		 * length的求餘, 所以也解釋為什麼HashMap的擴容始終為2的倍數(2的指數值)。
		 */
		// 保證結果在[0,maxLength-1]:equal to 'result % maxLength'
		return result & (maxLength - 1);
	}
}

public class BloomFilter {

	// 構建hash函式的關鍵字,總共7個
	private static final int[] HashSeeds = new int[] { 3, 5, 7, 11, 13, 17, 19 };

	// Hash工具類的陣列
	private static BloomHash[] HashList = new BloomHash[HashSeeds.length];

	// BloomFilter的長度,最好為插入數量的10倍,目前為2的20次方,大約100萬個
	private static final int BloomLength = 1 << 20;

	// 對位的操作類,java自帶的BitSet,共BloomLength個bit
	private BitSet bitSet = new BitSet(BloomLength);

	public BloomFilter() {
		// 初始化Hash工具類的陣列,每個hash工具類的hash函式都不同
		for (int i = 0; i < HashSeeds.length; i++) {
			HashList[i] = new BloomHash(BloomLength, HashSeeds[i]);
		}
	}

	/**
	 * 在布隆過濾器中加入值value,在多個hash函式生成的hashcode對應的位置上,置1
	 * 
	 * @param value字串,如果為數字,可以自己轉化成string
	 */
	public void addValue(String value) {
		for (int i = 0; i < HashSeeds.length; i++) {
			// 根據對應的hash函式得到hashcode
			int hashcode = HashList[i].hashCode(value);
			// 在點陣圖中,將對應的位,設定為1
			bitSet.set(hashcode);
		}
	}

	/**
	 * 在布隆過濾器中,檢驗是否可能有值value
	 * 
	 * @param value
	 * @return 如果返回false,則一定沒有<br>
	 *         如果返回true,就代表有可能有
	 */
	public boolean existsValue(String value) {
		boolean result = true;
		for (int i = 0; i < HashSeeds.length; i++) {
			// 根據對應的hash函式得到hashcode
			int hashcode = HashList[i].hashCode(value);

			/**
			 * 隱式把boolean轉換為整數進行按位與運算 “短路” 主要用於邏輯運算子中,即 “ ! && || "這三種運算子 短路 就是知如果左側的
			 * 表示式能確定運算後的結果,則不再計算右側的表示式。 如(1>2)&&(2<3) 明明左側已經為假 了 我 不用計算右側我一定知道 此表達是為假
			 */
			// 將result與對應位置上的0或1 做與運算
			// 如果全為1,則result最後為1
			// 如果有一個位置上為0,則最後result為0
			result = result & bitSet.get(hashcode);
		}
		return result;
	}
}

相關文章