以太坊密碼學Primer:從零理解區塊鏈的安全基石
本文以口語化、易懂的方式介紹以太坊密碼學的核心概念,包括 Hash、Keccak-256、公私鑰、ECDSA 簽名、Merkle Tree 以及前編譯合約等基礎元件。文章使用大量生活化的比喻和實際程式碼範例,幫助讀者建立對密碼學的直觀理解,並為學習進階密碼學內容奠定基礎。
以太坊密碼學Primer:從零理解區塊鏈的安全基石
概述
嗨,如果你剛開始接觸以太坊開發,一定聽過「ECDSA」、「Keccak-256」、「橢圓曲線」這些名詞,但看了半天文件還是霧煞煞——恭喜你,進對地方了。這篇文章就是要用最白話的方式,把以太坊密碼學的核心概念全部拆解給你。
我當初學這块的時候,也是被一堆數學符號嚇到差點放棄。後來才發現,密碼學最重要的不是會算積分,而是搞懂「為什麼這樣設計」以及「這些機制怎麼保護你的資產」。只要把幾個關鍵概念串起來,整個系統就會突然變得清晰。
以太坊密碼學就像是一把精密的鎖——你知道它能保護你的寶藏,但你想搞懂鎖裡面到底藏了什麼玄機,對吧?那我們開始吧。
什麼是密碼學?區塊鏈為什麼需要它?
說白了,密碼學就是「讓訊息只有特定的人能看懂」的技術。你可以把訊息加密之後,就算被別人攔截也沒用——反正他們看不懂。
區塊鏈場景下的密碼學更嚴苛,因為它要解決三個核心問題:
- 身份驗證:這筆交易真的是「你」發的嗎?而不是別人假冒你?
- 完整性:這筆交易在傳輸過程中有沒有被篡改過?
- 不可否認性:你簽名承認的交易,之後不能說「那不是我做的」
以太坊用密碼學工具優雅地解決了這三個問題,而這一切的核心,就是接下來要介紹的幾個基礎元件。
第一課:Hash(雜湊函數)—— 數據的指紋
Hash 是什麼?
Hash 的中文翻譯是「雜湊」或「哈希」,但我更喜歡叫它「數據指紋」。就像每個人的指紋都是獨一無二的,Hash 也是每段數據的唯一標識。
以太坊使用的 Hash 函數叫 Keccak-256,它是 SHA-3 標準的祖先。給任意長度的輸入,它都會輸出固定 256 位元(32 bytes)的「指紋」。
實際操作看看
別緊張,我們不需要真的去算數學,用瀏覽器 console 就能體驗:
// 在瀏覽器 console 或 Node.js 環境執行
// 使用 Web3.js 或 ethers.js
const { keccak256 } = require('ethers').utils;
// 試試不同輸入
console.log(keccak256("hello"));
// 輸出: 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
console.log(keccak256("hello ")); // 注意後面多了個空格
// 輸出: 0xa08dcfa70ebca8766a7b83c3a6494196c40893c621b0c50a9c39f52cb4f1823d
console.log(keccak256("Hello")); // 大小寫不同
// 輸出: 0xd0927f45a073c12c4cf8b4e470369f5a1a9e0ce1646eb534c6c9c30cba6b4d8a
看,即使只差一個空格,輸出就完全不一樣了!這就是 Hash 的「雪崩效應」——輸入的小改變會導致輸出的大改變。
Hash 的三大特性
1. 確定性:同樣的輸入,永遠得到同樣的輸出
2. 單向性:可以從輸出反推輸入嗎?不行!Hash 是單向函數
3. 碰撞阻力:找不到兩筆不同的輸入會產生相同的輸出
這三個特性讓 Hash 成為區塊鏈的「數據指紋機」。任何區塊的內容改變了,Hash 就會跟著變,別人一眼就能發現數據被篡改過。
以太坊中的 Hash 應用
在以太坊區塊鏈上,Hash 無處不在:
區塊結構:
┌─────────────────────────────────────┐
│ Block Header │
│ ├─ parentHash : 上個區塊的 Hash │
│ ├─ stateRoot : 狀態樹的 Hash │
│ ├─ transactionsRoot : 交易樹 Hash │
│ ├─ receiptsRoot : 收據樹 Hash │
│ └─ ... │
└─────────────────────────────────────┘
每一個區塊都包含上一個區塊的 Hash,形成一條「區塊鏈」。如果要篡改某個歷史區塊的數據,你必須重新計算那個區塊之後所有區塊的 Hash——這幾乎是不可能的任務。
第二課:公私鑰——你的數位身份
從比喻開始
想像你有兩個神奇的盒子:
- 公鑰盒子:你可以把這個盒子分享給全世界,任何人都能往裡面放東西
- 私鑰盒子:只有你自己能打開這個盒子,取出裡面的東西
在以太坊的世界裡,你的公鑰就像是你的「銀行帳戶號碼」——別人可以匯錢給你,但無法從你的帳戶取錢。而私鑰就像是你的「ATM 密碼」加上「身份證」——千萬不能讓別人知道!
橢圓曲線密碼學(ECDSA)
以太坊使用的公私鑰系統基於 secp256k1 橢圓曲線。不用被這個名字嚇到,概念很簡單:
核心思想:利用橢圓曲線上的數學問題(ECDLP),讓你很容易從私鑰計算出公鑰,但幾乎不可能從公鑰反推私鑰。
私鑰 →(單向計算)→ 公鑰
公鑰 ─→(無法反推)──✗ 私鑰
這就像是你可以把雞蛋煮成熟蛋,但你沒辦法把熟蛋還原成生蛋。
生成你的第一個以太坊帳戶
const { Wallet } = require('ethers');
// 隨機生成一個錢包
const wallet = Wallet.createRandom();
console.log('私鑰(千萬別分享!):', wallet.privateKey);
// 輸出: 0x...
// 格式: 64個十六進制字元 = 256 bits
console.log('公鑰:', wallet.publicKey);
// 輸出: 0x...
// 格式: 128個十六進制字元 = 512 bits(壓縮後是256 bits)
console.log('以太坊地址:', wallet.address);
// 輸出: 0x...
// 格式: 40個十六進制字元 = 160 bits
公鑰經過 Keccak-256 Hash,取最後 20 bytes,就是你的以太坊地址。這就是為什麼別人可以把 ETH 發到你的地址,但他們無法從你的地址轉出——因為轉帳需要私鑰簽名。
地址的由來——完整過程
1. 生成隨機私鑰 (256 bits)
↓
2. 用私鑰推導公鑰 (橢圓曲線乘法)
↓
3. Keccak-256(公鑰)
↓
4. 取 Hash 的最後 20 bytes
↓
5. 加上 "0x" 前綴 = 以太坊地址
私鑰: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
公鑰: 0x4d5e5f8a4c2b1c3d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d
地址: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
第三課:數位簽名——證明「這是我做的」
簽名是怎麼回事?
你可能會問:我用私鑰簽名,別人怎麼驗證是我簽的?他們又沒有我的私鑰。
好問題!這就是密碼學巧妙的地方。讓我用人類世界的比喻來解釋:
現實世界:你在一份文件上簽名,銀行會把你留下的「簽名樣本」拿出來比對。如果形狀差不多,就認為是你簽的。
數位世界:完全不一樣!你不是「比對形狀」,而是利用數學證明:
「我有私鑰,所以只有我能產生這個簽名。」
「我用私鑰對訊息的 Hash 做了某種計算,產生了簽名。」
「你用公鑰可以驗證這個計算是正確的。」
ECDSA 簽名過程
以太坊使用的簽名算法是 ECDSA(橢圓曲線數位簽名算法)。整個過程分為兩步:
步驟 1:創建簽名
const { Wallet, hashMessage } = require('ethers');
// 假設這是你的錢包
const wallet = new Wallet('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80');
// 要簽名的訊息
const message = "I approve 1 ETH to 0x123...";
// 簽名
const signature = wallet.signMessage(message);
console.log(signature);
// 輸出: 0x...
// 格式: 65 bytes = r(32) + s(32) + v(1)
步驟 2:驗證簽名
const { verifyMessage } = require('ethers');
// 從簽名反推出簽署者的地址
const recoveredAddress = verifyMessage(message, signature);
console.log(recoveredAddress);
// 輸出: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
// 比對是否一致
console.log(recoveredAddress === wallet.address);
// 輸出: true
簽名格式:r, s, v
一個 ECDSA 簽名由三個部分組成:
- r:隨機產生的值,用於確定簽名的「起點」
- s:結合私鑰和訊息 Hash 計算得出的值,控制簽名的「形狀」
- v:用於確定公鑰是哪個(因為橢圓曲線上每個 x 座標可能對應兩個 y 座標)
┌─────────────────────────────────────────────┐
│ ECDSA 簽名結構 │
├─────────────────────────────────────────────┤
│ r (32 bytes): 0x79025d5d86b0f2a8e6f0e... │
│ s (32 bytes): 0x5a8c9d8e7f6b5a4c3d2e1f... │
│ v (1 byte) : 0x1c │
└─────────────────────────────────────────────┘
重要提醒:千萬不要用同一個私鑰對相同的訊息簽名兩次!攻擊者可以從兩個簽名中數學推導出你的私鑰。這也是為什麼錢包每次交易都會使用 nonce(一個遞增的計數器)來確保訊息不會重複。
第四課:Merkle Tree——高效驗證大量數據
為什麼需要 Merkle Tree?
區塊鏈每秒鐘處理成千上萬筆交易,如果要驗證某一筆交易確實存在於某個區塊中,最笨的方法是下載整個區塊的所有交易數據,然後從頭到尾搜一遍。
Merkle Tree 解決了這個問題:它讓你只需要下載少量的「證明」,就能高效驗證任意交易是否存在。
Merkle Tree 的結構
想像你有 8 筆交易 Tx1 到 Tx8:
Root (根Hash)
/ \
Hash(1-4) Hash(5-8)
/ \ / \
Hash(1-2) Hash(3-4) Hash(5-6) Hash(7-8)
/ \ / \ / \ / \
Tx1 Tx2 Tx3 Tx4 Tx5 Tx6 Tx7 Tx8
每個區塊的 Header 都包含 transactionsRoot,這就是 Merkle Tree 的根節點。
驗證某筆交易——Merkle Proof
假設你想驗證 Tx3 確實在這個區塊中。你不需要下載所有交易,只需要提供:
┌─────────────────────────────────────────────┐
│ Tx3 的 Merkle Proof │
├─────────────────────────────────────────────┤
│ 1. Tx3 的 Hash │
│ 2. Hash(Tx4) → 計算 Hash(3-4) │
│ 3. Hash(Tx1-2) → 計算 Hash(1-4) │
│ 4. Hash(Tx5-8) → 計算 Root │
│ 5. 比對計算出的 Root 與區塊 Header 中的值 │
└─────────────────────────────────────────────┘
如果比對成功,就證明 Tx3 確實在這個區塊中!驗證成本從 O(n) 降到 O(log n)。
以太坊中的 Merkle 變種
以太坊其實用了兩種 Merkle 結構:
- Merkle Patricia Trie (MPT):用於存儲帳戶狀態(餘額、合約代碼等)
- Merkle Sum Trie:用於存儲交易和收據
MPT 比普通 Merkle Tree 更複雜,它能高效處理「前綴壓縮」和「更新」操作——因為區塊鏈的狀態是時刻變化的。
第五課:前編譯合約——以太坊內建的密碼學加速器
什麼是 Precompile?
以太坊虛擬機(EVM)原生並不擅長複雜的密碼學運算。如果你用 Solidity 純軟體實現橢圓曲線乘法,一筆交易可能要燃燒掉好幾個 ETH 的 Gas。
為了解決這個問題,以太坊在協議層面內建了 8 個「前編譯合約」——它們本質上是已編譯好的機器碼,執行效率比 EVM 解釋執行高出幾個數量級。
以太坊的前編譯合約
| 地址 | 名稱 | 功能 |
|---|---|---|
| 0x01 | ecrecover | 從簽名恢復簽署者地址 |
| 0x02 | sha256 | 計算 SHA-256 Hash |
| 0x03 | ripemd160 | 計算 RIPEMD-160 Hash |
| 0x04 | identity | 複製記憶體(資料驗證用) |
| 0x05 | modExp | 模指數運算 |
| 0x06 | bn128Add | BN128 曲線點加法 |
| 0x07 | bn128Mul | BN128 曲線標量乘法 |
| 0x08 | bn128Pairing | BN128 配對運算 |
使用前編譯合約——實例
ecrecover:這個超級重要!它讓你從簽名反推出地址,是智慧合約「驗證簽名」的基礎:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SignatureVerifier {
/**
* @notice 驗證簽名(使用 ecrecover 前編譯合約)
* @param message 原始訊息
* @param signature 簽名 (r, s, v)
* @return 簽署者地址
*/
function recoverSigner(
bytes32 message,
bytes32 r,
bytes32 s,
uint8 v
) public pure returns (address) {
// ecrecover 返回與給定簽名對應的地址
// 如果簽名無效,返回 0 地址
return ecrecover(message, v, r, s);
}
/**
* @notice 驗證訊息是否由特定地址簽署
*/
function verify(
address signer,
bytes32 messageHash,
bytes32 r,
bytes32 s,
uint8 v
) public pure returns (bool) {
return recoverSigner(messageHash, r, s, v) == signer;
}
}
Gas 消耗方面,ecrecover 只要 3000 Gas,而你自己用 Solidity 實現同樣功能可能需要數十萬 Gas!
實際應用場景
場景 1:錢包轉帳
當你用手機錢包轉帳 1 ETH 給朋友時,背後發生了什麼?
1. 錢包應用:
- 你的私鑰存在手機的安全晶片中
- 構造交易:from=你的地址, to=朋友地址, value=1 ETH
- 計算交易的 Hash
2. 簽名過程:
- 用私鑰對交易 Hash 進行 ECDSA 簽名
- 產生的 (r, s, v) 附加到交易上
3. 廣播網路:
- 節點收到交易,用公鑰驗證簽名
- 如果驗證通過,交易進入內存池(mempool)
4. 區塊打包:
- 礦工/驗證者將交易打包進區塊
- 執行交易:扣減你的餘額,增加朋友的餘額
整個過程中,你的私鑰從頭到尾都沒有離開你的手機——這就是「非託管」的核心含義。
場景 2:智慧合約授權
當你在 Uniswap 上用 ETH 換 USDT 時:
1. 授權步驟:
- 你簽署一筆「授權交易」,允許 Uniswap 合約
最多動用你的 1000 ETH
- 這個授權記錄在區塊鏈上,任何人都能查看
2. 兌換步驟:
- 你調用 Uniswap 的 swap 函數
- Uniswap 合約檢查你的授權額度
- 如果額度足夠,合約自動轉走你的 ETH 並轉入 USDT
你不需要每次交易都簽名——授權一次,之後合約就能幫你執行。這大大提升了使用者體驗,但同時也帶來了安全風險:萬一合約有漏洞,你的資金可能被盜走。
常見誤解澄清
誤解 1:「區塊鏈是匿名的」
不完全對。區塊鏈是「假名」的——你的地址是一串隨機字元,看不出是誰。但如果有人能把你的地址和真實身份連結起來(例如你在某網站提供了地址),區塊鏈上的所有交易記錄都會暴露。
誤解 2:「私鑰丢了,區塊鏈公司可以幫你恢復」
錯!去中心化的意義就在於:沒有人能幫你恢復私鑰。如果你丢了私鑰,對應的資產就永遠無法訪問了。這就是為什麼「助記詞備份」如此重要。
誤解 3:「量子電腦出來,區塊鏈就完了」
這過度恐慌了。確實,Shor's algorithm 理論上可以在多項式時間內破解 ECDSA——但這需要數百萬個邏輯量子位元的通用量子電腦,而目前最先進的系統只有幾百個。而且,以太坊社群正在積極研究後量子密碼學遷移方案(如 CRYSTALS-Dilithium)。
繼續學習的路徑
恭喜你完成了密碼學入門!接下來你可以探索:
- 初學者:學習如何安全地保管私鑰/助記詞,了解不同錢包的優缺點
- 中級:深入學習 EVM 運作原理,理解 Gas 和交易執行機制
- 進階:研究零知識證明(ZK-SNARK/STARK),探索以太坊的隱私解決方案
總結
今天我們學了:
- Hash:數據的唯一指紋,用於完整性驗證
- 公私鑰:你的數位身份,基於橢圓曲線數學
- 數位簽名:用私鑰「簽字」,用公鑰驗證
- Merkle Tree:高效驗證大量數據的樹狀結構
- 前編譯合約:以太坊內建的密碼學加速器
這些概念看起來抽象,但它們正是保護你資產安全的基石。搞懂了這些,你再看以太坊的各種操作——轉帳、簽名、部署合約——都會有「原來是這麼回事」的頓悟感。
下一篇文章,我們會深入探討密碼學在以太坊共識機制中的應用——特別是 POS 時代的 BLS 簽名和最終性保證。敬請期待!
延伸閱讀與參考
| 主題 | 資源 | 說明 |
|---|---|---|
| 密碼學基礎 | "Understanding Ethereum" by Andreas Antonopoulos | 經典教材,密碼學章節寫得很生動 |
| ECDSA 原理 | CoinDesk 文章「Elliptic Curve Cryptography Explained」 | 圖文並茂的科普文章 |
| Merkle Tree | 以太坊官方文檔 - Trees | 官方對 Merkle Patricia Trie 的說明 |
| Precompile | ethereum/execution-specs GitHub | 各前編譯合約的詳細規格 |
本指南旨在教育目的,不構成任何投資建議。密碼學是複雜的領域,建議在實際應用前充分測試。
相關文章
- 以太坊密碼學基礎完整指南:橢圓曲線密碼學、簽章機制與 Merkle Tree 結構 — 本文深入分析以太坊密碼學系統的三大支柱:secp256k1 橢圓曲線與 ECDSA 簽章機制的數學原理、KECCAK-256 雜湊函數的設計特點、以及 Patricia Merkle Trie 資料結構在狀態管理中的關鍵角色。我們從密碼學理論出發,經過詳盡的數學推導,最終落實到 Solidity、Go 與 Rust 的實際程式碼範例。涵蓋離散對數問題、點加法/倍增運算、ECDSA 簽章驗證、Merkle Proof、EIP-1559 等核心概念的完整技術解析。
- 以太坊密碼學互動實驗室:瀏覽器可執行範例與 secp256k1/ECDSA 深度教學 — 本文提供一套完整的以太坊密碼學互動式學習模組,讓讀者能夠在瀏覽器中直接執行和實驗密碼學運算。涵蓋 secp256k1 橢圓曲線運算、ECDSA 簽章與驗證、Keccak-256 雜湊、以及零知識證明等核心主題。所有範例都可以在現代瀏覽器中直接運行,無需額外的開發環境配置。提供完整的 JavaScript 程式碼範例,包括點加法、標量乘法、交易簽章模擬、Merkle 樹構建等互動實驗。
- 以太坊密碼學原語互動式教學:從橢圓曲線到 Merkle Patricia Tree — 本文以互動式教學方式解析以太坊核心密碼學原語,包括 Keccak 雜湊函數的海綿結構與實作、secp256k1 橢圓曲線密碼學的數學原理、ECDSA 簽章機制與 recovery id 解析、Merkle Patricia Tree 的混合設計與狀態驗證。透過大量圖解說明和 Python 程式碼範例,讓讀者從直觀理解密碼學而非陷入數學公式的泥沼。
- 以太坊密碼學原語直覺式圖解說明:橢圓曲線與布隆過濾器完整指南 — 本文以直覺式的圖解說明,降低密碼學原語的理解門檻。我們深入探討兩種在以太坊中扮演關鍵角色的密碼學技術:橢圓曲線密碼學(ECC)和布隆過濾器(Bloom Filter)。前者是以太坊帳戶地址和簽名算法的基礎,後者則用於區塊頭快速驗證和交易池管理等場景。通過豐富的圖示比喻和逐步推導,將抽象的數學概念轉化為工程師和普通用戶都能理解的知識。
- 以太坊密碼學原語互動式瀏覽器教學:從理論到實踐的完整指南 — 本文以互動式瀏覽器範例為核心,幫助讀者在實踐中理解以太坊的密碼學基礎。涵蓋橢圓曲線密碼學(secp256k1、ECDSA 簽名驗證)、Keccak-256 哈希函數、雪崩效應、Merkle 樹建造與驗證、以及以太坊狀態證明的實際應用。每個概念都配有可運行的 JavaScript 程式碼範例,讀者可以直接在瀏覽器控制台中實驗。特別適合視覺型學習者和希望深入理解密碼學的開發者。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案完整列表
- Solidity 文檔 智慧合約程式語言官方規格
- EVM 代碼庫 EVM 實作的核心參考
- Alethio EVM 分析 EVM 行為的正規驗證
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!