简易签名服务器的自建方案

简易签名服务器的自建方案

  • 作者:长江计算 易重辉

背景

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服务端,给大伙打个样,仅作参考,同志们可以自己选择技术栈搭建。

准备材料:

  1. 一个支持openUBMC构建的docker镜像
  2. 伙伴公司的证书文件
  3. 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 执行成功
6 个赞

唉,你们这个居然也是基于openssl得,那我们得那种就不好参考了,还是需要大改了。

1 个赞