区块链基础与密码学
区块链的本质是密码学、分布式系统和博弈论的结合
📖 章节概述
本章介绍区块链的密码学基础,包括哈希函数、非对称加密、数字签名、Merkle树等核心概念,为后续学习打下坚实基础。
学习目标
- 理解哈希函数的特性和应用
- 掌握非对称加密和数字签名的原理
- 理解 Merkle 树的结构和作用
- 了解区块链如何使用密码学保证安全
一、哈希函数(Hash Function)
1.1 哈希函数的定义
哈希函数是一种单向函数,将任意长度的输入映射为固定长度的输出。
H(input) = output (固定长度)
示例(SHA-256):
H("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
H("hello world") = b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
1.2 哈希函数的特性
| 特性 | 说明 | 区块链应用 |
|---|---|---|
| 确定性 | 相同输入总是产生相同输出 | 交易ID、区块ID |
| 快速计算 | 可以快速计算哈希值 | 高效验证 |
| 单向性 | 无法从哈希值反推原始数据 | 隐私保护 |
| 雪崩效应 | 输入微小变化导致输出剧变 | 防篡改 |
| 抗碰撞性 | 很难找到两个不同输入产生相同输出 | 唯一性保证 |
雪崩效应示例
H("hello") = 2cf24dba5fb0a30e...
H("hello!") = e6b7c45e2e7c6c77... (完全不同)
H("Hello") = 185f8db32271fe25... (完全不同)
1.3 常见哈希算法
| 算法 | 输出长度 | 安全性 | 区块链应用 |
|---|---|---|---|
| MD5 | 128位 | 已破解 | 不推荐 |
| SHA-1 | 160位 | ⚠️ 弱 | Git(非安全场景) |
| SHA-256 | 256位 | 强 | 比特币、以太坊 |
| SHA-3 | 可变 | 强 | 新一代标准 |
| Keccak-256 | 256位 | 强 | 以太坊地址 |
1.4 Go 语言实现
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
func main() {
data := "hello"
// SHA-256
hash := sha256.Sum256([]byte(data))
hashHex := hex.EncodeToString(hash[:])
fmt.Printf("SHA-256(%s) = %s\n", data, hashHex)
// 验证雪崩效应
data2 := "hello!"
hash2 := sha256.Sum256([]byte(data2))
hashHex2 := hex.EncodeToString(hash2[:])
fmt.Printf("SHA-256(%s) = %s\n", data2, hashHex2)
}
1.5 区块链中的哈希应用
应用1:交易ID(TXID)
交易内容 → SHA-256 → 交易ID
以太坊交易ID示例:
0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060
应用2:区块哈希
区块头(包含前一个区块哈希) → SHA-256 → 当前区块哈希
比特币区块链示例:
区块 #100: 000000007bc154e0fa7ea32218a72fe2c1bb9f86cf8c9ebf9a715ed27fdb229a
区块 #101: 00000000c4cbd75af741f3a2b2ff72d9ed4d83a048462c1efe331be31ccf006b
(包含#100的哈希)
🔑 二、非对称加密(Asymmetric Encryption)
2.1 非对称加密的原理
非对称加密使用一对密钥:公钥(Public Key)和私钥(Private Key)。
┌─────────────────────────────────────────┐
│ 非对称加密 │
├─────────────────────────────────────────┤
│ 公钥加密 → 私钥解密 (加密通信) │
│ 私钥签名 → 公钥验证 (数字签名) │
└─────────────────────────────────────────┘
核心特性:
- 公钥可以公开,私钥必须保密
- 从公钥无法反推出私钥
- 公钥加密的数据只能用私钥解密
- 私钥签名的数据可以用公钥验证
2.2 常见非对称加密算法
| 算法 | 密钥长度 | 安全性 | 区块链应用 |
|---|---|---|---|
| RSA | 2048/4096位 | 强 | 很少用(效率低) |
| ECC(椭圆曲线) | 256位 | 强 | 比特币、以太坊 |
| secp256k1 | 256位 | 强 | 比特币 |
为什么区块链用ECC而不是RSA?
- 密钥更短:256位ECC ≈ 3072位RSA
- 签名更快:ECC签名速度快10倍
- 体积更小:区块链需要存储大量签名
2.3 椭圆曲线加密(ECC)原理
椭圆曲线方程:
y² = x³ + ax + b
secp256k1曲线(比特币、以太坊):
y² = x³ + 7
密钥生成:
私钥(k):随机生成的256位数字
公钥(K):K = k × G (G是曲线上的基点)
重要性质:
- 已知 k 和 G,很容易计算 K
- 已知 K 和 G,几乎不可能反推 k (离散对数难题)
2.4 Go 语言实现
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
)
func main() {
// 生成私钥
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
// 导出公钥
publicKey := privateKey.PublicKey
fmt.Printf("私钥: %x\n", privateKey.D.Bytes())
fmt.Printf("公钥X: %x\n", publicKey.X.Bytes())
fmt.Printf("公钥Y: %x\n", publicKey.Y.Bytes())
}
✍️ 三、数字签名(Digital Signature)
3.1 数字签名的作用
数字签名用于证明身份和数据完整性:
| 作用 | 说明 |
|---|---|
| 身份认证 | 证明消息确实是私钥持有者发送的 |
| 不可抵赖 | 签名者无法否认签过名 |
| 防篡改 | 数据被修改后签名失效 |
3.2 签名和验证流程
┌──────────────────────────────────────────────┐
│ 签名流程 │
├──────────────────────────────────────────────┤
│ 1. 对消息计算哈希: h = SHA-256(message) │
│ 2. 用私钥签名哈希: signature = sign(h, 私钥) │
│ 3. 发送 (message, signature, 公钥) │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ 验证流程 │
├──────────────────────────────────────────────┤
│ 1. 收到 (message, signature, 公钥) │
│ 2. 计算哈希: h = SHA-256(message) │
│ 3. 用公钥验证: verify(h, signature, 公钥) │
│ 4. 如果验证通过,说明消息未被篡改且来自私钥持有者│
└──────────────────────────────────────────────┘
3.3 ECDSA 签名算法
ECDSA(Elliptic Curve Digital Signature Algorithm)是区块链最常用的签名算法。
签名过程:
1. 选择随机数 k
2. 计算点 R = k × G,取 R 的 x 坐标为 r
3. 计算 s = k⁻¹ (hash + r × 私钥) mod n
4. 签名结果:(r, s)
验证过程:
1. 计算 u1 = hash × s⁻¹ mod n
2. 计算 u2 = r × s⁻¹ mod n
3. 计算 R' = u1 × G + u2 × 公钥
4. 如果 R' 的 x 坐标 = r,则签名有效
3.4 Go 语言实现
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"math/big"
)
func main() {
// 1. 生成密钥对
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
publicKey := &privateKey.PublicKey
// 2. 待签名的消息
message := []byte("Transfer 10 BTC to Alice")
hash := sha256.Sum256(message)
// 3. 签名
r, s, err := ecdsa.Sign(rand.Reader, privateKey, hash[:])
if err != nil {
panic(err)
}
fmt.Printf("签名 (r, s):\n")
fmt.Printf("r = %x\n", r)
fmt.Printf("s = %x\n", s)
// 4. 验证签名
valid := ecdsa.Verify(publicKey, hash[:], r, s)
fmt.Printf("签名验证: %v\n", valid)
// 5. 篡改消息后验证
tamperedMessage := []byte("Transfer 100 BTC to Alice")
tamperedHash := sha256.Sum256(tamperedMessage)
validAfterTamper := ecdsa.Verify(publicKey, tamperedHash[:], r, s)
fmt.Printf("篡改后验证: %v\n", validAfterTamper)
}
3.5 区块链中的签名应用
比特币交易签名
交易输入:
{
"txid": "前一笔交易ID",
"vout": 0,
"scriptSig": "签名 + 公钥" ← 证明你是UTXO的所有者
}
以太坊交易签名
// 以太坊交易
{
nonce: 0,
to: "0x1234...",
value: 1000000000000000000, // 1 ETH
gasLimit: 21000,
gasPrice: 20000000000,
data: "0x",
v: 27, ← 签名参数
r: "0x...", ← 签名参数
s: "0x..." ← 签名参数
}
签名流程:
1. 构造交易对象
2. 用私钥签名交易哈希
3. 广播交易到网络
4. 矿工用公钥(地址)验证签名
🌳 四、Merkle 树(Merkle Tree)
4.1 Merkle 树的结构
Merkle树是一种哈希二叉树,叶子节点存储数据哈希,非叶子节点存储子节点哈希的哈希。
Root Hash
/ \
H(AB) H(CD)
/ \ / \
H(A) H(B) H(C) H(D)
| | | |
Data Data Data Data
A B C D
计算过程:
H(A) = SHA-256(Data A)
H(B) = SHA-256(Data B)
H(AB) = SHA-256(H(A) + H(B))
Root Hash = SHA-256(H(AB) + H(CD))
4.2 Merkle 树的作用
| 作用 | 说明 | 区块链应用 |
|---|---|---|
| 高效验证 | 只需 O(log n) 次哈希即可验证数据 | SPV轻节点 |
| 防篡改 | 任何数据修改都会改变根哈希 | 区块完整性 |
| 压缩存储 | 只需存储根哈希即可验证整棵树 | 区块头 |
4.3 Merkle Proof(Merkle 证明)
场景:轻节点验证某笔交易是否在区块中,但不下载整个区块。
示例:验证 Data B 是否在区块中
已知:
- Root Hash(存储在区块头中)
- Data B(待验证的交易)
- Merkle Proof:[H(A), H(CD)]
验证步骤:
1. 计算 H(B) = SHA-256(Data B)
2. 计算 H(AB) = SHA-256(H(A) + H(B)) ← 使用Proof中的H(A)
3. 计算 Root' = SHA-256(H(AB) + H(CD)) ← 使用Proof中的H(CD)
4. 如果 Root' == Root Hash,则 Data B 确实在区块中
优势:
- 只需传输
log₂(n)个哈希值 - 例如:1000笔交易,只需传输 10 个哈希(320字节),而不是整个区块(几百KB)
4.4 Go 语言实现
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
type MerkleTree struct {
RootNode *MerkleNode
}
type MerkleNode struct {
Left *MerkleNode
Right *MerkleNode
Data []byte
}
func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
node := &MerkleNode{}
if left == nil && right == nil {
// 叶子节点:直接哈希数据
hash := sha256.Sum256(data)
node.Data = hash[:]
} else {
// 非叶子节点:哈希子节点的拼接
prevHashes := append(left.Data, right.Data...)
hash := sha256.Sum256(prevHashes)
node.Data = hash[:]
}
node.Left = left
node.Right = right
return node
}
func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []*MerkleNode
// 创建叶子节点
for _, datum := range data {
node := NewMerkleNode(nil, nil, datum)
nodes = append(nodes, node)
}
// 如果节点数量为奇数,复制最后一个节点
if len(nodes)%2 != 0 {
nodes = append(nodes, nodes[len(nodes)-1])
}
// 构建树
for len(nodes) > 1 {
var level []*MerkleNode
for i := 0; i < len(nodes); i += 2 {
node := NewMerkleNode(nodes[i], nodes[i+1], nil)
level = append(level, node)
}
nodes = level
if len(nodes)%2 != 0 && len(nodes) > 1 {
nodes = append(nodes, nodes[len(nodes)-1])
}
}
return &MerkleTree{nodes[0]}
}
func main() {
data := [][]byte{
[]byte("Transaction A"),
[]byte("Transaction B"),
[]byte("Transaction C"),
[]byte("Transaction D"),
}
tree := NewMerkleTree(data)
fmt.Printf("Merkle Root: %s\n", hex.EncodeToString(tree.RootNode.Data))
}
4.5 比特币中的 Merkle 树
比特币区块头(80字节):
{
"version": 1,
"prev_block": "000000000019d668...",
"merkle_root": "4a5e1e4baab89f3a...", ← Merkle根哈希
"timestamp": 1231006505,
"bits": 486604799,
"nonce": 2083236893
}
作用:
- 区块头只存储 Merkle Root(32字节)
- 可以验证任意交易是否在区块中
- SPV 轻钱包只需下载区块头(80字节),不需要下载完整区块(可能几MB)
五、地址生成
5.1 比特币地址生成
1. 生成私钥(256位随机数)
私钥: 18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725
2. 生成公钥(椭圆曲线)
公钥: 04 + x坐标(32字节) + y坐标(32字节)
3. 哈希公钥
SHA-256(公钥) → RIPEMD-160 → 公钥哈希(20字节)
4. 添加版本号和校验码
版本号(1字节) + 公钥哈希(20字节) + 校验码(4字节)
5. Base58编码
比特币地址: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
5.2 以太坊地址生成
1. 生成私钥(256位随机数)
私钥: 0x4c0883a69102937d6231471b5dbb6204fe512961708279f8a37f7d9c9e5f1a5d
2. 生成公钥(椭圆曲线secp256k1)
公钥: 04 + x + y (65字节)
3. Keccak-256哈希公钥
Keccak-256(公钥) → 取后20字节
4. 添加0x前缀
以太坊地址: 0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed
以太坊地址校验和(EIP-55):
对地址哈希,如果哈希值对应位>= 8,则该位字母大写
0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed
(a变A,因为Keccak-256(地址)对应位>= 8)
📖 六、总结
密码学在区块链中的应用
| 密码学技术 | 区块链应用 | 作用 |
|---|---|---|
| 哈希函数 | 交易ID、区块哈希 | 唯一标识、防篡改 |
| 非对称加密 | 公钥/私钥、地址生成 | 身份认证 |
| 数字签名 | 交易签名 | 证明所有权、授权转账 |
| Merkle树 | 交易组织、SPV验证 | 高效验证、压缩存储 |
核心要点
- 哈希函数是基石:所有数据的唯一标识都依赖哈希
- 私钥是一切:私钥丢失 = 资产丢失,私钥泄露 = 资产被盗
- 签名保证安全:没有私钥签名,无法转移资产
- Merkle树提升效率:轻节点可以快速验证交易