openUBMC CSR/PSR 内层 EEPROM ECC 远程签名接入实践(SignServer + bingo)
社区里关于 SignServer 的帖子,之前主要集中在 HPM 外层 CMS/TSA 链路。
但在 CSR/PSR 场景中,除了外层 HPM 签名,还存在一个经常被忽略的点:
EEPROM/PSR 内层 .bin 也有自己独立的 ECC 签名链路。
这条链路和外层 HPM/CMS 不是一套机制,也不共用证书链。
本文记录我在 bingo 中接入 EEPROM 内层 ECC 远程签名 的实践方式,以及一些容易踩坑的边界。
1. 先说清楚:外层和内层是两条独立链路
在 CSR/PSR 出包里,通常同时存在两类签名:
-
外层 HPM 签名
-
对 HPM 外层文件清单做 CMS 签名
-
依赖
rootca.der/rootca.crl -
使用
hpm_signer/hpm_verify
-
-
内层 EEPROM ECC 签名
-
对要写入 EEPROM 的原始二进制内容做 ECC 签名
-
依赖固定公钥
device_desc_pubkey.bin -
设备侧和构建侧都围绕这个固定公钥做验签
-
这两条链路互不替代:
-
外层 CMS 成功,不代表内层 ECC 正确
-
内层 ECC 成功,也不代表外层 HPM/CMS 正确
所以建议把它们拆开理解、拆开配置、拆开排查。
2. 设备侧信任模型
内层 ECC 这条链路最关键的不是证书链,而是 固定公钥。
设备侧验签使用的是固定公钥文件:
/opt/bmc/trust/partner/device_desc_pubkey.bin
这意味着:
-
BMC 环境并不是拿 PEM/CRT 文本证书去验 EEPROM
-
它信任的是一个固定的原始公钥二进制文件
-
如果你后续切换了签名私钥,但没有同步更新设备侧公钥,升级就会失败
我的建议是:
-
为同一产品线/同一环境,固定使用一套 ECC 公私钥
-
公钥固定放到设备侧信任位置
-
私钥固定导入 SignServer 的 ECC worker
-
不要每次出一个 CSR/PSR 包就重新生成一套 ECC 密钥
3. ECC Worker 的要求
和外层 HPM/CMS 不同,EEPROM 内层 ECC 不能直接复用 CMS/TSA worker。
它需要单独的 ECC 签名 worker,满足以下要求:
-
接口方式:
POST -
Content-Type: application/octet-stream -
请求体:待签名原始二进制内容
-
响应体:原始 ECDSA DER 签名字节
-
算法:P-256 / SHA-256 / DER 编码
也就是说,这条链路不是 multipart 文件上传,不是 CMS 回包,也不是时间戳服务。
如果你之前做过 demo/ecc_sign/sign_server.py 的 simple server,会发现两者有一个关键区别:
-
simple server:接收
files={'file': ...} -
SignServer ECC:直接接收原始二进制 body
这一点如果搞错,服务端即使收到了请求,也会因为协议不匹配而无法正确签名。
4. 公钥准备方式
如果你只是先做验证,仓库里已经有一个最小 demo:
cd bingo/demo/ecc_sign
sh 1_create_sign_ecc.sh
执行后会生成:
-
private_key.pem -
device_desc_pubkey.bin
这个 demo 的作用很明确:
-
private_key.pem:构建侧或服务端签名使用 -
device_desc_pubkey.bin:设备侧与构建侧验签使用
当前 bingo 适配里,pubkey_bin 接受以下两种格式:
-
65 字节未压缩公钥点(首字节
0x04) -
64 字节
X||Y原始拼接
如果你不是用 demo,而是从已有 ECC 证书或 P12 中导出公钥,请确保最终落到设备侧/构建侧的是 原始公钥二进制,不是 PEM 文本证书本身。
5. 配置入口
和外层 CSR/HPM 签名一样,当前实现读取的是 .bmcgo/config 体系,而不是 manifest.yml:
-
当前产品目录
.bmcgo/config -
~/.bmcgo/config -
/etc/bmcgo.conf
建议优先在当前项目目录下配置。
6. 内层 ECC 配置示例
在 .bmcgo/config 中加入:
[eeprom_signserver]
url = https://<SIGN_SERVER_DOMAIN>/signserver/process?workerId=<ECC_WORKER_ID>
pubkey_bin = /path/to/device_desc_pubkey.bin
curl_cafile = /root/ca/ca.pem
insecure = false
如果只是先联通调试,也可以先这样:
[eeprom_signserver]
url = https://<SIGN_SERVER_DOMAIN>/signserver/process?workerId=<ECC_WORKER_ID>
pubkey_bin = /path/to/device_desc_pubkey.bin
insecure = true
参数说明:
-
url:ECC worker 接口地址 -
pubkey_bin:构建侧强制验签使用的固定公钥文件 -
curl_cafile:HTTPS 校验用 CA 文件 -
insecure:跳过 HTTPS 校验
说明:
-
pubkey_bin是必填项 -
如果
eeprom_signserversection 存在但缺少pubkey_bin,构建会直接报错 -
如果同时配置了
eeprom_signserver和旧的eeprom_self_sign/eeprom_server_sign,当前实现会优先走eeprom_signserver
7. 构建阶段发生了什么
当 bingo 进入内层 EEPROM ECC 签名阶段,流程如下:
-
根据
eeprom_signserver选择remote_ecc分支 -
先生成待签名的原始 EEPROM 二进制内容
-
使用
application/octet-stream将原始字节 POST 给 SignServer -
服务端返回 DER 编码的 ECDSA 签名字节
-
bingo 立即使用
pubkey_bin做一次本地强制验签 -
验签通过后,才把签名写回 EEPROM 签名区
这一步的意义很大:
-
不等到设备侧升级时才暴露问题
-
构建阶段就能发现“私钥/公钥不匹配”或“服务回包格式错误”
8. 关键日志
构建成功时,通常可以看到类似日志:
eeprom签名使用 remote_ecc 方法
>> POST SignServer接口
开始校验eeprom SignServer签名结果...
>> verify_eeprom_signserver --check-file /path/device_desc_pubkey.bin --data-bytes ... --sig-bytes ...
eeprom SignServer签名验签成功
如果同时也配置了外层 HPM 签名,你还会看到:
hpm签名使用 remote_cms 方法
这表示两条链路都已经接通。
9. 常见问题
9.1 为什么明明外层签名正常,设备升级还是失败
因为外层 CMS 和内层 ECC 是两回事。
如果设备升级阶段报 EEPROM/PSR 校验失败,优先看内层 ECC,而不是只盯着外层 HPM。
9.2 为什么这里要用 pubkey_bin,而不是 rootca.der/rootca.crl
因为内层 ECC 不是 CMS 证书链验签模型。
它是“固定公钥 + 原始签名”的模型,所以这里看的是 device_desc_pubkey.bin,不是 CMS 的根证书和吊销列表。
9.3 为什么不能直接复用 simple sign server
因为协议不一样:
-
simple server 用 multipart 上传文件
-
当前 SignServer ECC 分支要求原始二进制 body
两者接口不兼容。
9.4 如果公钥和私钥不匹配会怎样
当前实现会在构建阶段直接报错,不会继续回包。
这也是为什么 pubkey_bin 必须和服务端 ECC worker 内的私钥严格对应。
9.5 设备侧到底应该放什么
设备侧放的是固定公钥文件:
/opt/bmc/trust/partner/device_desc_pubkey.bin
不是 PEM 证书链,不是 rootca.der,也不是 CMS 的 CRL 文件。
10. 推荐使用方式
我更推荐下面这种方式:
-
先固定一套 ECC 密钥对
-
公钥导出为
device_desc_pubkey.bin -
公钥部署到 BMC 固定信任位置
-
私钥导入 SignServer ECC worker
-
所有 CSR/PSR 内层签名都复用这套固定密钥
这样做的好处是:
-
设备侧信任关系稳定
-
不需要每次出包都重新换公钥
-
排障路径清晰
11. 结语
CSR/PSR 场景里,外层 HPM/CMS 和内层 EEPROM ECC 经常被混在一起讨论。
但从实现和运维角度看,它们最好拆开理解:
-
外层看
hpm_signer/hpm_verify/rootca.der/rootca.crl -
内层看
device_desc_pubkey.bin + raw body + DER signature
只要把这个边界想清楚,内层 ECC 接入 SignServer 并不复杂。
真正要守住的,是固定公钥信任模型不要漂移。
参考资料
-
PKI 实践指南:构建完整的签名中心服务系统 x openUBMC hpm包签名实践
https://discuss.openubmc.cn/t/topic/2934 -
openUBMC 远程签名接入教程(SignServer + hpm_signer)
https://discuss.openubmc.cn/t/topic/3825
