密码学基础(上):对称加密与非对称加密、哈希函数、数字签名原理

各位同学,咱们今天聊点硬核的。密码学这东西,听起来高大上,其实说白了就是「藏」和「认」两个字。藏,是把数据藏起来不让别人看;认,是确认这东西到底是谁发的、有没有被改过。

我在固件开发这行干了十几年,见过太多因为密码学基础不牢导致的翻车事故。有一次,某款 IoT 设备固件升级包没做签名校验,结果被黑客塞了个恶意固件进去,整批设备变成了「肉鸡」。嗯,从那以后,我带的项目,签名验签这块绝不含糊。

核心观点:没有密码学保护的固件,就像没上锁的保险柜。你只是「假装」安全。

一、对称加密:一把钥匙开一把锁

对称加密,顾名思义,加密和解密用的是同一把密钥。你想想看,就像你家大门钥匙,你自己能开,你老婆也能开——只要钥匙一样。

常见的对称加密算法有:

  • AES(Advanced Encryption Standard)—— 目前最主流,我项目里 90% 的场景都用它
  • DES —— 老古董了,密钥太短,现在基本没人用
  • SM4 —— 国标算法,国内项目必须支持

对称加密最大的优点就是快。我在做嵌入式固件加密时,AES-128 在 Cortex-M4 上跑,加密 1KB 数据只需要几十微秒。但缺点也很明显——密钥怎么安全地传给对方?

我的经验:对称加密适合「自己跟自己玩」的场景。比如固件存在 Flash 里,你用 AES 加密后存进去,启动时用预置的密钥解密。密钥不出设备,问题不大。

但如果你要把加密固件通过网络发给成千上万台设备,每台设备都预置同一个密钥?那只要一台被破解,全军覆没。这就是对称加密的「密钥分发难题」。

二、非对称加密:公钥私钥,各司其职

非对称加密就聪明多了。它有一对密钥:公钥(Public Key)和私钥(Private Key)。公钥可以公开,私钥自己藏好。

怎么用?举个例子:

  • 别人用你的公钥加密数据,只有你用私钥能解密
  • 你用私钥签名数据,别人用你的公钥验证签名

常见的非对称加密算法:

算法 密钥长度 特点
RSA 2048 / 4096 位 最经典,但计算慢,密钥长
ECC(椭圆曲线) 256 位 同等安全强度下密钥更短,适合嵌入式
SM2 256 位 国密标准,国内项目必备

我个人习惯,在资源受限的 MCU 上首选 ECC。256 位的 ECC 密钥,安全强度相当于 3072 位的 RSA,但计算量小得多。我曾经在一个只有 64KB Flash 的芯片上跑 ECC 签名验证,完全跑得动。

注意:非对称加密虽然解决了密钥分发问题,但速度比对称加密慢 1000 倍以上。所以实际项目中,我们通常混合使用:用非对称加密传输对称密钥,再用对称密钥加密大量数据。

三、哈希函数:数据的「指纹」

哈希函数,说白了就是把任意长度的数据,压缩成一个固定长度的「摘要」。这个摘要就像人的指纹——数据不同,指纹就不同;数据一样,指纹一定一样。

哈希函数有几个关键特性:

  • 单向性:从哈希值无法反推出原始数据
  • 抗碰撞性:很难找到两个不同数据有相同的哈希值
  • 雪崩效应:输入改一个 bit,输出完全变样

常用的哈希算法:

  • MD5 —— 128 位,已被破解,别用了
  • SHA-1 —— 160 位,也有碰撞风险,建议淘汰
  • SHA-256 —— 256 位,目前最安全,我项目里都用它
  • SM3 —— 国密哈希,256 位

为什么会这样?因为哈希函数在固件签名中扮演着「压缩」的角色。固件可能几 MB 大,非对称加密一次只能加密几百字节。所以先对固件算哈希,得到一个 32 字节的摘要,再对这个摘要签名——效率就上来了。

避坑指南:我曾经遇到一个项目,工程师用 MD5 做固件完整性校验。结果黑客伪造了一个固件,MD5 值竟然和原版一样。从那以后,我规定所有项目哈希必须用 SHA-256 或 SM3。

四、数字签名:给固件盖上「防伪章」

数字签名,就是把哈希函数和非对称加密结合起来。流程是这样的:

  1. 对固件计算哈希值(比如 SHA-256)
  2. 用私钥对哈希值加密,得到签名
  3. 把签名附加在固件后面一起发布

验签时反过来:

  1. 用公钥解密签名,得到哈希值 A
  2. 对收到的固件计算哈希值 B
  3. 比较 A 和 B,相等则固件未被篡改

你想想看,这个流程保证了什么?

  • 完整性:固件有没有被改过?哈希值一比对就知道
  • 真实性:固件是不是你发的?只有你的私钥能生成有效签名
  • 不可否认性:你签了名,就不能说「这不是我发的」

下面我用一个简单的 Python 示例,展示签名和验签的核心逻辑:

# 伪代码示例:固件签名与验签
from cryptography.hazmat.primitives import hashes, asymmetric
from cryptography.hazmat.primitives.asymmetric import ec

# 生成密钥对(ECC P-256)
private_key = ec.generate_private_key(ec.SECP256R1())
public_key = private_key.public_key()

# 固件数据
firmware = b"Hello, this is my firmware v1.0"

# === 签名过程 ===
# 1. 计算哈希
digest = hashes.Hash(hashes.SHA256())
digest.update(firmware)
hash_value = digest.finalize()

# 2. 用私钥签名
signature = private_key.sign(firmware, ec.ECDSA(hashes.SHA256()))

# === 验签过程 ===
try:
    public_key.verify(signature, firmware, ec.ECDSA(hashes.SHA256()))
    print("验签通过!固件是完整的,且来自可信源。")
except:
    print("验签失败!固件可能被篡改或来源不可信。")

关键点:签名不是加密数据,而是加密数据的哈希值。公钥是公开的,任何人都能验签,但只有私钥持有者能签名。

五、知识体系总览

为了让你更直观地理解这些概念之间的关系,我画了一张图:

密码学基础(上)知识体系 对称加密 • 同一密钥加密/解密 • 速度快,适合大数据 • 密钥分发困难 代表算法: AES / DES / SM4 应用场景: 固件存储加密 通信数据加密 非对称加密 • 公钥/私钥成对出现 • 解决密钥分发问题 • 速度慢,适合小数据 代表算法: RSA / ECC / SM2 应用场景: 密钥交换 数字签名 哈希函数 • 任意长度→固定摘要 • 单向不可逆 • 抗碰撞性 代表算法: SHA-256 / SM3 应用场景: 数据完整性校验 密码存储 数字签名 = 哈希函数 + 非对称加密

从这张图你能看到,三大模块各有分工,但又紧密配合。数字签名正是把哈希函数和非对称加密结合起来,才实现了固件的完整性和真实性验证。

六、实际项目中的选择建议

说了这么多理论,最后给点实在的。你在做固件签名方案时,可以参考这个思路:

  • 哈希选 SHA-256 或 SM3,别碰 MD5 和 SHA-1
  • 签名算法选 ECC P-256 或 SM2,嵌入式资源有限,RSA 太费劲
  • 对称加密选 AES-128-GCM,带认证加密,能同时保证机密性和完整性
  • 密钥存储用硬件安全单元(HSM 或 SE),别把私钥明文放 Flash 里

血的教训:我曾经见过一个产品,私钥硬编码在代码里,而且用的是对称加密来做「签名」。结果黑客反编译固件,直接拿到了密钥,所有设备都能伪造升级包。记住:签名必须用非对称加密的私钥,而且私钥必须物理隔离。

好了,这一章的内容就到这。密码学基础是固件安全的基石,理解透了,后面讲签名验签流程时你才能游刃有余。下一章我们接着聊数字证书和 PKI 体系,到时候你会看到,光有签名还不够,还得解决「公钥是谁的」这个问题。

可可精品资料库