3、OpenSSL工具链:安装、配置与自签名根证书

说到固件签名,OpenSSL 是绕不开的工具。我入行那会儿,前辈丢给我一句「去学 OpenSSL」,然后扔了个 man 页面链接就走了。说实话,那感觉就像给你一把瑞士军刀,却不告诉你哪个是开瓶器。

这一章,咱们就把 OpenSSL 这把刀磨利索。从安装配置,到常用命令,再到生成第一张自签名根证书。嗯,一步步来。

3.1 安装与基础配置

OpenSSL 的安装其实没什么玄学。不同平台有不同玩法,我列个表,你对着看就行。

平台 安装方式 备注
Ubuntu/Debian sudo apt install openssl 一般系统自带,版本可能偏旧
CentOS/RHEL sudo yum install openssl 同上,建议用 EPEL 源更新
macOS brew install openssl 系统自带的是 LibreSSL,建议用 Homebrew 装
Windows 下载安装包或 WSL 我个人习惯用 WSL,省心

装完之后,先看一眼版本:

openssl version -a

你会看到类似 OpenSSL 1.1.1w 11 Sep 2023 的输出。为什么强调版本?因为 1.1.1 和 3.x 的某些命令参数有差异。我在项目中遇到过因为版本不同导致脚本跑挂的情况,后来学乖了,第一件事就是锁版本。

我的习惯: 在 CI/CD 环境里,我会显式指定 OpenSSL 版本号,或者在 Dockerfile 里固定镜像标签。别让「版本漂移」成为你签名流程里的定时炸弹。

3.2 配置文件:openssl.cnf 的秘密

很多人觉得 OpenSSL 难用,其实一半的坑都在配置文件上。默认的 openssl.cnf 路径可以通过 openssl version -d 查到。但说实话,我从来不用系统自带的那个——太啰嗦,而且很多默认值不适合固件签名场景。

我自己会准备一个精简版配置,像这样:

[ req ]
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only
prompt              = no

[ req_distinguished_name ]
countryName             = CN
stateOrProvinceName     = Beijing
localityName            = Beijing
organizationName        = MySecureBoot
organizationalUnitName  = FirmwareSigning
commonName              = My Root CA
emailAddress            = ca@mysecureboot.com

[ ca ]
default_ca = CA_default

[ CA_default ]
database    = index.txt
serial      = serial
new_certs_dir = newcerts
default_md  = sha256
policy      = policy_loose
copy_extensions = copy

你看,我故意把 prompt 设成了 no。为什么?因为自动化脚本里不能有交互式输入。你想想看,半夜三点流水线跑起来,弹个对话框让你输国家代码,那画面太美我不敢看。

注意: 配置文件里的 database、serial、new_certs_dir 这些路径,在运行命令前必须手动创建好。我曾经因为忘了建 newcerts 目录,折腾了半小时才找到原因。

3.3 常用命令速查

OpenSSL 命令多如牛毛,但固件签名场景下,常用的就那么十几个。我按用途分了个类:

用途 命令示例 说明
生成私钥 openssl genrsa -out key.pem 2048 2048 位 RSA,固件签名够用了
生成 CSR openssl req -new -key key.pem -out csr.pem -config openssl.cnf 证书签名请求,交给 CA 去签
自签名证书 openssl req -x509 -new -key key.pem -out cert.pem -days 3650 -config openssl.cnf 直接生成自签名证书,省掉 CSR 步骤
查看证书 openssl x509 -in cert.pem -text -noout 看证书内容,调试时必备
计算哈希 openssl dgst -sha256 -sign key.pem -out sig.bin firmware.bin 对固件做哈希签名
验证签名 openssl dgst -sha256 -verify pub.pem -signature sig.bin firmware.bin 验签,确认固件没被改过

这些命令我几乎每天都要敲。说实话,敲多了就形成肌肉记忆了。但有个坑我得提醒你——genrsa 生成的私钥默认是 PKCS#1 格式,而有些固件签名工具只认 PKCS#8。遇到这种情况,加一步转换:

openssl pkcs8 -topk8 -inform PEM -outform PEM -in key.pem -out key_pkcs8.pem -nocrypt
核心原则: 私钥永远不要离开你控制的环境。生成、存储、使用,全程加密。我在一个项目里见过有人把私钥明文放在 Git 仓库里……嗯,那是个很长的故事。

3.4 生成自签名根证书

好,重头戏来了。咱们亲手造一张根证书。这张证书将来会用来签发固件签名证书,所以它的私钥要像命根子一样保护好。

步骤其实就三步:

  1. 生成根证书私钥
  2. 生成根证书(自签名)
  3. 验证证书内容

先跑第一步:

mkdir -p ~/myca && cd ~/myca
openssl genrsa -aes256 -out rootCA.key 4096

这里我用了 -aes256,意思是私钥本身用 AES-256 加密。每次使用私钥时都要输密码。你可能会觉得麻烦,但想想看——如果私钥文件被人拷走了,没有密码他照样干瞪眼。

第二步,生成自签名根证书:

openssl req -x509 -new -sha256 -key rootCA.key -out rootCA.crt -days 7300 -config openssl.cnf

这里 -days 7300 是 20 年。根证书有效期长一点没关系,反正它只用来签中间证书,不直接签固件。我见过有些公司设 10 年,也有设 25 年的。我个人习惯是 20 年,够长但也不至于太离谱。

第三步,验证一下:

openssl x509 -in rootCA.crt -text -noout | head -20

你会看到证书的颁发者(Issuer)和主题(Subject)是一样的——这就是自签名的特征。还会看到签名算法是 sha256WithRSAEncryption,公钥长度 4096 位。

一个小技巧: 验证完证书后,顺手算一下证书的指纹:openssl x509 -fingerprint -sha256 -in rootCA.crt -noout。把这个指纹记下来,以后部署到设备端时,可以用来做证书锁定(Certificate Pinning)。

3.5 知识体系:根证书生成流程

说了这么多,咱们用一张图把整个流程串起来。这样你脑子里就有个全局画面了。

自签名根证书生成流程 1. 准备目录与配置 2. 生成 RSA 4096 私钥 3. 自签名生成根证书 4. 验证证书内容 5. 安全存储私钥与证书 关键输出 rootCA.key(加密私钥) rootCA.crt(自签名证书) 证书指纹(用于锁定) 安全提醒 私钥加密存储 离线保管 定期备份

这张图把整个流程拆成了五步。你看,从准备环境到安全存储,每一步都有明确的产出物。我当年第一次做根证书时,就是照着这个思路一步步来的,没出过岔子。

3.6 避坑指南

最后,分享几个我踩过的坑:

  • 私钥权限: 生成完私钥,立刻 chmod 600 rootCA.key。别问我为什么强调这个——有一次我忘了设权限,同事一个 cat 就把私钥内容打到终端上了。
  • 证书序列号: 自签名证书的序列号是随机生成的,但如果你用 CA 模式签发子证书,必须手动维护 serial 文件。我习惯用 echo 01 > serial 初始化。
  • 哈希算法: 别再用 SHA-1 了。虽然 OpenSSL 默认可能还是 SHA-1,但你一定要显式指定 -sha256。SHA-1 的碰撞攻击已经不是理论问题了。
  • 备份策略: 根证书私钥建议做离线备份,存到硬件安全模块(HSM)或者加密的 U 盘里。我见过一家公司把根证书私钥放在共享文件夹里,结果被勒索病毒一锅端了。
血的教训: 我曾经在一个项目中,因为测试环境和生产环境用了同一套根证书私钥,导致测试固件被误刷到量产设备上。从那以后,我坚持「测试 CA」和「生产 CA」完全隔离,连配置文件都分开管理。

好了,这一章的内容就到这儿。OpenSSL 工具链的安装配置、常用命令、自签名根证书生成,咱们都过了一遍。这些东西看着简单,但每一个细节都关系到固件签名的安全性。你动手试试,有问题随时回来翻翻这一章。