雜湊、加密與數位簽章

Yan Ru Su

雜湊和加密有什麼不同?

加密 雜湊
金鑰
可逆性 不可

加密

加密是可逆的,可以透過金鑰加密或解密,有分為非對稱加密與對稱加密
加密,透過一個金鑰將明文變成密文
解密,透過密鑰將密文解密明文

非對稱性加密

公鑰與私鑰

私鑰(Private Key):只有自己知道的鑰匙
公鑰(Public Key):公開給所有人的鑰匙

加解密流程

用公鑰加密,對應的私鑰解密

1
明文 -> 公鑰 -> 密文 -> 私鑰 -> 明文

簽名

驗證訊息,用私鑰簽名,用公鑰驗證

1
2
訊息 -> 私鑰簽名 -> 簽章
簽章 + 訊息 -> 公鑰 -> 驗證是否為你

簽名與加密的差別

加密

內容保密

簽名

身份驗證 防竄改

雜湊

把資料輸入一個函式輸出一串亂數,稱為雜湊值(Hash),函式稱為雜湊函式。不同的輸入產出的亂數都不同。同樣的輸入會得到相同的輸出,而儘管輸入只差一個字輸出就會截然不同,但若不同的輸入產出同樣的輸出則稱為碰撞,但他發生的機率是極低的,另外不論資料的長度輸入同個Hash Function都會得到相同長度的雜湊值。

1
abc -> SHA256 -> ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
1
abd -> SHA256 -> a52d159f262b2c6ddb724a61840befc36eb30c88877a4030b65cbe86298449c9

雜湊不算是加密,沒有金鑰,雜湊是不可逆的,無法透過雜湊值推回輸入資料。

為什麼雜湊是「不可逆」的?

沒有密鑰

單純是一個複雜的數學計算,不涉及密鑰。

訊號丟失

輸出是固定長度的,輸入的值過大,固定長度的輸出無法記錄所有輸入的資訊,有些輸入資訊會被丟棄,就無法透過輸出重建輸入。

keccak256 和 SHA3 是同一個東西嗎?

SHA3

NIST美國國家標準暨技術研究在2015公布的第三代雜湊演算法。

keccak256

早期 NIST 在決定 SHA3 時的候選者,可以說是SHA3的前身。

他們是同一個東西嗎?

他們不是同一個雜湊演算法, keccak256 是NIST在選定第三代雜湊演算法時的勝力候選者,不過在 2015 年發表 SHA3 的時候有更改了計算規則,因此若將相同的輸出放入 SHA-3 與 Keccak256 會得到不同的雜湊值

為什麼以太坊不用標準的 SHA3?

在Ethereum中,Hash Function 是 keccak256,為什麼不是SHA3,因為Ethereum在開發時,SHA3 還沒被發佈,因此採用 keccak256。

如何在程式中計算 keccak256?不同資料類型(字串、數字、地址)怎麼處理?

Solidity 內建函式 keccak256(),但 keccak256 的輸入型態是 bytes,所以在處理不同資料的時候要先透過abi.encode將資料變成bytes。

abi.encode()

將所有資料都轉32bytes,空的會補0。

abi.encodePacked()

保留資料原本的長度,不補零。
用 abi.encode 編碼完之後再去用 keccak256 做 Hash

1
2
3
4
5
6
7
8
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Hash {
function hashexample(address addr) internal pure returns (bytes32) {
return keccak256(abi.encode(addr));
}
}

如何從推導出私鑰 -> 公鑰 -> 地址

私鑰 隨機 256bit 32 bytes 64hex
→ 橢圓曲線乘法
公鑰
→ keccak256 取後 20bytes 加 0x
地址 0x+40Hex

ECDSA 簽名為什麼需要私鑰?公鑰的作用是什麼?

在ETH交易時發送者需要對交易簽名以讓節點確認是交易者本人發出的交易,在這之中就是使用 ECDSA 簽名進行。簽名會產生 r,s,v 並發送給節點,節點會將交易資訊進行 Hash,並透過 Hash 後的交易資訊與 r,s,v 計算出公鑰,並將公鑰Hash 得到後二十位,接著再驗證發送交易的地址是否符合得出的地址。

ECDSA簽名

交易發送者會使用私鑰對 Hash 過的交易訊息簽名。私鑰對 Hash 的訊息簽名過後會產生三個值 r,s,v , r,s 是簽名的結果,而 v 則是可以透過 r 跟 s 計算出公鑰。

為什麼需要私鑰?

產生簽名 r,s,v,計算公鑰

公鑰的作用?

公鑰可以得出地址,在以太坊中,地址的產生是公鑰進行 keccak256 雜湊後取後20位

ecrecover 如何從簽名還原出地址?

ecrecover將(Hash後的訊息, r, s, v)經過計算後得出公鑰,再取後 20 位得到地址

對任意文字計算 keccak256,用私鑰簽名,並且用 ecrecover 還原地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const { ethers } = require("hardhat");

async function main() {
const msg = "Hello world";
const msg_bytes = ethers.toUtf8Bytes(msg);
const wallet = ethers.Wallet.createRandom();
const walletadderss = wallet.address;

const sign = await wallet.signMessage(msg_bytes); // 內建hash加EIP-191前綴辨識簽名
const addr = await ethers.verifyMessage(msg_bytes, sign);

// const hash_msg = ethers.keccak256(msg_bytes);
// const signature = wallet.signingKey.sign(hash_msg); // 直接簽 hash 回傳物件
// const addr = ethers.recoverAddress(hash_msg, signature.serialized); //signature.serialized組合簽名

console.log("recovered:", addr);
console.log("wallet:", walletadderss);
console.log("match:", addr === walletadderss);
}

main().catch(console.error);

參考資料
https://zhung.com.tw/article/%E6%B7%BA%E8%AB%87encryption%E5%92%8Chash/
https://vocus.cc/article/61d6fd44fd8978000157e475
https://ithelp.ithome.com.tw/articles/10193762
https://ithelp.ithome.com.tw/m/articles/10208884
https://ithelp.ithome.com.tw/m/articles/10208884
https://www.binance.com/zh-TC/academy/glossary/keccak
https://zh.wikipedia.org/zh-tw/SHA-3