区块链密码学基础:哈希、椭圆曲线与数字签名

区块链底层密码学全景:SHA-256 哈希函数的性质与攻击场景、secp256k1 椭圆曲线数学、ECDSA 签名生成与验证流程、Merkle Tree 结构、SPV 轻客户端验证。从数学原理解释为什么区块链数据不可伪造。

导语:密码学是区块链的信任地基

区块链的"不可篡改"“不可伪造"“所有权证明"三大承诺,全部建立在密码学之上。没有密码学,区块链只是一台昂贵的广播服务器。

本篇文章从数学和工程两个层面,拆解区块链依赖的四大密码学构件:

  1. 哈希函数(SHA-256、Keccak-256):数据的指纹,保证完整性
  2. 椭圆曲线密码(secp256k1):公钥推导与密钥对的数学基础
  3. ECDSA 数字签名:所有权证明,保证交易的不可否认性
  4. Merkle Tree + SPV:把整块数据压缩成 32 字节根哈希,让轻节点能验证

一句话总结:哈希函数负责"压缩与锁定”,椭圆曲线负责"生成密钥对”,ECDSA 负责"签名证明所有权",Merkle Tree 负责"把海量交易压成一个根值"——四者组合,区块链才有了可信的历史。


1. 哈希函数:区块链的"指纹机"

1.1 哈希函数的性质

密码学哈希函数 H(m) 接受任意长度的输入,输出固定长度的摘要(如 SHA-256 输出 256 位)。它必须满足三个核心性质:

性质定义不满足的后果
抗原像性已知 H(m),难以反推出 m攻击者可反推原始数据
抗第二原像已知 m1,难以找到 m2 ≠ m1 使 H(m2) = H(m1)攻击者可伪造数据
抗碰撞性难以找到任意两个 m1 ≠ m2 使哈希相同攻击者可制造"合法"的伪造数据

第三个性质在区块链中尤其关键。SHA-256 抗碰撞强度约为 128 位(生日攻击下的有效强度),意味着约需 2^128 次计算才能以可忽略概率找到碰撞——这在可预见范围内是不可行的。

1.2 SHA-256 与 Keccak-256 的区别

比特币使用 SHA-256,以太坊使用 Keccak-256(注意:不是 SHA3-256 标准,而是原始 Keccak 参数)。二者输出都是 256 位,但内部填充规则不同,同一输入产生不同输出:

import hashlib

def sha256(data: bytes) -> bytes:
    return hashlib.sha256(data).digest()

# Bitcoin 对区块头使用 double-SHA256(哈希两次,防长度扩展攻击)
def double_sha256(data: bytes) -> bytes:
    return sha256(sha256(data))

# 以太坊使用 Keccak-256(Web3.py / eth_utils 提供)
from eth_hash.auto import keccak  # 或 eth_utils.keccak

print(sha256(b"birdor").hex())
print(keccak(b"birdor").hex())

1.3 哈希在区块链中的四个用途

用途 1:区块头哈希(Chaining)
   prev_hash 作为下一个区块头的一部分 → 形成哈希链

用途 2:Merkle Root
   所有交易逐层哈希压缩 → 32 字节根值

用途 3:地址生成
   Keccak-256(公钥) 取后 20 字节 → 以太坊地址

用途 4:PoW 难度目标
   矿工寻找 nonce 使 block_hash < target

一句话总结:哈希函数是区块链的"基础油"——它把任意长度的交易数据压缩成定长指纹,任何一位数据的改变都会让指纹面目全非,这正是不可篡改性的数学来源。


2. 椭圆曲线密码:secp256k1

2.1 什么是椭圆曲线

比特币与以太坊的密钥对基于 secp256k1 椭圆曲线,其方程为:

y² = x³ + 7   (定义在有限域 GF(p) 上,p = 2^256 - 2^32 - 977)

参数选择 a = 0, b = 7 使曲线方程简洁。这个曲线的阶(curve order)为:

n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

2.2 椭圆曲线点运算

椭圆曲线上定义两种基本运算:

P + Q(点加法):过 P、Q 的直线与曲线交于第三点 R',关于 x 轴对称得到 R = P + Q

2P = P + P(点倍乘):过 P 的切线与曲线交于 R',对称得到 2P

标量乘法:kP = P + P + ... + P(k 次)

椭圆曲线离散对数问题(ECDLP):已知基点 G 和公钥点 Q = kG,求私钥 k 在计算上不可行。这就是安全性根基。

# 使用纯 Python 演示 secp256k1 点运算(教学简化版)
# 实际实现必须使用经过审计的库,如 ecdsa、coincurve、libsecp256k1

P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8

def point_add(p1, p2):
    if p1 is None:
        return p2
    if p2 is None:
        return p1
    x1, y1 = p1
    x2, y2 = p2
    if x1 == x2 and (y1 + y2) % P == 0:
        return None  # 无穷远点
    if p1 == p2:
        lam = (3 * x1 * x1) * pow(2 * y1, -1, P) % P
    else:
        lam = (y2 - y1) * pow(x2 - x1, -1, P) % P
    x3 = (lam * lam - x1 - x2) % P
    y3 = (lam * (x1 - x3) - y1) % P
    return (x3, y3)

def scalar_mult(k, point):
    result = None
    addend = point
    while k:
        if k & 1:
            result = point_add(result, addend)
        addend = point_add(addend, addend)
        k >>= 1
    return result

G = (Gx, Gy)
# 随机私钥 k,公钥 Q = kG
# Q = scalar_mult(k, G)

2.3 从私钥到地址

私钥 d(256 位随机数,1 ≤ d < n)
   │  dG(椭圆曲线标量乘法)
   ▼
公钥 Q(压缩:02/03 前缀 + x 坐标;未压缩:04 + x + y)
   │  Keccak-256(Q) 取后 20 字节(以太坊)
   ▼
地址(十六进制,0x 前缀 + EIP-55 校验和)

比特币路径:SHA-256 + RIPEMD-160 → 160 位 Hash160 → Base58Check(含版本前缀与校验)
属性比特币以太坊
曲线secp256k1secp256k1
地址算法Hash160 + Base58CheckKeccak-256 后 20 字节 + hex
地址前缀1 / bc1(SegWit)0x
校验机制4 字节校验和EIP-55 大小写校验

一句话总结:椭圆曲线给了我们一个"单向门"——从私钥(随机数)出发可以轻松算出公钥,但反过来从公钥推私钥要面对 2^128 量级的计算量,这就是区块链账户安全的数学根基。


3. ECDSA 数字签名:所有权证明

3.1 签名与验证的数学流程

ECDSA 签名为 (r, s) 对。签名者持有私钥 d,选择随机 nonce k:

签名:
  z = 交易数据的哈希(截断为曲线阶 n 的位长)
  (x1, y1) = kG
  r = x1 mod n
  s = k⁻¹ (z + r·d) mod n
  输出签名 (r, s)

验证:
  w = s⁻¹ mod n
  u1 = z·w mod n,  u2 = r·w mod n
  (x1, y1) = u1·G + u2·Q
  校验:x1 mod n == r

关键点:随机 nonce k 必须真正随机且绝不重用。如果两个签名共用同一个 k,攻击者可以直接解出私钥:

已知两次签名 (r, s1) 与 (r, s2) 共用 k:
  s1 - s2 ≡ k⁻¹(z1 - z2) (mod n)   →  可解出 k → 私钥 d = (s·k - z)·r⁻¹

2013 年 PlayStation 3 的 ECDSA 签名漏洞正是因此被攻破。

3.2 Python 签名与验证

from ecdsa import SECP256k1, SigningKey, VerifyingKey

# 生成密钥对
sk = SigningKey.generate(curve=SECP256k1)
vk = sk.verifying_key

# 私钥与公钥(字节)
priv_bytes = sk.to_string()
pub_bytes = vk.to_string()  # 未压缩 65 字节;.to_string(compressed=True) 为 33 字节

# 签名(对交易哈希签)
message = b"transfer 0.5 BTC to Bob"
signature = sk.sign(message)   # 默认使用哈希算法,输出 DER 编码 (r, s)

# 验证
assert vk.verify(signature, message)

3.3 可延展性(Malleability)与签名规范

s 与 n - s 是同一个签名的两种表示(验证公式中都成立)。比特币的 BIP-62 / 低位 s 值 规范强制 s < n/2,避免第三方无密钥改写签名导致交易哈希变化。这是 BIP-146 最终落地的规则:

低 s 规范:若 s > n/2,则令 s' = n - s
好处:交易 ID 不被第三方篡改,为二层协议(如闪电网络)提供确定性

以太坊同样要求低位 s 值(v 值域约束),并在 EIP-2 中强制 0 ≤ s ≤ secp256k1n/2。

3.4 哈希类型(Hash Type)与签名的边界

哈希类型覆盖内容用途
SIGHASH_ALL全部输入输出,签名后不可修改标准转账
SIGHASH_NONE仅输入,输出可被他人更改见证人式签名
SIGHASH_SINGLE本输入与同索引输出部分签名
`SIGHASH_ALLSIGHASH_ANYONECANPAY`仅本输入被锁定

一句话总结:ECDSA 让私钥持有者可以对任意交易数据出具"数学签名",验证方只需公钥就能确权——nonce 复用、s 值延展性、哈希覆盖范围,是实际工程中三个最容易被忽视的坑。


4. Merkle Tree:交易的压缩指纹

4.1 构造与节点

Merkle Tree 是满二叉树(叶子不足时复制最后一个叶子——比特币对奇数节点做双哈希):

                 Root
               H(AB + CD)
            ┌────┴────┐
           H_AB       H_CD
         ┌──┴──┐    ┌──┴──┐
        H_A   H_B  H_C   H_D
        │     │    │     │
        TxA   TxB  TxC   TxD

每个内部节点 H(X + Y)。最终 100 万笔交易的区块,根哈希只有 32 字节。

4.2 存在性证明(Inclusion Proof)

证明某笔交易在树中,只需提供从叶子到根的兄弟路径(sibling path):

import hashlib

def leaf_hash(tx: bytes) -> bytes:
    return hashlib.sha256(hashlib.sha256(tx).digest()).digest()  # double-SHA256

def verify_inclusion(tx_hash: bytes, merkle_root: bytes, siblings: list) -> bool:
    """
    siblings: [(哈希, 方向)],方向 True 表示兄弟在左,False 表示兄弟在右
    """
    current = tx_hash
    for sibling_hash, is_left in siblings:
        if is_left:
            current = leaf_hash(sibling_hash + current)
        else:
            current = leaf_hash(current + sibling_hash)
    return current == merkle_root

证明大小 = log2(N) 个哈希。N = 1,000,000 时只需约 20 个 32 字节哈希(640 字节)——这是轻客户端能工作的关键。

4.3 验证流程:Merkle Path 图示

要验证 TxB 在树中:

Root  ← 比对最终结果
┌────┴────┐
H_AB      H_CD   ← 需要 H_AB 的兄弟 H_CD
          ↑ 提供 H_AB = H(H_A + H_B),其中 H_A 是 TxB 的兄弟

验证方只需要:TxB 的哈希 + H_A + H_CD。三个值即可从叶子推到根,比对根哈希即完成验证——O(log N) 存储、O(log N) 计算。

一句话总结:Merkle Tree 把"验证交易是否存在"的存储成本从"下载全部交易"降为"下载 log 个兄弟哈希",这是 SPV 轻钱包能够跑在手机上的前提。


5. SPV 轻客户端验证

5.1 完整节点 vs 轻节点

维度全节点(Full Node)轻节点(SPV)
存储全部区块(比特币 ~600GB+)仅区块头(~100MB,2024 年量级)
验证交易执行全部脚本/状态Merkle 存在性证明
验证规则完整共识规则最长链 + 难度检查(简化)
隐私自己扫描全部交易需向对方暴露请求的过滤条件(Bloom 过滤)
典型场景矿池、交易所、基础设施钱包、浏览器插件

5.2 SPV 的工作流程

1. 轻节点下载所有区块头(80 字节/个),构建区块头链
2. 需要验证某笔交易时:
   a. 向对等节点发起 Bloom filter / compact filter (BIP-157)
   b. 对方返回包含该交易的区块 + 对应 Merkle 证明路径
   c. 轻节点:用自己的区块头链,验证该区块的 prev_hash 链条有效
   d. 轻节点:用 Merkle 证明验证交易哈希存在于该区块的 Merkle Root 中
   e. 轻节点:等待 N 个确认(6 个确认 ≈ 99.99% 不可回滚)
// BIP-157 Compact Block Filters(getcfilters)伪代码流程
// 轻节点请求 "基本过滤器"(SipHash 编码的交易输出脚本集合)

async function spvVerify(txId, cfHeaders, blockHeaders) {
  // 1. 校验过滤器头部链与区块头链的锚定关系
  assert(anchorBlockHeaders(cfHeaders, blockHeaders));
  // 2. 获取包含交易的区块及其 Merkle 证明
  const { block, proof } = await peer.fetchFilteredBlock(txId);
  // 3. 用区块头校验工作量证明与 prev_hash
  assert(validPow(block.header));
  // 4. 用 Merkle 路径校验交易存在
  assert(verifyInclusion(txId, block.header.merkleRoot, proof));
  // 5. 确认数达标(概率终局)
  return currentHeight - block.header.height >= 6;
}

5.3 SPV 的信任边界

SPV 不是无条件安全的。攻击者可以伪造区块头链(如低难度假链)欺骗轻节点,对策是**检查链上累计难度(chainwork)**而非仅看长度。BIP-157 过滤器相比旧式 Bloom filter 更好的隐私性,也降低了"过滤器误报污染"问题。

一句话总结:SPV 用"只信区块头 + Merkle 证明"换来了千分之几的存储量,代价是放弃了对共识规则全量的独立验证——这是移动钱包在实用性与安全性之间的经典折中。


6. 密码学在区块链中的安全边界

6.1 量子威胁

Shor 算法可在多项式时间内破解椭圆曲线离散对数,但需要数千逻辑量子比特。当前量子计算进展下,secp256k1 仍是安全的,但生态已在为量子安全地址做准备:

方案思路代表
延迟签名只在签名时揭示公钥匿名/隐私协议
一次性地址每笔交易新地址隐私币(Monero 风格)
格密码签名抗量子 LWE/Dilithium未来的以太坊 EIP
Lamport 签名哈希基一次性签名比特币 BIP-xxx 讨论

6.2 密码学被破坏的现实案例

事故原因教训
PlayStation 3(2010)ECDSA nonce 复用随机数必须真随机且唯一
安卓 SecureRandom 缺陷(2013)熵不足导致私钥可预测钱包必须用 CSPRNG
Heartbleed(2014)OpenSSL 内存泄漏基础设施安全影响一切上层
已知曲线弱点曲线迁移不要自定义曲线参数

一句话总结:区块链的安全性建立在密码学算法参数被正确实现之上——“数学安全"不等于"工程安全”,随机数生成器、侧信道、协议级错误(nonce 复用)才是真正的高发风险。


7. 总结

区块链密码学四件套构成完整的安全闭环:

  1. 哈希函数:不可逆、抗碰撞,锁住历史数据(SHA-256 / Keccak-256)
  2. 椭圆曲线 secp256k1:单向标量乘法,提供密钥对的数学基础
  3. ECDSA 签名:不可伪造的所有权证明,配合 nonce 唯一性约束
  4. Merkle Tree + SPV:把整区块压缩成 32 字节根,让轻客户端低成本验证

这四层之上,才谈得上共识机制与智能合约。相关深入阅读:

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「blockchain」更多文章

  1. 跨链互操作:跨链桥、轻客户端验证与信任假设
  2. 共识算法深入:PoW/PoS、Casper FFG、Tendermint 与分叉规则
  3. Layer2 扩容:Rollup 架构、数据可用性与跨 L2 桥