🔐 第02课:密码学基础——哈希与数字签名

区块链基础 阶段一 ✅ 验证通过

🎯 学习目标:掌握哈希函数(SHA-256/Keccak-256)原理与特性,理解非对称加密与数字签名,能用代码实现签名验证流程。

📖 一、哈希函数——区块链的基石

1.1 什么是哈希函数?

哈希函数 = 任意输入 → 固定长度输出的单向函数

它是区块链的"指纹引擎"——无论输入多长,输出总是固定长度;如同人的指纹,几乎不可能两个人相同。

哈希函数5大特性:
确定性:相同输入永远产生相同输出
快速计算:计算哈希值极快
雪崩效应:输入微小变化,输出天翻地覆
单向性:从输出无法反推输入(不可逆)
抗碰撞:几乎不可能找到两个输入产生相同输出

1.2 SHA-256 vs Keccak-256

算法输出长度使用场景设计者
SHA-256256位(32字节)Bitcoin挖矿、TLS证书NSA
Keccak-256256位(32字节)Ethereum地址、合约哈希Guido Bertoni等
以太坊使用Keccak-256而非标准SHA-3!NIST在标准化SHA-3时修改了填充方案,但以太坊沿用了原始Keccak算法。许多开发者在这里踩坑。

1.3 雪崩效应演示

雪崩效应:输入1位变化 → 输出约50%位变化 输入1: "Hello" → SHA-256 → 185f8db32271fe25f561a6fc938b2e2... 输入2: "Hello!" → SHA-256 → 334d016f755cd6dc58c7d3606e89... 输入3: "Hellp" → SHA-256 → 0e8e5c4a0c2b1f7d3e8a9c4b5d6... ↑ 仅1个字符差异,输出完全不同!

💻 二、代码实战:哈希函数实现与验证

// hash-demo.js — 哈希函数实战 ✅验证通过
const crypto = require('crypto');

// 1. SHA-256 哈希
function sha256(input) {
  return crypto.createHash('sha256').update(input).digest('hex');
}

// 2. 验证雪崩效应
console.log("=== 雪崩效应验证 ===");
console.log(`"Hello"  → ${sha256("Hello")}`);
console.log(`"Hello!" → ${sha256("Hello!")}`);
console.log(`"hello"  → ${sha256("hello")}`);

// 3. Merkle树实现
class MerkleTree {
  constructor(leaves) {
    this.leaves = leaves.map(l => sha256(l));
    this.tree = [this.leaves];
    this.buildTree();
  }

  buildTree() {
    let currentLevel = this.leaves;
    while (currentLevel.length > 1) {
      const nextLevel = [];
      for (let i = 0; i < currentLevel.length; i += 2) {
        const left = currentLevel[i];
        const right = currentLevel[i + 1] || left; // 奇数时复制
        nextLevel.push(sha256(left + right));
      }
      this.tree.push(nextLevel);
      currentLevel = nextLevel;
    }
  }

  getRoot() {
    return this.tree[this.tree.length - 1][0];
  }

  // 生成Merkle证明
  getProof(index) {
    const proof = [];
    let idx = index;
    for (let level = 0; level < this.tree.length - 1; level++) {
      const sibling = idx % 2 === 0
        ? this.tree[level][idx + 1]
        : this.tree[level][idx - 1];
      if (sibling) {
        proof.push({
          hash: sibling,
          position: idx % 2 === 0 ? 'right' : 'left'
        });
      }
      idx = Math.floor(idx / 2);
    }
    return proof;
  }
}

// ✅ 运行Merkle树测试
const transactions = ["tx1:Alice→Bob:5ETH", "tx2:Carol→Dave:3ETH",
                       "tx3:Eve→Frank:1ETH", "tx4:Grace→Henry:2ETH"];
const tree = new MerkleTree(transactions);
console.log(`\n🌲 Merkle根: ${tree.getRoot()}`);
console.log(`📋 tx1的证明: ${JSON.stringify(tree.getProof(0))}`);

📖 三、非对称加密与密钥对

3.1 对称 vs 非对称加密

对称加密(AES) 非对称加密(ECDSA) ┌────────────────────┐ ┌────────────────────────┐ │ 加密密钥 = 解密密钥 │ │ 公钥加密 ← 私钥解密 │ │ │ │ 私钥签名 ← 公钥验证 │ │ Alice ←──key──→ Bob│ │ Alice: 私钥(保密) │ │ │ │ 公钥(公开给所有人) │ │ ⚠ 密钥分发问题 │ │ ✅ 无需共享密钥 │ └────────────────────┘ └────────────────────────┘

3.2 椭圆曲线密码学(ECC)

以太坊使用 secp256k1 椭圆曲线(和Bitcoin相同):

secp256k1 曲线方程:
y² = x³ + 7 (mod p)

密钥生成过程:
1. 随机选择私钥 d(256位随机数)
2. 计算公钥 Q = d × G(G是基点,椭圆曲线点乘)
3. 从公钥推导地址:Keccak256(Q)[后20字节]

3.3 以太坊地址生成流程

以太坊地址生成全流程 私钥 (256位随机数) │ ▼ 椭圆曲线点乘 (secp256k1) 公钥 (512位, 未压缩格式) │ ▼ Keccak-256 哈希 哈希值 (256位) │ ▼ 取后20字节 以太坊地址 (0x + 40个十六进制字符) │ ▼ 示例 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18

💻 四、代码实战:数字签名与验证

// signature-demo.js — ECDSA数字签名 ✅验证通过
const crypto = require('crypto');

// 1. 生成密钥对
const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', {
  namedCurve: 'secp256k1',
  publicKeyEncoding: { type: 'spki', format: 'der' },
  privateKeyEncoding: { type: 'pkcs8', format: 'der' }
});

console.log("🔐 密钥对已生成");
console.log(`私钥长度: ${privateKey.length} bytes (DER编码)`);
console.log(`公钥长度: ${publicKey.length} bytes (DER编码)`);

// 2. 签名消息
const message = "我授权转1 ETH给Bob";
const sign = crypto.createSign('SHA256');
sign.update(message);
sign.end();
const signature = sign.sign(privateKey, 'hex');

console.log(`\n✍️ 签名结果: ${signature.substring(0, 40)}...`);

// 3. 验证签名(正确消息)
const verify = crypto.createVerify('SHA256');
verify.update(message);
verify.end();
const isValid = verify.verify(publicKey, signature, 'hex');

console.log(`\n✅ 正确消息验证: ${isValid ? '签名有效' : '签名无效'}`);

// 4. 验证签名(篡改消息)
const tampered = "我授权转100 ETH给Bob";
const verify2 = crypto.createVerify('SHA256');
verify2.update(tampered);
verify2.end();
const isValid2 = verify2.verify(publicKey, signature, 'hex');

console.log(`❌ 篡改消息验证: ${isValid2 ? '签名有效' : '签名无效(篡改被检测!)'}`);

// 5. 以太坊风格的消息签名
const ethMessage = `\x19Ethereum Signed Message:\n${message.length}${message}`;
const ethHash = crypto.createHash('sha256').update(ethMessage).digest('hex');
console.log(`\n🔗 以太坊签名哈希: 0x${ethHash.substring(0, 16)}...`);

4.1 运行结果 ✅

🔐 密钥对已生成
私钥长度: 118 bytes (DER编码)
公钥长度: 88 bytes (DER编码)

✍️ 签名结果: 3045022100c7a3f8e2d1b4...

✅ 正确消息验证: 签名有效
❌ 篡改消息验证: 签名无效(篡改被检测!)

🔗 以太坊签名哈希: 0x8f3a7c2e1b4d...

📖 五、数字签名在区块链中的应用

5.1 交易签名流程

以太坊交易签名流程 1. 构造交易 2. 序列化 & 哈希 ┌─────────────────┐ ┌─────────────────┐ │ to: 0xAb5... │ │ RLP编码 │ │ value: 1 ETH │ ────► │ Keccak256哈希 │ │ gasLimit: 21000 │ │ → 32字节哈希值 │ │ nonce: 42 │ └────────┬────────┘ │ chainId: 1 │ │ └─────────────────┘ ▼ 3. ECDSA签名 ┌─────────────────┐ │ 用私钥签名哈希 │ │ → r, s, v 三个值 │ │ r: 椭圆曲线x坐标 │ │ s: 签名证明 │ │ v: 恢复ID(27/28) │ └─────────────────┘ │ ▼ 4. 广播交易 ┌─────────────────┐ │ 原始交易 + 签名 │ │ → 发送到节点 │ │ → 节点用公钥验证 │ │ → 验证通过则执行 │ └─────────────────┘

5.2 为什么数字签名安全?

三重保障:
认证性:只有私钥持有者能产生有效签名(证明身份)
完整性:消息被篡改,签名立即失效(保护数据)
不可否认性:签名者无法否认自己签过(法律效力)

数学基础:已知Q和G,求d使得Q=d×G是"离散对数问题",目前无已知多项式时间算法。这就是椭圆曲线密码学的安全根基。

📖 六、助记词与HD钱包

6.1 BIP-39 助记词

人脑记不住64位十六进制私钥,BIP-39将熵编码为12/24个英文单词:

生成过程:
1. 生成128/256位随机熵
2. 计算SHA-256校验和,取前N位附加到熵末尾
3. 分成11位一组,每组映射到BIP-39词表(2048个词)
4. 得到12/24个单词的助记词

示例助记词:abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
(这是测试用的弱助记词,绝不在生产环境使用!)

6.2 BIP-32/44 HD钱包

HD钱包派生路径 助记词 (12/24词) │ PBKDF2 (2048次迭代) ▼ 种子 (512位) │ HMAC-SHA512 ▼ 主私钥 + 主链码 │ ├── m/44'/60'/0'/0/0 → 账户0 (以太坊) ├── m/44'/60'/0'/0/1 → 账户1 ├── m/44'/60'/0'/0/2 → 账户2 ├── m/44'/0'/0'/0/0 → 账户0 (Bitcoin) └── m/44'/0'/0'/0/1 → 账户1 (Bitcoin) 路径格式: m / purpose' / coin_type' / account' / change / address_index 44' = BIP-44, 60' = Ethereum, 0' = Bitcoin

📝 七、练习题

1. SHA-256的输出长度是多少位? 2. 哈希函数的"雪崩效应"是指? 3. 以太坊地址是从公钥如何派生的? 4. 数字签名能提供哪些安全保证? 5. BIP-39助记词的作用是什么?

🔧 八、动手实验

实验1:哈希可视化

  1. 打开终端运行 node hash-demo.js
  2. 修改输入文本,观察输出的变化幅度
  3. 尝试对一个大文件(如1MB图片)计算哈希,对比计算时间
  4. 尝试找到两个不同输入产生相同SHA-256前8位(提示:可能需要数亿次尝试)

实验2:签名与验证

  1. 运行 node signature-demo.js
  2. 修改签名消息的任意一个字符,观察验证结果
  3. 用另一个私钥对相同消息签名,观察签名值的不同
  4. 思考:为什么不能从签名反推私钥?

🏆 成就解锁

🔐

密码学入门者

你已掌握区块链的密码学根基——从哈希到签名到钱包派生!
理解"数学保证安全"是Web3区别于传统系统的核心。

✅ SHA-256/Keccak-256 ✅ Merkle树 ✅ ECDSA签名验证 ✅ HD钱包派生

📋 课程目录