3、PKI体系在OTA中的应用:CA机构角色、证书链验证、CRL与OCSP在线状态检查

OTA升级,说白了就是给设备“打补丁”。但你怎么确定这个补丁是官方发的,不是黑客塞进来的?

我早年做第一版OTA方案时,就踩过这个坑。当时图省事,直接在固件包里塞了个签名,设备拿到后验一下签名就完事。结果呢?签名算法没问题,但签名用的私钥被逆向出来了——因为所有设备用的都是同一把私钥。那次教训让我彻底明白:没有PKI体系的OTA,就像没上锁的保险柜。

3.1 CA机构在OTA中的角色

CA(Certificate Authority),就是数字世界的“公安局”。它负责发身份证、验证身份、吊销坏人的证件。

在OTA场景里,CA的角色很明确:

  • 签发设备证书:每台设备出厂时,CA给它一个独一无二的身份凭证
  • 签发固件签名证书:OTA服务器用的签名证书,也是CA发的
  • 维护信任链:设备只信任CA签发的证书,其他的一概不认

核心原则:设备端只预置根CA证书,不预置任何中间证书或终端证书。所有信任都从根CA推导出来。

我见过不少团队图省事,直接把服务器证书硬编码到设备里。这样做的问题很明显——证书过期了怎么办?私钥泄露了怎么办?你得OTA升级固件才能换证书,但OTA本身又依赖这个证书……死循环了。

3.2 证书链验证:信任是怎么传递的

证书链,就是一条“信任传递链”。根CA信任中间CA,中间CA信任终端证书。设备只需要信任根CA,就能通过链式验证,信任任何一个由该CA体系签发的证书。

举个例子:

根CA证书(自签名,预置在设备中)
  └── 签发 → 一级中间CA证书
        └── 签发 → 二级中间CA证书
              └── 签发 → OTA服务器证书(用于签名固件)

设备验证固件签名时,会做这么几步:

  1. 拿到OTA服务器证书 + 所有中间CA证书
  2. 从最底层开始,逐级向上验证签名
  3. 一直验证到根CA证书——这个根证书设备出厂时就存好了
  4. 全部通过,信任建立;任何一环失败,直接拒绝

我的经验:证书链不要搞太长。3层就够了(根CA → 中间CA → 终端证书)。太长的话,设备端验证耗时多,而且中间证书的管理成本也高。我曾经见过一个项目搞了5层,结果设备升级时证书链解析就花了2秒多,用户都以为死机了。

证书链验证的流程,我画了张图:

证书链验证流程 证书链验证流程 根CA证书(预置) 步骤1:验证中间CA证书签名 中间CA证书 步骤2:验证终端证书签名 OTA服务器证书 验证要点 • 逐级向上,不可跳过 • 任何一级失败即拒绝 • 根CA必须预置在设备 • 证书链长度建议≤3层

3.3 CRL与OCSP:证书被吊销了怎么办

证书不是永久的。私钥泄露、员工离职、算法被破解……这些情况都需要吊销证书。那设备怎么知道一个证书还“活着”?

两种主流方案:

方案 全称 工作方式 OTA场景适用性
CRL 证书吊销列表 CA定期发布一个“黑名单”,设备下载后对照检查 适合离线设备、低功耗设备
OCSP 在线证书状态协议 设备实时向CA查询某个证书的状态 适合联网设备、实时性要求高的场景

3.3.1 CRL:离线检查的黑名单

CRL说白了就是一张表,里面列了所有被吊销的证书序列号。设备拿到证书后,去CRL里查一下——在列表里?吊销了,不认。不在列表里?继续验证。

CRL的格式大概长这样:

Certificate Revocation List (CRL)
├── 签发者: CN=OTA Root CA
├── 本次更新: 2025-01-01 00:00:00 UTC
├── 下次更新: 2025-02-01 00:00:00 UTC
├── 吊销证书列表:
│   ├── 序列号: 0x3A1F, 吊销日期: 2024-12-15, 原因: 私钥泄露
│   ├── 序列号: 0x4B2E, 吊销日期: 2024-12-20, 原因: 设备报废
│   └── 序列号: 0x5C3D, 吊销日期: 2024-12-28, 原因: 证书过期前撤销
└── 签名: [CA私钥签名,防篡改]

注意:CRL有个大问题——实时性差。CA可能一周才更新一次CRL,这期间被吊销的证书依然“有效”。我曾经遇到一个案例,攻击者利用这个时间差,用一台已泄露私钥的设备发起了OTA攻击。所以,CRL只适合对实时性要求不高的场景。

3.3.2 OCSP:实时查询的“在线警察”

OCSP解决了CRL的实时性问题。设备拿到证书后,直接向OCSP服务器发个请求:“这个证书还活着吗?”服务器秒回:“活着”或“死了”。

OCSP请求和响应的核心内容:

// OCSP请求(设备 → OCSP服务器)
{
  "协议版本": "1.0",
  "请求类型": "状态查询",
  "目标证书": {
    "签发者哈希": "0x...",
    "序列号": "0x3A1F"
  },
  "扩展": "请求签名(可选)"
}

// OCSP响应(OCSP服务器 → 设备)
{
  "响应状态": "成功",
  "证书状态": "good",  // good / revoked / unknown
  "本次更新": "2025-01-15 10:30:00",
  "下次更新": "2025-01-16 10:30:00",
  "签名": [OCSP响应服务器的签名]
}

OCSP的响应状态有三种:

  • good:证书有效,放心用
  • revoked:证书已被吊销,拒绝
  • unknown:查不到这个证书,可能是查询参数错了,也可能是CA压根没发过这个证书

我的建议:在OTA场景里,我倾向于混合使用。设备联网时优先走OCSP,实时性强;如果网络不好或OCSP服务器挂了,就回退到CRL——前提是设备本地缓存了一份不太旧的CRL。我在一个车机项目里就是这么做的,效果不错。

3.4 实际落地中的几个坑

理论说完了,聊聊实战中我踩过的坑:

  • 证书链不完整:设备只拿到了终端证书,没拿到中间CA证书。验证到一半卡住了。解决方案:OTA升级包必须把完整的证书链打包进去。
  • 时间不同步:证书有有效期,设备时间不对就会误判。我遇到过设备时间被重置到1970年,结果所有证书都显示“过期”。解决方案:设备在验证证书前,先通过NTP同步时间。
  • OCSP服务器单点故障:OCSP服务器挂了,所有设备都无法验证证书。解决方案:部署多台OCSP服务器,设备端配置多个OCSP地址,轮询或负载均衡。
  • CRL文件过大:设备内存有限,几万条吊销记录的CRL文件可能把内存撑爆。解决方案:使用分片CRL或增量CRL,只下载设备关心的那部分。

一句话总结:PKI体系在OTA中的核心价值,就是让设备能自主、安全、实时地验证固件的来源和完整性。CA是信任的起点,证书链是信任的传递,CRL/OCSP是信任的维护——三者缺一不可。