PKI 实践指南:构建完整的签名中心服务系统 x openUBMC hpm包签名实践

1. 引言

本文基于 SignServer 社区版,面向验证 / PoC 场景,给出一套可复制的最小签名中心(CMS + TSA)实践;示例以可维护与可扩展为优先。文中涉及的 Keyfactor / EJBCA / SignServer 仅作开源实现示例,不构成任何背书。示例配置均为演示用途,生产上线需结合贵司的 mTLS/鉴权、密钥保护、审计留存、容量与高可用策略另行完善。


2. 架构与术语

  • SignServer:签名服务框架,内部以 Worker 为单位组织模块;

  • Worker:包括 Signer(如 CMSSigner、TimeStampSigner)与 Crypto Token(密钥与证书载体);

  • Crypto TokenPKCS12(P12/JKS);

  • Admin Web/CLI/REST:管理界面、命令行与 HTTP 接口。


3. 环境与前置条件

主机与系统

  • Ubuntu 20.04 Server(离线安装),4G+ RAM / 30G+ 磁盘(按并发放大);

  • 容器运行时:Docker;docker‑compose 。

你已准备完毕,无需额外安装步骤。

证书与信任

  • 管理端客户端证书 (由 EJBCA 的 ManagementCA 或等价 CA 签发),已导入浏览器。

持久化与权限(必读)

  • SignServer 容器对持久化目录以 UID=10001, GID=10001 写入;

  • 必须将挂载到容器的目录设置为 10001:10001,否则读写失败。

3.1 目录与权限(复制即用)

  # 项目目录(存放 compose.yml)
  sudo mkdir -p /opt/signserver-compose/
  
  # 持久化与证书目录(将被挂载进容器)
  sudo mkdir -p /opt/signserver-data/ /opt/signserver-secrets/
  
  # 关键:目录所有权设为 10001:10001(必须)
  sudo chown -R 10001:10001 /opt/signserver-data/ /opt/signserver-secrets/
  
  # 自检(应看到所有者/组均为 10001 10001)
  ls -ld /opt/signserver-data/ /opt/signserver-secrets/

3.2 从 EJBCA 获取 ManagementCA.crt(PEM → CRT)

  1. 管理员浏览器打开: https://<EJBCA主机或IP>/ejbca/adminweb/ca/cafunctions.xhtml 例如:https://192.168.13.115/ejbca/adminweb/ca/cafunctions.xhtml

  2. CA Functions 页面下载 CA certificate (PEM/Base64),保存为 ManagementCA.pem

  3. 转为 .crt(保持 PEM 编码),并放入挂载目录 /opt/signserver-secrets/

  # 假设浏览器下载到 ~/Downloads/ManagementCA.pem
  sudo openssl x509 -in ~/Downloads/ManagementCA.pem -outform PEM \
    -out /opt/signserver-secrets/ManagementCA.crt
  
  # 设置所有者(与目录一致)
  sudo chown 10001:10001 /opt/signserver-secrets/ManagementCA.crt

说明:.crt 为期望文件名/扩展名;内容保持 PEM(Base64)即可满足挂载使用。

3.3 (可选)为 Docker Daemon 配置 systemd 代理

适用场景:环境需通过 HTTP(S) 代理访问外网镜像仓库或网络资源时使用。请确保此操作符合贵司合规要求;示例中的 IP/端口与 NO_PROXY 请按你的实际网络替换。

创建代理配置

 sudo mkdir -p /etc/systemd/system/docker.service.d
 sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf

在文件中添加以下内容(按需替换代理地址与 NO_PROXY):

 [Service]
 Environment="HTTP_PROXY=http://192.168.xx.xxx:7890"
 Environment="HTTPS_PROXY=http://192.168.xx.xxx:7890"
 Environment="NO_PROXY=127.0.0.1,localhost,192.168.13.116"

3.4 重新加载 systemd 并重启 Docker

执行以下命令让配置生效:

 sudo systemctl daemon-reload
 sudo systemctl restart docker

然后检查 Docker 是否正常运行:

 sudo systemctl status docker --no-pager -l

如果 Active: active (running),说明 Docker 已成功启动。


3.5 验证代理是否生效

执行以下命令检查代理是否生效:

 docker info | grep -i proxy

如果输出:

 HTTP Proxy: http://192.168.xxx.xxx:7890
 HTTPS Proxy: http://192.168.xxx.xxx:7890

说明 Docker Daemon 代理已生效!


4. 部署与初始化

4.1 用 vim 创建 compose(含 mTLS 管理)

 # 打开文件(不存在则新建)
 sudo vim /opt/signserver-compose/compose.yml

在 vim 中按 i 进入插入模式,粘贴以下内容;粘贴完按 Esc,输入 :wq 保存退出:

 services:
   signserver:
     image: keyfactor/signserver-ce:latest
     container_name: signserver
     hostname: sign.example.local
     environment:
       - TLS_SETUP_ENABLED=true
       - DATABASE_JDBC_URL=jdbc:h2:/mnt/persistent/signserverdb;DB_CLOSE_DELAY=-1
       - DATABASE_USER=signserver
       - DATABASE_PASSWORD=signserver
     ports:
       - "80:8080"
       - "443:8443" # 建议仅启用 TLS 端口(PoC 亦同)
     volumes:
       - /opt/signserver-data:/mnt/persistent
       - /opt/signserver-secrets/ManagementCA.crt:/mnt/external/secrets/tls/cas/ManagementCA.crt:ro

必须已完成/opt/signserver-data//opt/signserver-secrets/ 目录及其中的 ManagementCA.crt 文件,所有者均为 10001:10001(见第 3 章)。

4.2 启动与验证

 cd /opt/signserver-compose/
 docker compose up -d
 
 # 查看容器
 docker ps -a | grep signserver
 
 # 快速检查日志(无输出则未发现 ERROR)
 docker logs -f signserver | grep ERROR || echo "No ERROR found"

访问验证

  • 管理界面(需导入管理员 P12):https://<主机IP或域名>/signserver

成功标志

  • 容器日志无 ERROR

  • 欢迎页可打开


5. 参考截图

5.1 SignServer 管理界面欢迎页

  • 打开:https://<主机IP或域名>/signserver

  • 需导入管理员 crt;欢迎页可正常显示。


5.2 点击 EJBCA AdminWeb 并下载 CA 证书(PEM/Base64)

  • 打开:https://<EJBCA主机或IP>/ejbca/adminweb

  • 顶部导航点击 CA Functions → 选择 CA certificate (PEM/Base64) → 下载。

图 2

6. 时间戳远程签名部署(Crypto Token + TimeStampSigner)

目标:基于 PKCS#12(P12) 证书私钥创建 Crypto Token,并绑定到 TimeStampSigner,完成 RFC‑3161 时间戳服务上线。

6.1 前置条件 & 约定(Docker Compose 映射版)

  • 目录映射约定docker-compose.yml 中将宿主机目录映射为容器内持久化目录:

    services:
      signserver:  # 你的服务名可能不同
        volumes:
          - /opt/signserver-data:/mnt/persistent:rw
    
  • P12 放置位置(宿主机):请将时间戳证书 *.p12 放到宿主机路径:/opt/signserver-data/,例如:

    • 宿主机:/opt/signserver-data/Test_Timestamp_EE.p12

    • 容器内(SignServer 看到的路径):/mnt/persistent/Test_Timestamp_EE.p12

  • 路径使用规范:后续在 SignServer 的 KEYSTOREPATH 一律使用 容器内路径/mnt/persistent/...)。

  • 密码:已知该 P12 的访问密码。

  • 证书来源建议(可选):建议搭配 Keyfactor EJBCA 证书服务器签发 TSA 证书(含 EKU timeStamping),也可以使用任意受信 CA 颁发的现成 P12,流程保持一致。

  • 快速自检(可复制执行)

    # 1) 确保宿主机目录存在并放入 P12
    sudo cp /path/to/Test_Timestamp_EE.p12 /opt/signserver-data/
    
    # 2) 验证容器内可见(将 <svc> 替换为 compose 服务名signserver)
    cd /opt/signserver-compose
    docker compose exec <svc> ls -l /mnt/persistent | grep Test_Timestamp_EE.p12
    
  • 示例参数沿用

    • Crypto Token 名称:CryptoTimestampG2

    • TimeStampSigner 名称:TimeStampSignerG2

    • 证书主题示例:CN=Test Timestamp EE,O=Test,C=CN

    • PSS 签名算法:SHA256withRSAandMGF1

6.2 用 OpenSSL 确认 DEFAULTKEY(P12 私钥别名 alias)

DEFAULTKEY 必须填写 P12 中私钥条目的 friendlyName(别名),不要想当然使用证书的 CN。

命令:

# 会提示输入 P12 密码;不加 -nokeys 才能看到“Shrouded Keybag”
cd /opt/signserver-data/
openssl pkcs12 -in ./Test_Timestamp_EE.p12 -info

在输出中找到:

  • Shrouded Keybag 段落下的:

    • friendlyName: <这里就是 DEFAULTKEY>以此为准
  • (通常)与下面叶子证书的 Certificate bagfriendlyName: 一致

例:若看到 friendlyName: Test Timestamp EE,则 DEFAULTKEY=Test Timestamp EE(大小写/空格都要完全一致)。

可选快速筛选:

openssl pkcs12 -in ./Test_Timestamp_EE.p12 -info \
| sed -n '/Shrouded Keybag/,/Certificate bag/p' | grep -m1 'friendlyName:'

6.3 新建 Crypto Token(KeystoreCryptoToken / P12)

图示:

操作路径Workers → Add… → From TEMPLATE → keystore-crypto.properties(不是 From Properties),粘贴以下模板(把尖括号占位改成你自己的):

# === Crypto token for Test TSA (P12) ===
WORKERGENID42.TYPE=CRYPTO_WORKER
WORKERGENID42.IMPLEMENTATION_CLASS=org.signserver.server.signers.CryptoWorker
WORKERGENID42.CRYPTOTOKEN_IMPLEMENTATION_CLASS=org.signserver.server.cryptotokens.KeystoreCryptoToken

# 供其他工作者引用的名字(自定义,避免与现有重复)
WORKERGENID42.NAME=CryptoTimestampG2

# 使用 P12 文件
WORKERGENID42.KEYSTORETYPE=PKCS12
# 注意:这里必须写 **容器内路径**(compose 映射 /opt/signserver-data -> /mnt/persistent)
WORKERGENID42.KEYSTOREPATH=/mnt/persistent/Test_Timestamp_EE.p12
# 设置后可“自动激活”;若不想明文保存,可留空,改用手动 Activate
#建议:留空并手动 ACTIVATE,或使用机密管理注入(示例:/mnt/external/secrets/...)。请勿将任何密钥材料、P12 密码提交到公共仓库或截图中。
WORKERGENID42.KEYSTOREPASSWORD=<你的P12密码>

# 关键:填“私钥 Keybag 的 friendlyName”(区分大小写与空格)
WORKERGENID42.DEFAULTKEY=Test Timestamp EE

# 启用
WORKERGENID42.DISABLED=FALSE

激活与自检

  1. 选中新建的 CryptoTimestampG2 → 点击 ACTIVATE(若写了 KEYSTOREPASSWORD 会自动激活)。

  2. 点击 TEST KEY…,应成功读取到 DEFAULTKEY 对应密钥。

常见报错:

  • No such aliasDEFAULTKEY 与 P12 中的别名不完全一致(注意空格/大小写)。

  • Token not active:未激活或密码错误。

6.4 新建 TimeStampSigner 并绑定到 Crypto Token

操作路径Workers → Add… → From TEMPLATE → timestamp.properties(不是 From Properties),粘贴以下模板(把尖括号占位改成你自己的):

# === New TimeStampSigner (G2) ===
WORKERGENID66.TYPE=PROCESSABLE
WORKERGENID66.IMPLEMENTATION_CLASS=org.signserver.module.tsa.TimeStampSigner
WORKERGENID66.NAME=TimeStampSignerG2

# 认证
#⚠️ 仅限实验环境
WORKERGENID66.AUTHTYPE=NOAUTH

# 绑定新的 P12 加密令牌
WORKERGENID66.CRYPTOTOKEN=CryptoTimestampG2
# alias/friendlyName(已用 openssl 确认)
WORKERGENID66.DEFAULTKEY=Test Timestamp EE

# 策略(与你当前 worker 保持一致,可按需调整)
WORKERGENID66.DEFAULTTSAPOLICYOID=1.2.3.4.1
WORKERGENID66.TSAPOLICYIDS=1.2.3.4.5.6,1.2.3.4.5.7
#(仅 PoC;生产建议按需设置 DEFAULTTSAPOLICYOID/TSAPOLICYIDS)
WORKERGENID66.ACCEPTANYPOLICY=true

# 哈希/算法
WORKERGENID66.ESSCERTIDALGORITHM=SHA256
WORKERGENID66.TSA_ALLOWED_HASHES=SHA1,SHA256,SHA384,SHA512
# 使用 RSASSA-PSS
WORKERGENID66.SIGNATUREALGORITHM=SHA256withRSAandMGF1

# TSA GeneralName(建议与证书主题一致)
WORKERGENID66.TSA=CN=Test Timestamp EE,O=Test,C=CN

# 其余与现有保持一致
WORKERGENID66.MAXSERIALNUMBERLENGTH=16
WORKERGENID66.INCLUDESIGNINGTIMEATTRIBUTE=false
WORKERGENID66.INCLUDESTATUSSTRING=false
WORKERGENID66.DISABLEKEYUSAGECOUNTER=true
WORKERGENID66.VERIFY_TOKEN_SIGNATURE=true
WORKERGENID66.DISABLED=FALSE

自检

  • 选中 TimeStampSignerG2TEST KEY… 应成功。

6.4.1 成功界面(示例)

CryptoTimestampG2TimeStampSignerG2 都显示 ACTIVE,且 TEST KEY… 通过,即表示配置完成。

6.5 客户端请求示例(RFC‑3161)

建议用 workerName,避免 ID 改动带来影响。

# 准备个示例数据(你也可以换成自己的文件)
echo -n 'hello-tsa' > payload.bin

# 生成 TSQ(DER 格式)
openssl ts -query -data payload.bin -sha256 -cert -out sig.tsq

# 已有 sig.tsq(由 cms 签名值或原文生成的时间戳请求)
curl -k -H "Content-Type: application/timestamp-query" \
  --data-binary "@sig.tsq" \
  "https://<host>/signserver/process?workerName=TimeStampSignerG2" \
  -o sig.tsr

查看/验证 TSR:

# 人类可读
openssl ts -reply -in sig.tsr -text

6.6 常见问题排查清单

  • 别名不匹配DEFAULTKEY 与 P12 私钥别名不同(检查空格、大小写、全角/半角)。

  • 未激活:忘记 ACTIVATE 或 KEYSTOREPASSWORD 填错。

  • 链不完整:P12 导出时未携带中间证书,重新导出时加 -certfile chain.pem

  • 路径问题KEYSTOREPATHSignServer 进程内部可见 的路径(容器内路径)。

  • 算法不一致:证书/密钥要求的算法与 SIGNATUREALGORITHM 不匹配(如 PSS vs PKCS#1 v1.5)。

  • 策略/哈希限制:请求的 Policy OID 或哈希算法不在允许列表内(检查 ACCEPTANYPOLICY/TSAPOLICYIDS/TSA_ALLOWED_HASHES)。

6.7 运行与维护建议

  • 优先使用 workerName 调用/process?workerName=TimeStampSignerG2

  • 分环境/多算法:需要多套证书/策略时,可复制多个 TimeStampSigner(每个绑定不同 Crypto Token)。

  • 密钥与配置备份:安全保存 P12、密码与导出的 Properties;生产上建议 HSM/P11。

  • 前置负载/容灾:多节点部署 + 共享数据库,前面挂反向代理做健康检查与负载均衡。

6.8 一页式检查表

  • openssl pkcs12 -info 确认 Shrouded Keybag → friendlyName

  • 新建 CryptoTimestampG2(P12 路径/密码/DEFAULTKEY)并 Activate

  • TEST KEY… 通过

  • 新建 TimeStampSignerG2 并绑定 CryptoTimestampG2

  • TEST KEY… 通过

  • curl 请求得到 sig.tsropenssl ts -reply -text 检视

7. CMS 远程签名

目标:配置 CMSSigner 实现远程 CMS/PKCS#7 签名(支持 DetachedAttached),并给出可直接复制的 curl 自测与 openssl 验证步骤。默认基于 P12 软证书,也可后续迁移 HSM/PKCS#11。

7.1 前置条件与目录映射

  • Compose 映射:/opt/signserver-data:/mnt/persistent(容器内路径以 /mnt/persistent 为准)。

  • CMS 签名用 P12 放到宿主机:/opt/signserver-data/Test_CMS_EE.p12(容器内即 /mnt/persistent/Test_CMS_EE.p12)。

  • 已知 P12 密码;已通过 6.2 的方法确认 friendlyNameDEFAULTKEY)。

  • 证书来源建议:推荐使用 Keyfactor EJBCA 为 CMS 用途签发(EKU: codeSigning 或按业务需求);当然也可直接使用任何受信 CA 的现成 P12。

可复用 6 章的 Crypto Token 吗?

  • 安全与审计上更推荐独立:为 TSA 与 CMS 分配不同证书/令牌(如 CryptoTimestampG2CryptoCMSG2)。

  • 若仅演示,可临时复用同一 P12,但生产不建议。

7.2 新建 Crypto Token(KeystoreCryptoToken / P12)

操作路径Workers → Add… → From TEMPLATE → keystore-crypto.properties(KeystoreCryptoToken 模板)

图示:

导入模板后,检查/覆盖以下关键项(保持容器路径规范):

# === Crypto token for Test CMS (P12) ===
WORKERGENID52.TYPE=CRYPTO_WORKER
WORKERGENID52.IMPLEMENTATION_CLASS=org.signserver.server.signers.CryptoWorker
WORKERGENID52.CRYPTOTOKEN_IMPLEMENTATION_CLASS=org.signserver.server.cryptotokens.KeystoreCryptoToken

WORKERGENID52.NAME=CryptoCMSG2
WORKERGENID52.KEYSTORETYPE=PKCS12
# 注意:容器内路径
WORKERGENID52.KEYSTOREPATH=/mnt/persistent/Test_CMS_EE.p12
# 不想明文保存可留空,改用手动 Activate
#建议:留空并手动 ACTIVATE,或使用机密管理注入(示例:/mnt/external/secrets/...)。请勿将任何密钥材料、P12 密码提交到公共仓库或截图中。
WORKERGENID52.KEYSTOREPASSWORD=<你的P12密码>
# P12 私钥 Keybag 的 friendlyName(区分大小写与空格)
WORKERGENID52.DEFAULTKEY=<你的DEFAULTKEY>

WORKERGENID52.DISABLED=FALSE

激活与自检:选中 CryptoCMSG2ACTIVATETEST KEY… 应成功。

7.3 新建 CMSSigner 并绑定 Crypto Token(定长配置)

操作路径Workers → Add… → From TEMPLATE → cmssigner.properties(CMSSigner 模板)

图示:

按“定长”要求的属性集(与示例界面一致,可直接复制)

# === Test_CMSSigner (Fixed profile) ===
WORKERGENID77.TYPE=PROCESSABLE
WORKERGENID77.IMPLEMENTATION_CLASS=org.signserver.module.cmssigner.CMSSigner
WORKERGENID77.NAME=Test_CMSSigner

# 鉴权策略(如在内网/前置网关受控场景):
#⚠️ 仅限实验环境
WORKERGENID77.AUTHTYPE=NOAUTH

# 绑定 Crypto Token(若你的 7.2 名称不同,请相应替换)
WORKERGENID77.CRYPTOTOKEN=CryptoCMSG2
WORKERGENID77.DEFAULTKEY=Test Code Signing EE

# 算法(示例为 RSA-PSS):
WORKERGENID77.SIGNATUREALGORITHM=SHA256withRSAandMGF1

# 允许的摘要算法集合(固定三种):
WORKERGENID77.ACCEPTED_HASH_DIGEST_ALGORITHMS=SHA-256,SHA-384,SHA-512

# 允许客户端指定摘要/是否分离签名(客户端可覆盖):
WORKERGENID77.ALLOW_CLIENTSIDEHASHING_OVERRIDE=TRUE
WORKERGENID77.ALLOW_DETACHEDSIGNATURE_OVERRIDE=TRUE

# 输出为分离签名(.p7s);如需附带内容请改为 FALSE:
WORKERGENID77.DETACHEDSIGNATURE=TRUE

# 其他与示例界面一致:
#默认携带证书链(可选)
#WORKERGENID77.INCLUDECERTIFICATELEVEL=Chain
WORKERGENID77.DER_RE_ENCODE=TRUE
WORKERGENID77.DISABLEKEYUSAGECOUNTER=TRUE
WORKERGENID77.DISABLED=FALSE

界面参考(定长属性示例):

自检:选中 Test_CMSSignerTEST KEY… 应成功(若失败,多半是 CRYPTOTOKEN/DEFAULTKEY 或证书算法不匹配)。

7.4 成功界面(示例)

成功界面(示例)

CryptoCMSG2Test_CMSSigner 均为 ACTIVE,且 TEST KEY… 通过。

7.5 客户端请求示例(curl:两种方式,写死示例)

小/中等文件用“非预哈希”(原文直传);大文件务必用“预哈希”。输出统一为 data.bin.cms

7.5.1 非预哈希(原文直传)

# 原文:data.bin
# 输出:data.bin.cms
#⚠️ 仅限实验环境
curl -k \
  -F workerName=Test_CMSSigner \
  -F file=@data.bin \
  --output data.bin.cms \
  "https://<host-or-ip>/signserver/process"

7.5.2 预哈希(大文件推荐)

# 第一步:生成“二进制”摘要(不是十六进制字符串)
openssl dgst -sha256 -binary data.bin > data.bin.sha256


# 第二步:以 multipart/form-data 方式提交:
# - file 上传的是上一步得到的 data.bin.sha256
# - 通过 REQUEST_METADATA.* 传入客户端预哈希的元数据
#⚠️ 仅限实验环境
curl -k \
-F workerName=Test_CMSSigner \
-F REQUEST_METADATA.USING_CLIENTSUPPLIED_HASH=true \
-F REQUEST_METADATA.CLIENTSIDE_HASHDIGESTALGORITHM=SHA-256 \
-F file=@data.bin.sha256 \
--output data.bin.cms \
"https://<host-or-ip>/signserver/process"

:warning: 大文件必须用预哈希,否则可能因上传超时/内存占用导致失败或非常慢。

7.6 使用 OpenSSL 本地验证(仅验证签名正确性)

只检查“签名 = 原文 + 私钥”是否匹配,不校验证书链/EKU/有效期。适用于快速自检或签名链尚未就绪的场景。

7.6.1 分离签名(Detached)

# 原文:data.bin
# 签名:data.bin.cms(DER)
#⚠️ 仅限实验环境
openssl cms -verify \
  -binary -inform DER \
  -in data.bin.cms \
  -content data.bin \
  -noverify \
  -out /dev/null && echo "OK"
  #⚠️ 仅自检:以上命令只验证签名完整性

7.7 常见问题排查

  • No such alias / bad aliasDEFAULTKEY 与 P12 中 friendlyName 不一致(参考 6.2)。

  • Token not active:未激活、密码错误或 P12 路径未按容器内路径填写。

  • Algorithm mismatch:证书/策略不允许所选算法(PSS vs PKCS#1 v1.5)。

  • Detached/Attached 不符:确认 DETACHEDSIGNATURE 设置;不同版本模板的覆盖策略可能不同。

  • 链不完整:签名可成功但验证失败;将 -CAfile 指向包含中间证书的 bundle 文件,或修正服务端证书链配置。

7.8 迁移到 HSM/PKCS#11(提要)

  • 将 7.2 中的 KEYSTORETYPE=PKCS12 改为 PKCS11,并按厂商文档配置:

    • P11LIBRARY=/usr/local/lib/your_hsm_pkcs11.so

    • P11SLOTLABEL / P11SLOT / P11TOKENLABEL

    • DEFAULTKEY=<HSM 内的 key label>

8. openUBMC × Keyfactor/EJBCA & SignServer:应用场景与验证

目的:给出 openUBMC 在两个开源平台组合下的落地方式与验证路径,不涉及实现细节

  • hpm_signer:需要结合项目做适配后再用于远程 CMS 签名。当前 hpm_signer 基于 SignServer 的适配已完成,PR 已提交至社区bingo仓库,如有需要可自行提前获取代码进行验证。

  • hpm_verify:可直接用于校验签名结果,现阶段用于联调。

8.1 两种场景

场景 A:企业 CA = Keyfactor EJBCA,签名中心 = SignServer

openUBMC  →  (适配后)hpm_signer  →  SignServer·CMSSigner(可选 TSA)
                                         ↓
                                   *.cms(+ *.tsr) → 分发/入库

场景 B:仅使用 SignServer(证书来自预置 P12 或 HSM)

openUBMC  →  (适配后)hpm_signer  →  SignServer·CMSSigner(可选 TSA)
                                         ↓
                                   *.cms(+ *.tsr) → 分发/入库

说明: 两种场景在 openUBMC 侧一致;差别仅在证书来源与签发流程。联调阶段建议先完成服务端(第 6–7 章)并用 hpm_verify 验证链路。
当前 hpm_signer 基于 SignServer 的适配已完成,PR 已提交至社区仓库,如有需要可自行提前获取代码进行验证。

8.2 现阶段验证(先用 hpm_verify)

直接用 hpm_verify 对第 7.5 生成的 *.cms 做校验,确认链路正确。

验证命令

# 分离签名:原文 + CMS(DER)
./hpm_verify -r rootca.pem -c uboot.bin -s uboot.bin.cms && echo "OK"

仅做完整性快速自检也可参考第 7.6openssl cms -verify -noverify 单条命令。

8.3 常见问题(概述)

  • 返回 HTML/XML:多为预哈希表单字段错误;按 7.5.2 的 REQUEST_METADATA 方式处理。

  • 链不完整:请在服务端开启 INCLUDECERTIFICATELEVEL=Chain 后重新签名。

  • 用途报错:若使用 OpenSSL 的全量验证,可能遇到 unsuitable certificate purpose;联调阶段建议仅做完整性检查(见 7.6),或在验证端放宽用途。