3. 密码学基础(下)

上一讲我们聊了哈希和对称加密,今天要啃的这块骨头更硬——数字签名和PKI。说实话,我刚入行时觉得签名就是个「加密的逆过程」,直到在固件更新项目里栽了跟头,才真正理解它有多重要。

你想想看,固件更新包从服务器飞到设备上,中间可能经过多少个节点?CDN、代理、甚至恶意中间人。如果没有签名验证,设备怎么知道这个包是官方发的,还是黑客塞的恶意代码?

核心观点:数字签名解决的是「谁写的」和「被改过没」两个问题。前者叫身份认证,后者叫完整性校验。缺一个,固件更新就是裸奔。

3.1 数字签名:不是加密,是「盖章」

很多人以为数字签名就是把消息加密。不对。签名用的是私钥,验证用的是公钥。私钥只有你一个人有,公钥全世界都知道。这就像你在合同上盖了个章,别人拿你的印章样本一比对,就知道是不是你盖的。

具体流程分三步:

  1. 签名方:对消息做哈希,然后用私钥加密这个哈希值,得到签名。
  2. 验证方:用公钥解密签名,得到哈希值A;再对收到的消息自己做哈希,得到哈希值B。
  3. 比对:A == B?是则签名有效,否则无效。

这里有个关键点——签名不是加密消息本身,而是加密消息的哈希。为什么?因为非对称加密慢得要命。你想想看,一个固件包动辄几十MB,用私钥加密整个包?那得等到天荒地老。哈希一下再签名,效率高得多。

我的经验:我在一个IoT项目里见过有人直接用RSA加密整个固件包,结果升级一次要等15分钟。后来改成先哈希再签名,整个流程压缩到30秒以内。嗯,选对方案比堆算力重要得多。

3.2 RSA签名:老当益壮

RSA签名算法,说白了就是RSA加密的「反向使用」。私钥加密,公钥解密。但实际工程中没人用「教科书式RSA」,因为太脆弱了。

实际用的是PKCS#1 v1.5或PSS填充方案。填充的作用是什么?防止你伪造签名。教科书RSA有个数学性质:如果我知道一个有效签名,我可以构造另一个有效签名。这还得了?填充就是打破这个数学结构。

特性 PKCS#1 v1.5 PSS
兼容性 极好,几乎所有库都支持 较新,部分老旧系统不支持
安全性 有已知攻击(Bleichenbacher),需谨慎实现 有严格安全证明,推荐使用
签名长度 固定,等于密钥长度 固定,等于密钥长度
适用场景 遗留系统、兼容性优先 新设计、安全敏感场景

我个人习惯在新项目里直接用PSS。虽然兼容性差一点,但安全这东西,能多一分是一分。我曾经在一个金融级固件项目里坚持用PSS,被同事说「过度设计」。结果后来行业爆出Bleichenbacher攻击变种,我们完全不受影响。

3.3 ECDSA签名:小而美

ECDSA(椭圆曲线数字签名算法)是RSA的替代者。同样的安全强度,ECDSA的密钥长度只有RSA的十分之一。举个例子:256位的ECDSA和3072位的RSA安全等级差不多,但签名长度只有64字节,RSA要384字节。

这对嵌入式设备太友好了。你想想看,一个MCU的Flash可能只有512KB,RAM只有64KB。RSA的密钥和签名占用的空间,在ECDSA面前就是庞然大物。

ECDSA的数学原理比RSA复杂得多,但工程上你不需要理解所有细节。你只需要知道:

  • 它基于椭圆曲线上的离散对数问题
  • 签名由两个整数(r, s)组成
  • 曲线参数选择至关重要,别自己发明曲线

避坑指南:我曾经见过一个团队自己「优化」了ECDSA曲线参数,结果签名验证时好时坏。查了三天才发现是曲线参数不合法。记住:永远使用标准曲线,比如secp256r1(P-256)或secp384r1(P-384)。自己造曲线是安全工程师的大忌。

下面是一个简单的ECDSA签名验证流程示意图:

ECDSA 签名与验证流程 签名方(私钥) 计算消息哈希 H(m) 用私钥签名哈希 → (r, s) 发送:消息 + 签名(r, s) 验证方(公钥) 计算消息哈希 H(m)' 用公钥验证签名(r, s) 比对 H(m) == H(m)' ? 验证通过 → 固件可信 私钥签名,公钥验证 签名不可伪造

3.4 公钥基础设施(PKI):信任的锚点

好,现在你有了签名和验证的能力。但问题来了:设备的公钥从哪来?如果公钥本身被篡改了,那签名验证还有什么意义?

这就是PKI要解决的问题。PKI说白了就是一个「信任链」:你信任某个权威机构(CA),CA给各个实体颁发证书,证书里绑定了公钥和身份信息。你验证证书的签名,就知道这个公钥是不是真的属于某个实体。

PKI的核心组件:

  • CA(证书颁发机构):信任的根,自己签自己的证书(自签名)
  • 中间CA:根CA签发的下级CA,用于分层管理
  • 终端实体证书:最终设备或服务器的证书
  • CRL/OCSP:证书吊销机制,告诉别人哪些证书已经失效

关键理解:PKI不是技术问题,是信任问题。你选择信任哪个根CA,决定了你的安全边界。在固件更新场景里,设备出厂时烧录的根证书,就是整个信任链的锚点。

3.5 证书链:从根到叶子

证书链验证,说白了就是「向上追溯直到信任的根」。每个证书都由上一级CA签名,直到根CA。根CA的证书是自签名的,也就是自己证明自己。

验证流程:

  1. 拿到终端证书和中间CA证书
  2. 用中间CA的公钥验证终端证书的签名
  3. 用根CA的公钥验证中间CA证书的签名
  4. 检查根CA是否在设备的信任列表中
  5. 检查所有证书是否过期、是否被吊销

这里有个坑:证书链的长度。我见过一个项目,证书链有7层。每次固件更新验证都要遍历7个证书,MCU跑得气喘吁吁。后来我建议他们压缩到3层(根CA → 中间CA → 终端),验证速度提升了3倍。

我的建议:嵌入式设备里,证书链不要超过4层。每多一层,验证时间翻倍,Flash占用也翻倍。另外,根证书最好固化在BootROM里,防止被篡改。

3.6 固件更新中的PKI实践

在实际的固件更新系统里,PKI的部署通常是这样:

组件 存储位置 作用
根CA私钥 离线HSM(硬件安全模块) 签发中间CA证书,几乎从不使用
中间CA私钥 在线签名服务器 签发固件更新包的签名证书
签名证书私钥 签名服务器内存 对固件包进行签名
根CA公钥(证书) 设备BootROM 验证整个证书链的根

你可能会问:为什么不用根CA直接签固件?省掉中间层多好?

原因很简单:安全隔离。如果签名服务器被攻破,攻击者只能拿到中间CA的私钥。你可以吊销中间CA证书,重新签发一个。但如果根CA私钥泄露了,整个信任体系就崩塌了,所有设备都得回厂更新根证书。

血的教训:我曾经在一个项目里看到他们把根CA私钥放在一个联网服务器上,说是「方便自动化」。我当场就让他们改成了离线HSM。你想想看,根CA私钥一旦泄露,攻击者可以给任何固件签名,所有设备都是待宰的羔羊。这个风险绝对不能冒。

3.7 小结:选型建议

说了这么多,最后给点实际建议:

  • 新项目:优先选ECDSA + P-256曲线,签名短、速度快、安全性好
  • 兼容老旧系统:用RSA-2048 + PSS填充,别用PKCS#1 v1.5
  • 证书链:控制在3层以内,根证书固化在硬件中
  • 私钥保护:根CA私钥必须离线存储,中间CA私钥可以用HSM或加密机

嗯,密码学基础这部分就到这里。这些概念看起来抽象,但你在固件更新项目里迟早会碰到。到时候回头翻翻这章,应该能帮你少踩几个坑。