简易签名服务器的自建方案
- 作者:长江计算 易重辉
背景
openUBMC中的伙伴签名目前有文件模式和签名服务器两种模式,文件模式会将证书直接暴露给开发者,有一定的安全风险,通过搭建签名服务器,自建签名服务,有助于提高固件安全性,降低风险。
目前,此方案已和社区 bingo 软件完成对接,通过在 manifest.yml 文件中进行相关配置,则可以通过 bingo 软件进行签名,无需将证书暴露给开发者。
签名网络传输协议
bingo与签名服务器的交互如下图所示:
根据上图可知,先由bingo的签名模块向签名服务器发送POST请求,请求中包含待签名文件信息,签名服务器收到请求后,进行签名,返回签名后的文件。
其中【签名参数】主要包括:
| 参数 | 类型 | 解释 |
|---|---|---|
| CertId | 字符串 | 证书ID |
| FileName* | 字符串 | 待签名文件名(example.img) |
| FileSha256* | 字符串 | 文件sha256校验数据 |
| Token | 字符串 | 认证令牌 |
| Version | 字符串 | 协议版本 |
- 注:*号为必填项,其他三项为扩展字段,后文有进一步解释。
假如【待签名文件】名为 example.img,则【签名后文件】则名为 example.img.signed.zip
example.img.signed.zip 为一个zip压缩包,解压后包含以下四个文件:
| 文件名 | 解释 |
|---|---|
| root.crl | root CA的crl文件 |
| root.crl.sha256 | root CA的crl sha256 |
| example.img.cms | 签名后的文件 |
| example.img.cms.sha256 | 签名后的文件 sha256 |
综上所述,签名网络传输过程是这样的:bingo进行BMC构建过程中,会有几次签名的动作,会对某些文件进行签名,如果配置签名服务器的形式,就会将待签名的文件通过POST请求发送给签名服务器,同时也会计算sha256校验同步发给签名服务器,签名服务器收到请求后,就进行签名,并返回签名后的文件,bingo签名模块收到签名后的文件后,进行后续操作。
在整个通信过程中,所有传输的文件均要通过sha256进行校验,保证数据的完整性。
除此之外,还有一些扩展性质的字段:
Token为签名服务器的认证令牌,每次签名请求都需要携带此令牌,签名服务器收到请求后,会验证此令牌是否合法,如果不合法,则拒绝签名。
CertId为证书ID,也就是签名服务器可以有多个证书,根据不同的ID来使用不同的证书进行签名,有助于证书吊销和管理。
Version为协议版本,未来如果bingo签名模块升级,可以升级协议版本,保证前后兼容。
签名服务的具体流程
其实通过上述的网络传输协议,可以确认签名服务主要有以下几个功能:
- *响应POST请求并回传文件
- 验证Token是否合法
- 验证CertId是否合法
- 验证Version是否合法
- *验证文件是否完整
- *执行签名处理流程
- *返回文件打包
扩展字段不在本次讨论范围内。
其中的签名处理流程主要参考bingo的本地文件签名流程,我们可以从manifest的构建日志中查到有如下打印:
[2025-07-04 01:53:54,794 INFO] >> openssl x509 -in /home/workspace/manifest/build/product/ca/rootca.der -inform der -outform pem -out rootca.pem
[2025-07-04 01:53:54,860 INFO] >> openssl crl -in /home/workspace/manifest/build/product/ca/rootca.crl -inform der -outform pem -out cms.crl.pem
[2025-07-04 01:53:54,875 INFO] >> hpm_signer -s /home/workspace/manifest/build/product/ca/signer.pem -t /home/workspace/manifest/build/product/ca/ts_signer.pem -T /home/workspace/manifest/build/product/ca/tsa.cnf -i /home/workspace/manifest/temp/build_openUBMC_debug_dev/sdk/origin_uboot_debug/uboot.bin -o /home/workspace/manifest/temp/build_openUBMC_debug_dev/sdk/origin_uboot_debug/uboot.bin.cms
[2025-07-04 01:53:54,943 INFO] >> hpm_verify -r rootca.pem -C cms.crl.pem -c /home/workspace/manifest/temp/build_openUBMC_debug_dev/sdk/origin_uboot_debug/uboot.bin -s /home/workspace/manifest/temp/build_openUBMC_debug_dev/sdk/origin_uboot_debug/uboot.bin.cms
显然通过 openssl x509、openssl crl、hpm_signer、hpm_verify四条命令,以及rootca.der、rootca.crl、signer.pem、ts_signer.pem、tsa.cnf、uboot.bin作为输入,即可完成签名。其中uboot.bin是待签名文件,得到uboot.bin.cms为已签名文件。
综上,签名服务的核心特征有:
- 通过openssl x509、openssl crl、hpm_signer、hpm_verify四条命令完成签名。
- 需要在签名服务器中保存的证书文件有:rootca.der、rootca.crl、signer.pem、ts_signer.pem、tsa.cnf。
- 需要响应来自 bingo 的 HTTP POST请求,并将签名文件打包返回
签名服务器搭建流程
以下流程是我基于go搭建的HTTP服务端,给大伙打个样,仅作参考,同志们可以自己选择技术栈搭建。
准备材料:
- 一个支持openUBMC构建的docker镜像
- 伙伴公司的证书文件
- SignServer.zip
思路:
以BMC的构建docker镜像为基础,安装go运行环境,导入签名材料,再发布为签名服务器运行镜像,运行此镜像则完成签名服务器搭建,可对外提供签名服务。
SignServer.zip 目录结构如下:
.
├── README.md # 指导手册
├── ca # 证书目录,需要放入自己公司的证书
├── client # 客户端调试目录
│ ├── rootfs_TaiShan200_2280v2.filelist # 调试文件
│ └── sign_client.py # 客户端调试脚本
├── dockerfile # docker镜像生成文件
└── sign_service.go # 签名服务主程序
解压 SignServer.zip 并将进入此目录
zip -q SignServer.zip && cd SignServer
将自己公司的证书放在ca目录
需要有以下几个文件,当然,有些文件不是必须的
ca/
├── rootca.crl
├── rootca.der
├── signer.pem
├── signer.pem.enc
├── ts_signer.pem
├── ts_signer.pem.enc
└── tsa.cnf
设置dockerfile中的依赖镜像为bmc的构建镜像
以openubmc/25.03:latest为例(此镜像为BMC构建镜像):
FROM openubmc/25.03:latest
使用dockerfile生成go运行环境镜像
在SignServer目录下执行命令即可生成镜像:
docker build -t sign-server .
启动容器
执行命令:
docker run -d \
-p 8080:8080 \
--privileged=true \
--name sign-server \
--restart=always \
sign-server:latest
启动后,会使用宿主机的8080端口侦听来自客户端的请求。
至此,签名服务器搭建完毕
调试验证
从客户端和BMC构建两个角度进行调试验证。
客户端调试
在宿主机的client目录下,直接执行命令:
python3 sign_client.py
收到以下打印说明签名服务正常:
GET请求响应: 这是GET请求的响应
{'metadata': '{"CertId": "BMC-DEV-2024-0001-0365", "FileName": "rootfs_TaiShan200_2280v2.filelist", "FileSha256": "db2597b7a3051b19b1521d73d304a7da0ed0b71e0a4ef53678fa0fad56c783b8", "Token": "111", "Version": "222"}'}
POST请求成功,正在保存文件...
文件已保存为:rootfs_TaiShan200_2280v2.filelist.signed.zip
BMC构建调试
需要用伙伴证书中的对应证书文件替换掉华为的rootca.der、rootca.crl(位于build/product/ca/)。
在 manifest.yml 文件的 [base] [signature]字段中添加:
simple_signer_server:
url: "http://localhost:8080/post"
rootca_der: "${product}/ca/rootca.der"
cert_der: "BMC"
ssl_verify: false
其中 url 为签名服务器的地址,建议用 https 地址;rootca_der 为伙伴根 CA 的公钥证书, der 格式;cert_id 为签名证书 ID (以字母开头,可包含数字、下划线或连字符,但不能以符号结尾),目前我的方案暂时没有用到;ssl_verify 为是否验证 https 证书。
详细的字段定义请参考构建环境中的本地文件manifest.schema.json:
当前,我在 wsl 环境上做此验证,因此 url 直接写 http 协议的 localhost 地址,且 ssl_verify 设置为 false ,表示不验证 https 证书。
manifest 构建打印为:
{'metadata': '{"CertId": "BMC", "FileName": "rootfs_openUBMC.filelist", "FileSha256": "0214dce373a7de99cf6b121d4c4ae4647bb81fb15e6d28d46270d1f9e2d4a59a", "Token": "", "Version": "0"}'}
签名服务接口返回成功,正在保存文件...
签名结果已保存为:signed.zip
>> unzip -o signed.zip
给 hpm 包 rootfs_openUBMC.hpm 签名
>> cms_sign_hpm.sh 2 rootfs_openUBMC.hpm
复制 rootfs_openUBMC.hpm.signed 到 /home/workspace/manifest/output/rootfs_openUBMC.hpm
任务 work.task_sign_and_pack_hpm 完成
任务 work.task_buildhpm_ext4 完成
任务 work.task_buildgppbin 完成
任务 work.task_hpm_envir_prepare 完成
任务 work.task_build_rootfs_img 完成
任务 personal 执行成功
