第1章 密码学基础:对称加密、非对称加密、哈希函数、数字签名概述
说实话,不少做SDK的同学一上来就踩坑——把加密算法随便封装一下就往外抛。结果呢?密钥没管理好,算法选错,签名验证漏掉边界……我在架构评审里看到过太多次这种问题了。所以这一章,咱们先把基础打牢。
你想想看,密码学这个东西,说白了就是“通信双方在不安全的信道上安全地传东西”。那怎么保证安全?主要就靠四样东西:对称加密、非对称加密、哈希函数、数字签名。下面我一个一个说。
1.1 对称加密
对称加密,就是加密和解密用同一个密钥。好比一把钥匙开一把锁。
我的习惯:对称加密适合加密大量数据,速度极快。但密钥分发是个老大难问题。
常见的对称加密算法:
| 算法 | 密钥长度 | 分组模式 | 安全性 |
|---|---|---|---|
| AES | 128/192/256 bit | ECB, CBC, GCM 等 | 高(推荐) |
| DES | 56 bit | ECB, CBC | 已废弃(可暴力破解) |
| 3DES | 112/168 bit | ECB, CBC | 中等(性能差,不推荐新项目用) |
| SM4 | 128 bit | ECB, CBC | 高(国标,国内项目常见) |
我刚开始做加密SDK时,犯过一个低级错误——直接用AES-ECB模式。结果密文的相同明文块输出总是相同,安全性大打折扣。所以,我个人建议:如果没特殊需求,优先选AES-GCM。它自带认证,能防篡改,也防重放。
避坑:千万不要在ECB模式下处理图片或结构化数据!相同的前缀会导致可预测的密文块,攻击者很容易从中推断出信息。
1.2 非对称加密
非对称加密,简单来说就是有一对密钥:公钥公开出去,私钥自己藏着。公钥加密的数据只能用私钥解开;反过来,私钥签名的数据可以用公钥验签。
为什么需要它?对称加密的密钥分发是死穴——你怎么把密钥传给对方?非对称加密就解决了这个问题:你只需要拿到对方的公钥,就能给他发密文,他用自己的私钥解密。
常见的非对称算法:
- RSA:经典,支持加密和签名。密钥长度通常2048/4096 bit。不过加密速度慢,不适合大块数据。
- ECC(椭圆曲线):同等安全强度下,密钥比RSA短很多。比如256位ECC ≈ 3072位RSA。性能更高,移动端更友好。
- SM2:国标椭圆曲线,在国内项目里很常见。
- DH / ECDH:密钥交换协议,不能直接加密,但能安全地协商对称密钥。
实际项目经验:我在做IoT设备SDK时,设备算力弱,RSA握手太慢了。于是改用ECC + ECDH先交换临时密钥,再用AES-GCM传业务数据。这样既安全,速度也ok。
但是注意,非对称加密不是万能的。它有个大毛病:慢。所以实践中,我们通常混合使用:非对称加密只用来协商对称密钥(一次握手),之后大量数据用对称加密。
1.3 哈希函数
哈希函数是一种单向函数。你给它任意长度的输入,它固定输出一个摘要(比如256位)。而且理论上你无法从摘要反推出原始数据。这就叫抗原像性。
哈希函数的作用太多了:
- 密码存储:存hash不存明文。
- 数据完整性校验:下载文件后比对hash。
- 数字签名:先对待签名数据做hash,再对hash签名。因为签名私钥操作太耗时,hash能大大缩短签名数据量。
- 消息认证码(HMAC):基于hash构造,用于验证消息真实性和完整性。
常见哈希算法:
| 算法 | 输出长度 | 现状 |
|---|---|---|
| MD5 | 128 bit | 已破解,不推荐 |
| SHA-1 | 160 bit | 已有碰撞攻击,应停用 |
| SHA-256 | 256 bit | 目前安全,推荐 |
| SHA-3 | 可变 | 安全性高,逐步推广 |
| SM3 | 256 bit | 国标,国内推荐 |
我曾经遇到过一个经典bug:某项目用MD5做文件完整性校验,结果被攻击者伪造了相同MD5的文件塞进去了。事后查出来,MD5早被公开碰撞了。所以记住:密码学里不要用任何已被证明有安全缺陷的算法,哪怕它快。
1.4 数字签名
数字签名解决的是身份认证 + 不可否认性。你用自己的私钥对消息(或消息的hash)签名,其他人用你的公钥验证签名。只要公钥可信,就能确认这条消息确实是你发的,而且中途没被篡改。
数字签名的流程:
- 发送方对消息做哈希(比如SHA-256)。
- 发送方用自己私钥对哈希值签名(比如RSA或ECDSA)。
- 接收方收到消息和签名后,对消息做相同的哈希。
- 接收方用发送方公钥验证签名是否匹配。
这里有个细节要注意:签名算法不是直接对原始消息签名,而是先hash再签名。为什么?因为非对称加密签名操作很慢,而hash可以大大缩短输入长度。同时,hash还能提供额外的“完整性”保护。
我个人的做法:在SDK抽象层里,我会把“签名”和“验签”拆成两个独立接口。因为实践中签名时你有私钥,验签时有公钥,两者的密钥类型不同,分开接口更清晰。
1.5 四者之间的关系
讲了这么多,你可能有点晕:它们之间到底怎么配合?我画了一张图帮你梳理整个密码学核心知识体系。你看完就明白了。
1.6 实战中的选择建议
说了这么多理论,你肯定想知道:做SDK抽象层时到底怎么选?我列个快速决策表格,你对照着来。
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 大量数据加密传输 | AES-GCM | 高速,自带认证 |
| 密钥协商 | ECDH + AES | 前向安全,性能好 |
| 身份认证 / 不可否认 | ECDSA (P-256) 或 RSA-PSS | 证书链成熟 |
| 密码存储 | bcrypt / scrypt / Argon2 | 抗暴力破解,不要直接用SHA-256 |
| 消息完整性校验 | HMAC-SHA256 | 带密钥的hash,防篡改 |
重点提醒:在SDK抽象层设计里,不要假设调用方会正确使用密码学原语。你应该提供更高层的接口,比如encrypt(data, keyId),底层自动选算法、做填充、加随机数。用户只需要关心业务,不需要懂GCM还是CBC。