深入理解区块链 - 第1章:信任问题的本质

86 阅读46分钟

深入理解区块链 - 第1章:信任问题的本质

学习目标

通过本章学习,你将彻底理解:

  1. 为什么人类社会需要信任机制
  2. 传统信任机制的局限性和问题
  3. 数字世界面临的特殊信任挑战
  4. 密码学如何为区块链提供数学基础
  5. 去中心化如何从根本上解决信任问题

学习原则

  • 从问题出发:先理解问题,再理解解决方案
  • 深入本质:不仅要知其然,更要知其所以然
  • 建立联系:理解各个概念之间的内在联系
  • 思考验证:每学一个概念,都要问"为什么"

第一部分:信任的本质

1.1 什么是信任?

信任是人类社会运行的基础。但信任到底是什么?

定义:信任是相信对方会按照承诺行事,即使你无法监督。

这个定义包含三个关键要素:

  1. 承诺:对方做出了某种承诺
  2. 不确定性:你无法完全监督对方是否履行
  3. 相信:你选择相信对方会履行
生活中的信任例子

例子1:借钱给朋友

  • 你借给朋友1000元,约定下个月还
  • 你相信他会还,但你没有法律保障
  • 如果他跑了,你怎么办?
  • 信任成本:可能损失1000元

例子2:网上购物

  • 你在网上买东西,先付钱给商家
  • 你相信他会发货,但你没有见到商品
  • 如果他收了钱不发货,你怎么办?
  • 信任成本:可能损失货款

例子3:银行存款

  • 你把钱存银行,银行承诺会还给你
  • 你相信银行会还,但钱不在你手里
  • 如果银行倒闭了,你怎么办?
  • 信任成本:可能损失存款
信任的核心矛盾

信任的核心矛盾是:

  • 需要信任:因为无法完全监督
  • 信任有风险:因为对方可能违约
  • 必须信任:因为社会需要合作

这个矛盾,就是区块链要解决的根本问题。


1.2 传统社会如何建立信任?

人类历史上有三种主要的信任机制:

方法1:法律和制度

原理

  • 通过法律强制对方履行承诺
  • 如果违约,可以起诉
  • 有政府强制执行

优点

  • 有强制力
  • 相对公平
  • 适用范围广

缺点

  • 需要第三方(法院、警察)
  • 成本高(律师费、时间成本)
  • 时间长(诉讼可能需要几年)
  • 可能不公平(法律可能偏向某些群体)
  • 依赖政府(如果政府腐败,法律失效)

本质问题

  • 仍然需要信任第三方(政府、法院)
  • 如果第三方作恶,整个系统失效
方法2:声誉机制

原理

  • 如果违约,声誉受损
  • 以后没人相信你
  • 长期损失大于短期收益

优点

  • 成本低(不需要第三方)
  • 速度快(立即生效)
  • 自执行(违约自动惩罚)

缺点

  • 只在小范围内有效(熟人社会)
  • 陌生人之间无效
  • 可以造假(可以伪造声誉)
  • 无法量化(不知道声誉值多少钱)

本质问题

  • 依赖社会网络
  • 如果社会网络断裂,机制失效
方法3:第三方担保

原理

  • 银行担保交易
  • 支付宝担保支付
  • 公证处证明文件

优点

  • 降低信任成本
  • 提高交易效率
  • 有专业保障

缺点

  • 依赖第三方
  • 第三方可能作恶
  • 第三方可能倒闭
  • 需要支付费用

本质问题

  • 把信任问题转移给第三方
  • 但第三方本身也需要被信任

1.3 传统信任机制的根本问题

通过分析三种传统信任机制,我们发现一个共同的问题:

都需要信任第三方

无论是法律、声誉还是担保,最终都依赖某个第三方:

  • 法律依赖政府
  • 声誉依赖社会网络
  • 担保依赖担保机构

如果第三方失效,整个信任机制失效

这就是传统信任机制的根本问题:

  • 单点故障:第三方失效,系统失效
  • 单点控制:第三方可以作恶
  • 信任成本:需要持续信任第三方

第二部分:数字世界的信任危机

2.1 数字世界的特殊性

数字世界和物理世界有一个根本区别:

数字可以被完美复制

在物理世界:

  • 你给朋友一本书,你就没有这本书了
  • 你给朋友100元,你就没有这100元了

在数字世界:

  • 你复制一个文件给朋友,你还有这个文件
  • 你复制一个数字给朋友,你还有这个数字

这个区别带来了三个核心问题。


2.2 问题1:如何证明"我是我"?

在物理世界,你可以:

  • 出示身份证
  • 签名
  • 按指纹

但在数字世界:

  • 身份证可以伪造(PS)
  • 密码可以泄露
  • 生物识别可以复制

传统解决方案

  • 用户名 + 密码
  • 手机验证码
  • 生物识别

问题

  • 密码可以泄露
  • 手机可以丢失
  • 生物识别可以复制
  • 都需要信任服务器(服务器可以作恶)

本质问题

  • 数字身份无法像物理身份那样"唯一"
  • 需要依赖中心化服务器验证

2.3 问题2:如何防止数据被篡改?

在物理世界:

  • 纸质文件修改会留下痕迹
  • 可以对比原件和副本

但在数字世界:

  • 数据可以完美复制
  • 修改不会留下痕迹(除非有日志)
  • 无法区分原件和副本

传统解决方案

  • 存在服务器上
  • 有备份
  • 有日志

问题

  • 服务器可以被攻击
  • 管理员可以修改
  • 备份也可以被修改
  • 日志也可以被删除

本质问题

  • 数字数据无法像物理数据那样"不可篡改"
  • 需要依赖中心化服务器保护

2.4 问题3:如何防止双重支付?

这是数字世界特有的问题,也是区块链要解决的核心问题之一。

2.4.1 什么是双重支付?

定义:双重支付(Double Spending)是指同一笔钱被花费两次或多次。

生活中的类比

想象一下,你有一张100元的现金:

  • 你给朋友A 100元,现金就没了
  • 你不能再给朋友B 100元(因为你没有现金了)
  • 物理世界:现金是唯一的,给出去就没了

但在数字世界:

  • 你有一个数字文件,代表100元
  • 你可以完美复制这个文件
  • 你可以同时给朋友A和朋友B各100元
  • 但实际你只有100元
  • 数字世界:数字文件可以完美复制,如何防止?
2.4.2 为什么数字世界容易出现双重支付?

核心原因:数字文件可以完美复制

物理资产:
- 现金:给出去就没了,无法复制
- 商品:给出去就没了,无法复制
- 物理资产是"唯一"的

数字资产:
- 数字文件:可以完美复制
- 数字数据:可以完美复制
- 数字资产不是"唯一"的

具体例子

例子1:数字文件

你有一个文件:money.txt
内容:"100元"

你可以:
1. 复制这个文件:money_copy.txt
2. 给朋友A发送:money.txt
3. 给朋友B发送:money_copy.txt
4. 两个朋友都收到"100元"
5. 但你实际只有100元

问题:如何防止这种情况?

例子2:数字代币

你有一个数字代币:token_001
代表:100元

你可以:
1. 复制这个代币:token_001_copy
2. 给商家A发送:token_001(购买商品)
3. 给商家B发送:token_001_copy(购买商品)
4. 两个商家都收到"100元"
5. 但你实际只花了100元,却买了200元的商品

问题:如何防止这种情况?

例子3:数字签名

你有一个数字签名:signature_001
代表:授权支付100元

你可以:
1. 复制这个签名:signature_001_copy
2. 给银行A发送:signature_001(支付100元)
3. 给银行B发送:signature_001_copy(支付100元)
4. 两个银行都扣款100元
5. 但你实际只授权支付100元

问题:如何防止这种情况?
2.4.3 双重支付的攻击场景

场景1:简单双重支付

攻击者:Alice
目标:用100元买200元的商品

步骤:
1. Alice有100元(数字文件)
2. Alice复制这个文件:100元_copy
3. Alice给商家A发送:100元(买100元的商品)
4. Alice给商家B发送:100元_copy(买100元的商品)
5. 两个商家都收到"100元"
6. Alice用100元买了200元的商品

结果:攻击成功,Alice获利100元

场景2:网络延迟攻击

攻击者:Alice
目标:利用网络延迟进行双重支付

步骤:
1. Alice有100元
2. Alice同时向两个商家发送支付请求:
   - 向商家A发送:支付100元(网络路径1)
   - 向商家B发送:支付100元(网络路径2)
3. 由于网络延迟,两个商家可能在不同时间收到
4. 如果两个商家都接受,双重支付成功

结果:如果网络同步不及时,攻击可能成功

场景3:分叉攻击

攻击者:Alice
目标:利用区块链分叉进行双重支付

步骤:
1. Alice有1002. Alice在链A上:支付100元给商家A
3. Alice在链B上:支付100元给商家B(分叉)
4. 如果两条链都被接受,双重支付成功

结果:如果网络分叉,攻击可能成功
2.4.4 传统解决方案

方案1:中心化账本(银行)

原理:
- 所有交易记录在银行的中心化账本
- 每次支付,银行检查余额
- 如果余额不足,拒绝支付

流程:
1. Alice要支付100元给商家A
2. 银行检查:Alice余额 = 100元
3. 银行记录:Alice余额 = 0元,商家A余额 += 100元
4. Alice要支付100元给商家B
5. 银行检查:Alice余额 = 0元
6. 银行拒绝:余额不足

优点:
- 简单有效
- 可以防止双重支付

缺点:
- 依赖银行(单点故障)
- 银行可以修改记录(单点控制)
- 银行可以拒绝服务
- 银行可能倒闭

方案2:时间戳服务器

原理:
- 使用时间戳服务器记录交易时间
- 如果发现同一笔钱在相近时间支付两次,拒绝

流程:
1. Alice要支付100元给商家A
2. 时间戳服务器记录:时间T1,Alice支付100元给商家A
3. Alice要支付100元给商家B
4. 时间戳服务器检查:发现时间T1已经支付过
5. 时间戳服务器拒绝:已经支付过

优点:
- 可以检测双重支付

缺点:
- 依赖时间戳服务器(单点故障)
- 时间戳服务器可以修改记录
- 如果时间戳服务器被攻击,系统失效

方案3:数字现金系统(DigiCash等)

原理:
- 使用盲签名等技术
- 银行签名后,用户可以使用
- 但银行不知道用户如何使用

流程:
1. Alice从银行获得签名的数字现金
2. Alice可以使用这个数字现金
3. 银行无法追踪使用情况

优点:
- 保护隐私

缺点:
- 仍然依赖银行
- 无法完全防止双重支付
- 系统复杂

传统方案的根本问题

  1. 依赖中心化机构

    • 银行、时间戳服务器等
    • 单点故障、单点控制
  2. 无法完全防止

    • 如果中心化机构被攻击
    • 如果中心化机构作恶
    • 双重支付仍然可能发生
  3. 信任成本高

    • 需要信任中心化机构
    • 如果机构失效,系统失效
2.4.5 区块链如何解决双重支付?

核心思想:去中心化账本 + 共识机制

方案1:UTXO模型(比特币)

原理:
- 每笔交易使用UTXO(未花费的交易输出)
- 每个UTXO只能使用一次
- 如果UTXO已被使用,再次使用会被拒绝

流程:
1. Alice有UTXO_001(100元)
2. Alice要支付100元给商家A:
   - 使用UTXO_001作为输入
   - 创建新的UTXO_002(100元给商家A)
   - UTXO_001被标记为"已花费"
3. Alice要支付100元给商家B:
   - 尝试使用UTXO_001作为输入
   - 系统检查:UTXO_001已被使用
   - 系统拒绝:UTXO已被花费

优点:
- 完全防止双重支付
- 不依赖中心化机构
- 公开可验证

关键:
- 所有节点都有完整的UTXO记录
- 需要网络共识确认UTXO是否被使用

方案2:账户模型(以太坊)

原理:
- 每个账户有余额
- 每次支付,检查余额
- 如果余额不足,拒绝支付

流程:
1. Alice账户余额 = 100元
2. Alice要支付100元给商家A:
   - 检查余额:100元 >= 100元
   - 更新余额:Alice = 0元,商家A = 100元
3. Alice要支付100元给商家B:
   - 检查余额:0元 < 100元
   - 拒绝支付:余额不足

优点:
- 简单直观
- 完全防止双重支付

关键:
- 所有节点都有完整的账户状态
- 需要网络共识确认状态更新

方案3:共识机制保证

原理:
- 所有节点共同维护账本
- 通过共识机制确认交易
- 如果发现双重支付,拒绝

流程:
1. Alice广播交易A:支付100元给商家A
2. 节点验证:
   - 检查余额/UTXO
   - 如果有效,接受
3. Alice广播交易B:支付100元给商家B(双重支付)
4. 节点验证:
   - 检查余额/UTXO
   - 发现已被使用
   - 拒绝交易B
5. 网络共识:
   - 所有节点都拒绝交易B
   - 只有交易A被确认

优点:
- 完全防止双重支付
- 不依赖中心化机构
- 网络共识保证

关键:
- 需要网络共识
- 如果网络分叉,可能暂时出现双重支付
- 但最终只有一条链被接受
2.4.6 区块链防止双重支付的机制

机制1:交易验证

每个节点在接收交易时验证:

1. 检查输入是否有效:
   - UTXO是否存在?
   - UTXO是否已被使用?
   - 签名是否有效?

2. 检查输出是否合理:
   - 输出金额是否合理?
   - 输入金额 >= 输出金额?

3. 如果发现双重支付:
   - 拒绝交易
   - 不广播给其他节点

机制2:区块确认

交易被打包到区块后:

1. 区块包含所有交易
2. 如果区块中有双重支付:
   - 区块无效
   - 被其他节点拒绝

3. 只有有效的区块被接受
4. 交易被确认

机制3:最长链规则

如果出现分叉(可能包含双重支付):

1. 网络可能暂时分叉
2. 不同节点看到不同的交易
3. 最长链规则:
   - 选择最长的链
   - 放弃较短的链
   - 较短链上的交易被回滚

4. 最终只有一条链被接受
5. 双重支付被防止

机制4:确认数

为了进一步防止双重支付:

1. 等待多个确认
2. 每个确认 = 后面又多了一个区块
3. 确认数越多,被回滚的概率越低

例子:
- 1个确认:可能被回滚(如果分叉)
- 6个确认:几乎不可能被回滚
- 12个确认:几乎绝对安全

比特币建议:6个确认
以太坊建议:12个确认(因为出块更快)
2.4.7 实际例子:比特币如何防止双重支付

场景:Alice尝试双重支付

初始状态:
- Alice有1个比特币(来自交易A的输出)

尝试1:支付给商家A
1. Alice创建交易B:
   - 输入:交易A的输出(1 BTC)
   - 输出1:0.5 BTC给商家A
   - 输出2:0.49 BTC找零给Alice
   - 输出3:0.01 BTC手续费
2. Alice用私钥签名
3. 广播到网络
4. 节点验证:
   - 输入有效(交易A的输出存在)
   - 输入未被使用
   - 签名有效
5. 交易B被接受,打包到区块100

尝试2:支付给商家B(双重支付)
1. Alice创建交易C:
   - 输入:交易A的输出(1 BTC)- 同一个输入!
   - 输出1:0.5 BTC给商家B
   - 输出2:0.49 BTC找零给Alice
   - 输出3:0.01 BTC手续费
2. Alice用私钥签名
3. 广播到网络
4. 节点验证:
   - 输入有效(交易A的输出存在)
   - 输入已被使用(在交易B中)
   - 验证失败!
5. 交易C被拒绝,不被打包

结果:
- 只有交易B被确认
- 双重支付被防止

如果Alice尝试利用网络延迟?

场景:网络延迟攻击

尝试:
1. Alice同时广播交易B和交易C
2. 由于网络延迟,不同节点可能先收到不同的交易
3. 可能出现分叉:
   - 链1:包含交易B
   - 链2:包含交易C

防御:
1. 最长链规则:
   - 如果链1先挖出下一个区块,链1更长
   - 所有节点选择链1
   - 链2被放弃,交易C被回滚

2. 确认数:
   - 等待6个确认
   - 即使出现分叉,最终只有一条链被接受
   - 双重支付被防止

结果:
- 即使利用网络延迟,双重支付也无法成功
2.4.8 为什么区块链可以防止双重支付?

原因1:去中心化账本

所有节点都有完整的账本:

  • 所有交易记录
  • 所有UTXO状态
  • 所有账户余额

如果尝试双重支付:

  • 所有节点都能检测到
  • 所有节点都会拒绝
  • 无法成功

原因2:共识机制

网络共识保证:

  • 所有节点对账本状态达成一致
  • 如果出现分歧,通过共识解决
  • 最终只有一条链被接受

如果尝试双重支付:

  • 网络共识会拒绝
  • 只有一条链被接受
  • 双重支付被防止

原因3:不可篡改

区块链不可篡改:

  • 一旦交易被确认,无法修改
  • 一旦UTXO被使用,无法再次使用
  • 历史记录永久保存

如果尝试双重支付:

  • 第一次支付被确认后,无法撤销
  • 第二次支付会被拒绝
  • 双重支付被防止

原因4:公开可验证

所有交易公开可验证:

  • 任何人都可以查看所有交易
  • 任何人都可以验证交易
  • 任何人都可以检测双重支付

如果尝试双重支付:

  • 任何人都能发现
  • 任何人都能拒绝
  • 双重支付被防止
2.4.9 总结

双重支付是数字世界的核心问题

  • 数字文件可以完美复制
  • 如何防止"同一笔钱花两次"?

传统解决方案的问题

  • 依赖中心化机构
  • 单点故障、单点控制
  • 无法完全防止

区块链的解决方案

  • 去中心化账本
  • 共识机制
  • 不可篡改
  • 公开可验证

区块链如何防止双重支付

  1. UTXO模型:每个UTXO只能使用一次
  2. 账户模型:检查余额,防止超支
  3. 交易验证:每个节点验证交易
  4. 区块确认:只有有效区块被接受
  5. 最长链规则:最终只有一条链被接受
  6. 确认数:等待多个确认,提高安全性

这是区块链的核心价值之一:在去中心化的环境中,完全防止双重支付,而不依赖任何中心化机构。


2.5 中心化系统的三个致命问题

通过分析数字世界的三个核心问题,我们发现它们都依赖中心化系统。

而中心化系统有三个致命问题:

问题1:单点故障

描述

  • 如果中心服务器被攻击,数据丢失
  • 如果中心服务器故障,服务中断
  • 如果中心服务器倒闭,数据无法访问

例子

  • 很多创业公司倒闭,用户数据丢失
  • 很多网站关闭,数据无法访问
  • 很多App下架,数据无法恢复

本质

  • 所有数据依赖单一节点
  • 节点失效 = 系统失效
问题2:单点控制

描述

  • 中心服务器可以修改数据
  • 中心服务器可以拒绝访问
  • 中心服务器可以作恶

例子

  • 公司可以删除用户数据
  • 平台可以封禁用户
  • 银行可以冻结账户

本质

  • 所有控制权集中在单一节点
  • 节点作恶 = 系统作恶
问题3:信任成本

描述

  • 需要信任服务器不会故障
  • 需要信任管理员不会作恶
  • 需要信任公司不会倒闭

例子

  • 你把钱存银行,需要信任银行
  • 你把数据存云盘,需要信任云服务商
  • 你用社交平台,需要信任平台

本质

  • 需要持续信任第三方
  • 信任成本高,风险大

第三部分:去中心化的解决方案

3.1 去中心化的核心思想

定义:去中心化是不依赖单一节点,多个节点共同维护,没有单点故障,没有单点控制。

关键要素

  1. 多个节点:不是单一节点
  2. 共同维护:所有节点都有数据
  3. 无单点故障:节点失效 ≠ 系统失效
  4. 无单点控制:节点作恶 ≠ 系统作恶

3.2 去中心化如何解决单点故障?

传统系统
数据存储:
┌─────────────┐
│ 中心服务器   │  ← 所有数据在这里
└─────────────┘

如果服务器故障:

  • 数据丢失
  • 服务中断
  • 系统失效
去中心化系统
数据存储:
┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐
│节点1│  │节点2│  │节点3│  │节点4│
│数据 │  │数据 │  │数据 │  │数据 │
└─────┘  └─────┘  └─────┘  └─────┘

如果节点1故障:

  • 其他节点还在
  • 数据依然存在
  • 系统依然运行

需要所有节点同时故障 = 几乎不可能 本质

  • 数据分布在多个节点
  • 节点失效 ≠ 数据丢失
  • 系统更可靠

3.3 去中心化如何解决单点控制?

传统系统
控制权:
┌─────────────┐
│ 中心服务器   │  ← 完全控制
└─────────────┘

可以:
- 修改数据
- 拒绝访问
- 作恶
去中心化系统
控制权:
┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐
│节点1│  │节点2│  │节点3│  │节点4│
└─────┘  └─────┘  └─────┘  └─────┘
   ↓       ↓       ↓       ↓
   需要网络共识才能修改

单个节点无法:

  • 修改数据(需要共识)
  • 拒绝访问(其他节点可以访问)
  • 作恶(会被其他节点拒绝)

本质

  • 控制权分散在多个节点
  • 需要共识才能修改
  • 单个节点无法作恶

3.4 去中心化如何降低信任成本?

传统系统
信任对象:
- 服务器不会故障
- 管理员不会作恶
- 公司不会倒闭

信任成本:高
信任风险:大
去中心化系统
信任对象:
- 数学和密码学(客观规律)
- 代码和共识(公开透明)

信任成本:低
信任风险:小

本质

  • 从信任人/机构,转向信任数学/技术
  • 数学规律不会作恶
  • 代码公开,可以验证

3.5 去中心化面临的挑战

去中心化虽然解决了中心化的问题,但也带来了新的挑战:

挑战1:如何达成共识?

问题

  • 多个节点如何达成一致?
  • 如何防止恶意节点?
  • 如何保证数据一致性?

解决方案

  • 共识机制(PoW、PoS等)
  • 通过数学和密码学保证
  • 通过激励机制保证

这是区块链的核心技术之一,我们会在后续章节详细讲解。

挑战2:如何保证安全?

问题

  • 如何防止攻击?
  • 如何防止篡改?
  • 如何防止双重支付?

解决方案

  • 密码学(哈希、签名)
  • 共识机制
  • 激励机制

这是区块链的核心技术之一,我们会在后续章节详细讲解。

挑战3:如何提高性能?

问题

  • 去中心化通常更慢
  • 如何提高速度?
  • 如何降低成本?

解决方案

  • Layer2解决方案
  • 分片技术
  • 优化共识机制

这是区块链的优化方向,我们会在后续章节详细讲解。


第四部分:密码学基础

4.1 为什么需要密码学?

去中心化解决了架构问题,但如何保证安全?

答案:密码学。

密码学提供了数学保证:

  • 哈希函数:保证数据不被篡改
  • 数字签名:保证身份和完整性
  • 非对称加密:保证通信安全

核心思想:用数学规律保证安全,而不是依赖人。


4.2 哈希函数(Hash Function)

4.2.1 什么是哈希函数?

定义:哈希函数是把任意长度的数据,转换成固定长度的数据。

哈希函数就像一个"数字指纹生成器":

  • 输入:任意数据(可以是1个字节,也可以是1TB)
  • 输出:固定长度的"指纹"(通常是256位,即64个十六进制字符)
  • 作用:用这个"指纹"来唯一标识数据

数学表示

H(x) = y

其中:
x:输入(任意长度)
y:输出(固定长度,如256位)
H:哈希函数

生活中的类比

想象一下,你有一本书,你想给这本书一个唯一的"身份证号":

  • 这本书可能有100页,也可能有1000页(任意长度)
  • 但"身份证号"总是18位数字(固定长度)
  • 不同的书有不同的"身份证号"
  • 即使两本书内容几乎一样,只要有一个字不同,"身份证号"就完全不同

哈希函数就是这样工作的。

实际例子

# 使用Python计算SHA-256哈希
import hashlib

# 例子1:短文本
data1 = "Hello World"
hash1 = hashlib.sha256(data1.encode()).hexdigest()
print(f"输入: {data1} (11个字符)")
print(f"输出: {hash1} (64个字符)")
# 输出: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e

# 例子2:长文本
data2 = "The quick brown fox jumps over the lazy dog"
hash2 = hashlib.sha256(data2.encode()).hexdigest()
print(f"\n输入: {data2} (43个字符)")
print(f"输出: {hash2} (64个字符)")
# 输出: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592

# 例子3:大文件(假设)
# 即使是一个1GB的文件,哈希值也还是64个字符

关键观察

  • 输入长度不同,输出长度相同:无论输入是1个字符还是1TB,输出都是64个字符
  • 输入内容不同,输出完全不同:即使只改一个字符,输出也完全不同
  • 输出看起来随机:输出是一串看似随机的字符,但实际上是完全确定的

为什么叫"哈希"?

"哈希"(Hash)这个词来自英文,原意是"切碎、混合"。哈希函数就像把数据"切碎、混合",然后得到一个"指纹":

  • 数据被"切碎"成小块
  • 这些小块被"混合"处理
  • 最终得到一个唯一的"指纹"

这个"指纹"可以用来:

  • 验证数据是否被修改
  • 快速比较两个数据是否相同
  • 作为数据的唯一标识

4.2.2 哈希函数的四个特性

特性1:确定性(Deterministic)

描述:相同输入,一定得到相同输出。

数学表示

如果 x1 = x2,则 H(x1) = H(x2)

例子

H("Hello") = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
H("Hello") = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

无论计算多少次,结果都一样。

为什么重要

  • 可以验证数据完整性
  • 可以重复验证

特性2:快速计算(Fast Computation)

描述:计算哈希值很快,即使是大文件。

例子

计算1MB文件的哈希值:< 1毫秒
计算1GB文件的哈希值:< 10毫秒
计算1TB文件的哈希值:< 1秒

为什么重要

  • 可以实时验证
  • 可以处理大量数据

特性3:不可逆(One-Way)

描述:无法从输出推回输入。

数学表示

给定 y = H(x),无法找到 x

例子

已知哈希值:"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
无法知道原始数据是什么。

可能是:"Hello"
也可能是:"H3ll0"
也可能是其他任何数据

为什么不可逆?

这是哈希函数最神奇的特性,也是区块链安全的基础。让我们深入理解为什么不可逆。

数学原理1:信息丢失(信息论角度)

想象一下:

  • 输入可以是任意长度:1个字节、1KB、1MB、1GB、1TB...
  • 输出固定长度:256位(32字节)

这就像:

  • 你有一杯水(任意大小)
  • 你把它倒进一个固定大小的杯子(256位)
  • 如果水太多,会溢出(信息丢失)
  • 如果水太少,杯子也装不满(信息丢失)

具体例子

输入1"Hello" (5个字节)
输入2"Hello World" (11个字节)
输入3"Hello World, this is a very long text..." (1000个字节)

输出:都是256位(32个字节)

问题:给你一个256位的输出,你能知道原始输入是什么吗?
答案:不能!因为:
- 可能是5个字节的"Hello"
- 可能是11个字节的"Hello World"
- 可能是1000个字节的长文本
- 可能是任何能产生这个哈希值的数据

信息已经丢失,无法恢复。

数学原理2:单向函数(密码学角度)

哈希函数在数学上是一个单向函数(One-Way Function):

正向计算(容易):
输入 → 哈希函数 → 输出
时间:微秒级

逆向计算(困难):
输出 → ??? → 输入
时间:需要尝试2^256次(宇宙毁灭都算不完)

为什么正向容易,逆向困难?

正向计算

  • 有明确的算法步骤
  • 计算机可以快速执行
  • 时间复杂度:O(n),n是输入长度

逆向计算

  • 没有明确的算法
  • 只能暴力尝试所有可能的输入
  • 时间复杂度:O(2^256),需要尝试2^256次

2^256有多大?

2^256 ≈ 10^77

这是什么概念?
- 宇宙中的原子数量:约10^80
- 2^256 ≈ 10^77,接近宇宙原子数量

即使全宇宙的计算机都用来计算,也需要:
- 时间:比宇宙年龄还长
- 能量:比太阳的能量还多

所以,从数学上,哈希函数是不可逆的。

数学原理3:碰撞抵抗(安全性角度)

即使理论上可以找到输入,但:

  • 找到的输入可能不是原始输入(碰撞)
  • 需要找到"正确"的输入,概率极低
  • 即使找到了,也无法证明是"原始"输入

为什么重要

哈希函数的不可逆性,使得:

  1. 可以隐藏原始数据

    • 存储哈希值,不存储原始数据
    • 即使哈希值泄露,原始数据也安全
  2. 可以存储密码

    • 不存储明文密码
    • 只存储密码的哈希值
    • 即使数据库泄露,密码也安全
  3. 可以证明知道数据,但不泄露数据

    • 你可以证明你知道某个数据(通过提供哈希值)
    • 但不需要泄露数据本身
    • 这在零知识证明中有重要应用
  4. 区块链的安全基础

    • 每个区块都有哈希值
    • 如果修改数据,哈希值会变
    • 无法伪造哈希值(因为不可逆)
    • 因此区块链不可篡改

特性4:雪崩效应(Avalanche Effect)

描述:输入微小变化,输出完全不同。

例子

H("Hello") = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
H("Hallo") = "d85b121619a5e7b638a8e9b4e8c8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8"

只改了一个字母(e → a),但哈希值完全不同。

为什么重要

  • 可以检测任何修改
  • 即使只改1位,也能发现

4.2.3 为什么哈希函数可以防篡改?

原理

  1. 计算原始数据的哈希值
  2. 存储哈希值
  3. 需要验证时,重新计算哈希值
  4. 对比两个哈希值
  5. 如果相同,数据未被修改;如果不同,数据被修改

例子

原始数据:"Hello"
原始哈希:"2cf24dba..."

存储:数据 + 哈希值

验证时:
重新计算:"Hello""2cf24dba..."
对比:相同 → 数据未被修改

如果数据被修改:"Hallo"
重新计算:"Hallo""d85b1216..."
对比:不同 → 数据被修改

在区块链中的应用

  • 每个区块都有哈希值
  • 每个区块都包含前一个区块的哈希值
  • 如果修改任何区块,哈希值会变
  • 后面的区块都会不匹配
  • 因此无法篡改

这是区块链"不可篡改"的数学基础。


4.2.4 SHA-256的工作原理(深入理解)

SHA-256是比特币和以太坊使用的哈希函数。让我们深入理解它是如何工作的。

SHA-256是什么?

  • SHA:Secure Hash Algorithm(安全哈希算法)
  • 256:输出256位(32字节,64个十六进制字符)
  • 设计者:美国国家安全局(NSA)
  • 标准:FIPS 180-4(美国联邦信息处理标准)

SHA-256的工作流程(详细)

步骤1:预处理和填充

原始输入:任意长度的数据

填充规则:
1. 在数据末尾添加一个"1"2. 添加k个"0"位,使得:
   数据长度 + 1 + k + 640 (mod 512)
3. 添加64位,表示原始数据的长度(以位为单位)

结果:数据长度是512位的倍数

例子

输入:"Hello" (5字节 = 40位)

填充后:
"Hello" + "1" + "000...000" (k个0) + "000...0101000" (64位长度)
总长度:512位的倍数

步骤2:分块处理

把填充后的数据分成512位的块:
块1: 512位
块2: 512位
块3: 512位
...

每个块独立处理

步骤3:初始化哈希值

初始化8个32位的变量(h0到h7):
h0 = 0x6a09e667
h1 = 0xbb67ae85
h2 = 0x3c6ef372
h3 = 0xa54ff53a
h4 = 0x510e527f
h5 = 0x9b05688c
h6 = 0x1f83d9ab
h7 = 0x5be0cd19

这些是前8个质数的平方根的小数部分

步骤4:处理每个512位块

对每个512位块,进行64轮计算:

每轮计算:
1. 使用块中的16个32位字
2. 扩展成64个32位字(使用消息调度)
3. 使用8个工作变量(a, b, c, d, e, f, g, h)
4. 进行复杂的位运算:
   - 右旋转(ROTR)
   - 右移(SHR)
   - 异或(XOR)
   - 与(AND)
   - 或(OR)
   - 加法(模2^32)
5. 使用64个常量(K0到K63)
6. 更新工作变量

64轮后,更新哈希值:
h0 = h0 + a
h1 = h1 + b
...
h7 = h7 + h

步骤5:输出最终哈希值

把所有块的哈希值连接起来:
h0 || h1 || h2 || h3 || h4 || h5 || h6 || h7

得到256位的最终哈希值

为什么这么复杂?

这种复杂的设计是为了:

  1. 雪崩效应

    • 输入的微小变化,会导致输出的巨大变化
    • 即使只改1位,输出也完全不同
  2. 碰撞抵抗

    • 无法找到两个不同的输入产生相同的输出
    • 即使尝试2^128次,概率也极低
  3. 不可逆性

    • 无法从输出推回输入
    • 只能暴力破解,计算量巨大

实际计算示例

import hashlib

# 计算SHA-256
data = "Hello World"
hash_value = hashlib.sha256(data.encode()).hexdigest()
print(f"输入: {data}")
print(f"输出: {hash_value}")
# 输出: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e

# 只改一个字符
data2 = "Hello Worle"  # d 改成 e
hash_value2 = hashlib.sha256(data2.encode()).hexdigest()
print(f"\n输入: {data2}")
print(f"输出: {hash_value2}")
# 输出: d85b121619a5e7b638a8e9b4e8c8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8

# 观察:只改了一个字符,但输出完全不同

为什么用SHA-256?

  1. 安全性高

    • 目前没有找到碰撞(两个不同输入产生相同输出)
    • 即使量子计算机,也需要2^128次计算
    • 被认为是安全的
  2. 计算快

    • 硬件优化(CPU、GPU、ASIC)
    • 可以并行计算
    • 适合大规模使用
  3. 标准化

    • 广泛使用(比特币、以太坊、TLS等)
    • 经过多年验证
    • 有完整的实现
  4. 输出固定

    • 总是256位
    • 适合存储和传输
    • 适合作为数据标识

在区块链中的应用

  1. 区块哈希

    • 每个区块都有哈希值
    • 用于链接区块
    • 用于验证区块完整性
  2. 交易哈希

    • 每笔交易都有哈希值
    • 用于Merkle树
    • 用于快速验证
  3. 地址生成

    • 从公钥生成地址
    • 使用哈希函数
    • 保证地址的唯一性
  4. 挖矿

    • 计算区块哈希
    • 找到符合条件的哈希值
    • 证明工作量

4.3 数字签名(Digital Signature)

4.3.1 什么是数字签名?

定义:数字签名是用私钥签名,用公钥验证的机制。

作用

  1. 证明身份:只有拥有私钥的人才能签名
  2. 证明完整性:数据没有被修改
  3. 不可否认:签名者无法否认签过名

类比

  • 就像手写签名
  • 只有你能签你的名字
  • 别人可以验证是你的签名
  • 你无法否认签过名

4.3.2 公钥和私钥

密钥对(Key Pair)

  • 私钥(Private Key):保密,只有你知道
  • 公钥(Public Key):公开,任何人都可以知道

关系

  • 用私钥签名 → 任何人都可以用公钥验证
  • 但无法从公钥推导出私钥

数学原理

  • 基于非对称加密
  • 私钥和公钥是数学相关的
  • 但无法从公钥推导私钥(单向函数)

例子

私钥:0x1234567890abcdef...(保密)
公钥:0xabcdef1234567890...(公开)

用私钥签名 → 可以用公钥验证
但无法从公钥推导出私钥

为什么重要

  • 可以公开公钥,不泄露私钥
  • 任何人都可以验证,但只有你能签名

4.3.3 如何签名?(详细步骤)

数字签名的过程比想象中复杂,让我们详细理解每一步。

步骤1:计算数据的哈希值

原始数据:"Hello World"(可能是任意长度)

计算哈希:
hash = SHA256("Hello World")
hash = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"

为什么先计算哈希?
- 数据可能很大(几MB、几GB)
- 签名大数据的计算很慢
- 哈希值固定长度(256位),签名很快
- 哈希值代表数据,签名哈希值 = 签名数据

步骤2:用私钥"加密"哈希值(实际上是签名)

这里需要澄清一个概念:

不是"加密",而是"签名"

虽然技术上是用私钥对哈希值进行数学运算,但这个过程和加密不同:

加密:用公钥加密,用私钥解密(保护数据)
签名:用私钥签名,用公钥验证(证明身份)

签名过程:
signature = sign(hash, private_key)

数学上:
signature = (hash^d) mod n

其中:
- d:私钥
- n:模数(公钥的一部分)
- 这是基于RSA或椭圆曲线的数学运算

详细过程

1. 使用私钥对哈希值进行数学运算
2. 得到签名值(通常也是256位或更长)
3. 签名值看起来像随机数,但实际上包含了:
   - 哈希值的信息
   - 私钥的信息
   - 但无法从签名推导出私钥

步骤3:得到签名

最终签名:
signature = "3a5f8b2c9d1e4f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"

这个签名:
- 是唯一的(只有这个私钥能产生这个签名)
- 是确定的(相同数据和私钥,总是产生相同签名)
- 是不可伪造的(没有私钥无法产生有效签名)

完整流程示例

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend

# 1. 生成密钥对
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
public_key = private_key.public_key()

# 2. 要签名的数据
message = b"Hello World"

# 3. 计算哈希值(内部自动完成)
# 实际过程:
# hash = SHA256(message)
# signature = sign(hash, private_key)

# 4. 签名
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

print(f"数据: {message}")
print(f"签名: {signature.hex()}")

结果

  • 签名:一串数字(签名值)
  • 数据:原始数据
  • 公钥:用于验证的公钥

有了这三个,任何人都可以验证签名。

为什么不是直接签名数据?

  1. 效率

    • 数据可能很大(几GB)
    • 签名大数据的计算很慢
    • 哈希值固定长度(256位),签名很快
  2. 安全性

    • 哈希函数保证数据完整性
    • 签名哈希值 = 签名数据
    • 如果数据被修改,哈希值会变,签名验证会失败
  3. 标准化

    • 所有数据都先哈希,再签名
    • 统一处理流程
    • 便于实现和验证

关键理解

签名不是"加密数据",而是:

  • 证明身份:只有拥有私钥的人才能签名
  • 证明完整性:如果数据被修改,签名验证会失败
  • 不可否认:签名者无法否认签过名(因为只有他有私钥)

4.3.4 如何验证?(详细步骤)

验证签名的过程是签名的逆过程,让我们详细理解。

验证的目标

  1. 确认签名是用对应私钥签的
  2. 确认数据没有被修改
  3. 确认签名者的身份

步骤1:计算数据的哈希值

收到:
- 数据:"Hello World"
- 签名:signature
- 公钥:public_key

计算数据的哈希值:
hash1 = SHA256("Hello World")
hash1 = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"

步骤2:用公钥"解密"签名(实际上是验证)

同样需要澄清:这不是"解密",而是"验证"。

验证过程:
hash2 = verify(signature, public_key)

数学上:
hash2 = (signature^e) mod n

其中:
- e:公钥指数
- n:模数
- 这是签名的逆运算

详细过程

1. 使用公钥对签名进行数学运算
2. 得到哈希值(如果签名有效)
3. 对比这个哈希值和计算出的哈希值

步骤3:对比两个哈希值

如果 hash1 == hash2:
    验证通过
    - 签名是用对应私钥签的
    - 数据没有被修改
    - 签名者身份确认

如果 hash1 != hash2:
    验证失败
    - 签名可能是伪造的
    - 数据可能被修改了
    - 签名者身份无法确认

完整验证示例

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend

# 假设我们已经有了:
# - message: 数据
# - signature: 签名
# - public_key: 公钥

# 验证签名
try:
    public_key.verify(
        signature,
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    print("✓ 验证成功!")
    print("  - 签名有效")
    print("  - 数据未被修改")
    print("  - 签名者身份确认")
except:
    print("✗ 验证失败!")
    print("  - 签名可能无效")
    print("  - 数据可能被修改")
    print("  - 签名者身份无法确认")

验证失败的几种情况

情况1:签名是伪造的

攻击者没有私钥,尝试伪造签名:
- 计算错误的签名值
- 验证时,hash1 != hash2
- 验证失败

情况2:数据被修改了

原始数据:"Hello World"
修改后:"Hello Worle"

签名是用原始数据签的:
- hash1(原始数据)≠ hash2(修改后数据)
- 验证失败

情况3:使用了错误的公钥

签名是用私钥A签的
但用公钥B验证:
- 公钥B无法验证私钥A的签名
- 验证失败

为什么验证是安全的?

  1. 数学保证

    • 基于非对称加密的数学困难性
    • 无法伪造签名(除非拥有私钥)
    • 无法从签名推导出私钥
  2. 哈希保证

    • 如果数据被修改,哈希值会变
    • 签名验证会失败
    • 保证数据完整性
  3. 公开验证

    • 任何人都可以用公钥验证
    • 不需要私钥
    • 不需要信任第三方

在区块链中的应用

  1. 交易签名

    • 每笔交易都需要签名
    • 用你的私钥签名,证明交易是你发的
    • 其他人用你的公钥验证,确认是你发的
  2. 防止伪造

    • 没有私钥无法签名
    • 无法伪造交易
    • 保证交易安全
  3. 不可否认

    • 签名验证通过,证明你签过名
    • 你无法否认
    • 保证交易的可追溯性

4.3.5 为什么可以证明身份?

原理

  1. 只有拥有私钥的人才能签名
  2. 如果签名验证通过,说明签名者拥有私钥
  3. 因此可以证明身份

数学保证

  • 基于非对称加密的数学困难性
  • 无法伪造签名(除非拥有私钥)
  • 无法从公钥推导私钥

在区块链中的应用

  • 每笔交易都需要签名
  • 用你的私钥签名,证明交易是你发的
  • 其他人用你的公钥验证,确认是你发的
  • 因此可以防止伪造交易

这是区块链"证明身份"的数学基础。


4.3.6 为什么私钥不能泄露?

如果私钥泄露

  • 别人可以冒充你
  • 别人可以花你的钱
  • 无法撤销

例子

如果你的比特币私钥泄露:
- 别人可以用你的私钥签名交易
- 把你的比特币转走
- 你无法阻止(因为交易已经上链)
- 你无法撤销(因为区块链不可篡改)

保护方法

  1. 永远不要泄露私钥

    • 不要告诉任何人
    • 不要存在网上
    • 不要截图
  2. 使用硬件钱包

    • 私钥存在硬件设备里
    • 不接触网络
    • 更安全
  3. 使用多重签名

    • 需要多个私钥才能签名
    • 即使一个泄露,也不影响

这是区块链安全的第一原则。


4.4 非对称加密(Asymmetric Encryption)

4.4.1 什么是非对称加密?

定义:非对称加密是用一对密钥(公钥和私钥)进行加密和解密的机制。

特点

  • 公钥加密,私钥解密
  • 私钥加密,公钥解密(用于签名)
  • 无法从公钥推导私钥

对比对称加密

  • 对称加密:用同一个密钥加密和解密
  • 非对称加密:用不同的密钥加密和解密

4.4.2 RSA算法(简化理解)

原理

  • 基于大数分解的困难性
  • 公钥 = (n, e)
  • 私钥 = (n, d)
  • n是两个大质数的乘积
  • 从n分解出两个质数很困难

应用

  • 数字签名
  • 密钥交换

问题

  • 密钥较长(2048位)
  • 计算较慢

4.4.3 椭圆曲线加密(ECC)

为什么用ECC?

  • 更安全:相同安全级别,密钥更短
  • 更快:计算更快
  • 更省空间:密钥更短

比特币和以太坊都用ECC

例子

RSA 2048  ECC 256位(相同安全级别)
但ECC计算更快,密钥更短

第五部分:总结和思考

5.1 核心概念回顾

通过本章学习,你应该理解:

  1. 信任的本质

    • 信任是相信对方会按照承诺行事
    • 传统信任机制都依赖第三方
    • 第三方失效 = 系统失效
  2. 数字世界的信任危机

    • 如何证明"我是我"?
    • 如何防止数据被篡改?
    • 如何防止双重支付?
    • 都依赖中心化系统
  3. 中心化系统的问题

    • 单点故障
    • 单点控制
    • 信任成本高
  4. 去中心化的解决方案

    • 多个节点共同维护
    • 无单点故障
    • 无单点控制
    • 降低信任成本
  5. 密码学基础

    • 哈希函数:保证数据不被篡改
    • 数字签名:保证身份和完整性
    • 非对称加密:保证通信安全

5.2 关键理解

理解1:区块链的本质

  • 区块链是用技术解决信任问题
  • 不依赖第三方,依赖数学和密码学
  • 这是区块链的根本创新

理解2:去中心化的意义

  • 不是技术炫技,而是解决根本问题
  • 解决单点故障、单点控制、信任成本
  • 这是区块链的核心价值

理解3:密码学的作用

  • 不是装饰,而是数学保证
  • 哈希函数保证不可篡改
  • 数字签名保证身份
  • 这是区块链安全的基础

5.3 思考题

  1. 为什么传统系统需要信任第三方?

    • 思考:能否不依赖第三方?
    • 思考:区块链如何解决?
  2. 去中心化如何解决信任问题?

    • 思考:多个节点如何达成一致?
    • 思考:如何防止恶意节点?
  3. 哈希函数如何保证数据不被篡改?

    • 思考:如果修改数据,哈希值会怎样?
    • 思考:区块链如何利用这个特性?
  4. 数字签名如何证明身份?

    • 思考:为什么只有拥有私钥的人才能签名?
    • 思考:如何防止私钥泄露?
  5. 如果私钥丢失了会怎样?

    • 思考:能否恢复?
    • 思考:如何保护私钥?

5.4 下一步

完成本章学习后,你应该:

  1. 理解为什么需要区块链
  2. 理解去中心化的意义
  3. 理解密码学的基础

下一章:我们将深入理解比特币的工作原理,看看区块链是如何实际运行的。


附录:实践练习

练习1:计算哈希值

import hashlib

# 计算SHA-256哈希
data = "Hello World"
hash_value = hashlib.sha256(data.encode()).hexdigest()
print(f"数据: {data}")
print(f"哈希: {hash_value}")

# 修改一个字符
data2 = "Hello Worle"
hash_value2 = hashlib.sha256(data2.encode()).hexdigest()
print(f"\n修改后数据: {data2}")
print(f"哈希: {hash_value2}")

# 观察:即使只改了一个字符,哈希值完全不同

运行结果

数据: Hello World
哈希: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e

修改后数据: Hello Worle
哈希: d85b121619a5e7b638a8e9b4e8c8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8

观察

  • 只改了一个字符(d → e),但哈希值完全不同
  • 这就是雪崩效应

练习2:生成密钥对

# 使用OpenSSL生成RSA密钥对
openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -pubout -out public_key.pem

# 查看私钥
cat private_key.pem

# 查看公钥
cat public_key.pem

观察

  • 私钥和公钥是不同的
  • 私钥更长,包含更多信息
  • 公钥可以从私钥推导,但私钥不能从公钥推导

练习3:数字签名和验证

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend

# 生成密钥对
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
public_key = private_key.public_key()

# 要签名的消息
message = b"Hello World"

# 签名
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

print(f"消息: {message}")
print(f"签名: {signature.hex()}")

# 验证
try:
    public_key.verify(
        signature,
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    print("✓ 验证成功!")
except:
    print("✗ 验证失败!")

# 尝试用错误的消息验证
wrong_message = b"Hello Worle"
try:
    public_key.verify(
        signature,
        wrong_message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    print("✓ 验证成功!")
except:
    print("✗ 验证失败!(这是正常的,因为消息被修改了)")

运行结果

消息: b'Hello World'
签名: 3a5f8b2c...
✓ 验证成功!
✗ 验证失败!(这是正常的,因为消息被修改了)

观察

  • 正确的消息可以验证通过
  • 错误的消息验证失败
  • 这证明了数字签名可以检测数据是否被修改

学习检查清单

完成本章学习后,检查你是否理解:

  • 什么是信任?为什么需要信任?
  • 传统信任机制有哪些?各有什么问题?
  • 数字世界面临哪些信任挑战?
  • 中心化系统有什么问题?
  • 去中心化如何解决这些问题?
  • 哈希函数是什么?有什么特性?
  • 为什么哈希函数可以防篡改?
  • 数字签名是什么?如何工作?
  • 为什么数字签名可以证明身份?
  • 为什么私钥不能泄露?

如果以上问题你都能回答,说明你已经理解了信任问题的本质,可以进入下一章了!