密码学基础(上):对称加密与非对称加密的区别,哈希函数(SHA256)的特性与应用
各位做嵌入式开发的同行,咱们今天聊点实在的。签名验签这事儿,说白了就是靠密码学撑起来的。你如果不懂加密和哈希,那签名验签就是个黑盒子。我当年刚入行时也踩过这个坑——照着文档把代码写出来了,但出了问题根本不知道从哪查起。
所以这一章,咱们把基础打牢。我会结合自己这些年做嵌入式安全项目的经验,把对称加密、非对称加密、哈希函数这三个核心概念掰开揉碎了讲清楚。
一、对称加密:一把钥匙开一把锁
对称加密,顾名思义,加密和解密用的是同一把密钥。就像你家里的门锁,用同一把钥匙锁门和开门。
我最早接触对称加密是在做一款智能门锁产品时。当时需要在门锁和手机App之间传输开锁指令,用的就是AES-128加密。嗯,这里要注意,AES是目前嵌入式领域最常用的对称加密算法。
对称加密的核心特点:
- 加密密钥 = 解密密钥
- 运算速度快,适合大数据量加密
- 密钥分发是个大问题——你得想办法把密钥安全地交给对方
常见的对称加密算法有这些:
| 算法 | 密钥长度 | 嵌入式常用场景 |
|---|---|---|
| AES | 128 / 192 / 256 bit | 固件加密、通信加密 |
| DES / 3DES | 56 / 112 / 168 bit | 老旧设备兼容(不建议新设计使用) |
| SM4 | 128 bit | 国密标准,国内项目常用 |
我曾经踩过的坑:有一次做OTA升级固件加密,我用了ECB模式。结果固件头部有固定格式,加密后还能看出明显的模式重复。后来改成CBC模式,加个随机IV,问题才解决。记住:ECB模式在嵌入式里基本用不得,除非你明确知道自己在做什么。
二、非对称加密:公钥私钥,各司其职
非对称加密就很有意思了。它有两把钥匙:一把公钥,一把私钥。公钥可以公开,私钥自己藏好。用公钥加密的数据,只有对应的私钥能解开;反过来,用私钥签名的数据,公钥可以验证。
你想想看,这解决了什么问题?对,密钥分发问题!我不用费劲把密钥传给你,直接把公钥扔网上就行。这也是签名验签能成立的根本原因。
嵌入式里最常用的非对称算法是RSA和ECC(椭圆曲线)。我个人习惯用ECC,因为同样的安全强度下,ECC的密钥更短,运算更快,对MCU更友好。
| 算法 | 安全强度对比 | 密钥长度 | 签名速度 |
|---|---|---|---|
| RSA-2048 | 高 | 2048 bit | 较慢(尤其私钥操作) |
| ECC-256 | 高(等效RSA-3072) | 256 bit | 快 |
| SM2 | 高 | 256 bit | 快(国密标准) |
我的建议:如果你的MCU有硬件加密引擎,优先用ECC。如果MCU比较老、没有硬件加速,RSA-2048也能用,但签名操作可能会卡几百毫秒。我在一个STM32F103的项目上试过,RSA-2048私钥签名大概要300ms,而ECC-256只要50ms左右。
三、对称 vs 非对称:到底怎么选?
其实没有谁替代谁的说法。实际项目中,两者是配合使用的。我画了一张图,你看完就明白了:
说白了,实际项目中我们用的是「混合加密」:用非对称加密来传递对称密钥,然后用对称密钥加密真正的数据。这样既解决了密钥分发问题,又保证了加解密速度。
四、哈希函数:数据的数字指纹
哈希函数,你可以把它理解成数据的「数字指纹」。不管输入多大,输出都是固定长度。而且,哪怕只改一个bit,输出的哈希值都会完全不一样。
嵌入式里最常用的哈希函数就是SHA-256。输出256位,也就是32字节。我为什么偏爱SHA-256?因为它在安全性和计算开销之间取得了很好的平衡。SHA-1已经被证明不安全了,SHA-512对很多MCU来说又太吃资源。
SHA-256的核心特性:
- 单向性:从哈希值无法反推出原始数据
- 抗碰撞性:很难找到两个不同输入产生相同哈希值
- 雪崩效应:输入微小变化,输出天翻地覆
- 固定输出:不管输入多大,输出永远是32字节
我在项目中遇到过这样一个场景:需要验证固件是否被篡改。做法很简单——发布固件时计算SHA-256哈希值,设备端收到固件后重新计算哈希,对比一下就知道有没有被改过。
// 嵌入式SHA-256计算示例(基于mbedTLS)
#include <mbedtls/sha256.h>
int verify_firmware(const uint8_t *fw, size_t fw_len,
const uint8_t *expected_hash) {
uint8_t computed_hash[32];
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts(&ctx, 0); // 0表示SHA-224,1表示SHA-256
mbedtls_sha256_update(&ctx, fw, fw_len);
mbedtls_sha256_finish(&ctx, computed_hash);
mbedtls_sha256_free(&ctx);
// 对比哈希值
return memcmp(computed_hash, expected_hash, 32) == 0;
}
我曾经踩过的坑:有一次做固件完整性校验,我直接比较了哈希值,但忘了考虑字节序问题。PC端算出来的哈希和设备端算出来的,看起来一样,但字节序反了。排查了半天才发现是大小端的问题。所以,做哈希校验时,一定要确认两端的字节序一致。
五、哈希函数在签名验签中的角色
你可能会问:哈希函数跟签名验签有什么关系?关系大了去了。
非对称签名算法(比如RSA、ECDSA)有个特点——它们不能处理大数据。你想想看,一个固件可能几兆甚至几十兆,如果用私钥直接对整个固件签名,那计算量谁也扛不住。
所以实际做法是:先对固件计算SHA-256哈希,得到32字节的摘要,然后对这个摘要进行签名。验签时也一样,先计算哈希,再验证签名。
这样做有两个好处:
- 效率高:签名操作只处理32字节,而不是整个固件
- 安全性好:哈希函数的抗碰撞性保证了「改数据必改哈希」
我的建议:在设计签名验签方案时,一定要把哈希计算放在设备端。有些方案图省事,让服务器算好哈希再传给设备,设备直接验证签名。这就有安全漏洞了——攻击者可以同时篡改固件和哈希值。记住:哈希必须在本地计算,签名才可信。
六、本章小结
好了,这一章的内容就这些。咱们把三个核心概念理清楚了:
- 对称加密:速度快,适合加密数据,但密钥分发麻烦
- 非对称加密:解决了密钥分发问题,但速度慢,适合加密小数据
- 哈希函数:数据的数字指纹,单向、抗碰撞,是签名验签的基础