PAYATHON 2026

Maven artifact 签名用途适合写入 GnuPG user ID 的 comment 吗?

技术老齐

可以在 GnuPG user ID 的 comment 中填写 software,说明密钥用途,但这个描述不会限制密钥的使用权限。对 Acme 来说,应优先做好私钥隔离:公司核心主私钥由专人保管,开发团队或 CI 只获得软件发布所需的签名私钥。

“Maven 工具不能使用 subkey 签名”也不能一概而论。Maven GPG Plugin 的 GPG 后端可以通过 GnuPG 选择签名子密钥,因此无须向开发团队交出核心主私钥。

comment 能表达什么

下面的 user ID 可以表示“Acme 的软件签名密钥”:

Acme (software) <id@acme.example.com>

在 OpenPGP 格式中,user ID 是一段 UTF-8 文本,姓名 (comment) <邮箱> 是常见写法。协议没有限定 comment 的内容,也没有要求必须留空。参见 RFC 9580 的 User ID 定义。

建议留空,通常是为了避免加入含糊、容易过时或无助于识别身份的信息。如果 software 能帮助接收方区分公司管理的不同密钥,保留它就有用。

这个标注不会阻止 GnuPG 用该密钥签署财务报表。软件 artifact 和财务报表都可以作为数据签名的输入,OpenPGP 的签名用途标志也没有细分这两类业务。验签方需要根据预先认可的密钥指纹和业务规则,判断签名是否获得授权。参见 RFC 9580 的 Key Flags 定义。

一家公司可以拥有多把主密钥

OpenPGP 没有要求 Acme 只能拥有一把 primary key。是否共用一套主密钥,取决于管理责任和信任范围。

软件发布可以采用以下两种方式:

  • 在公司主密钥下创建专用签名子密钥,只向发布环境提供该子私钥。
  • 为软件发布建立独立的主密钥和签名子密钥,与财务用途分别管理。

如果软件发布与财务签署由不同团队负责,使用独立密钥通常更容易明确各自的授权、轮换和撤销流程。独立的软件发布主密钥也可以离线保管,只让 CI 使用其签名子密钥。

邮箱不决定私钥是否隔离。同一邮箱可以出现在不同密钥的 user ID 中,换成 software@acme.example.com 也不会自动增加安全性。隔离取决于是否使用不同的私钥材料,以及谁能访问这些私钥。

以下两种写法都可以:

Acme (software) <id@acme.example.com>
Acme Software Release <software@acme.example.com>

如果已有软件发布专用邮箱,第二种写法更方便接收方联系负责团队;如果统一使用公司身份邮箱,第一种也没有技术问题。

Maven 如何指定签名子密钥

Maven GPG Plugin 会将 gpg.keyname 参数作为 --local-user 传给 GnuPG。在完整指纹后附加 !,可以强制 GnuPG 使用指定的主密钥或子密钥,因此可以用这个参数选择签名子密钥。参见 Maven 参数文档和 GnuPG 密钥选择说明。

1. 找出签名子密钥的完整指纹

gpg --list-secret-keys --keyid-format long --with-subkey-fingerprint

选择具备 [S] 签名能力的子密钥,记录其完整指纹。不要误选仅具备 [E] 加密能力的子密钥。

2. 先确认 GnuPG 能直接签名

将以下命令中的 SIGNING_SUBKEY_FINGERPRINT 替换为实际的完整子密钥指纹:

gpg --local-user 'SIGNING_SUBKEY_FINGERPRINT!' \
    --armor --detach-sign example.jar

命令会生成分离签名 example.jar.asc。在 Bash、Zsh 等 shell 中,单引号可以避免 shell 对 ! 作特殊处理。

验证签名:

gpg --verify example.jar.asc example.jar

核对验签结果中的签名密钥指纹,确认使用的是预期子密钥。

3. 让 Maven 使用同一子密钥

以下以官方文档列出的 Maven GPG Plugin 3.2.8 和 gpg 后端为例:

mvn org.apache.maven.plugins:maven-gpg-plugin:3.2.8:sign \
    -Dgpg.signer=gpg \
    '-Dgpg.keyname=SIGNING_SUBKEY_FINGERPRINT!'

运行该命令前,项目需要已经生成待签名的 artifacts。如果已有发布流程,也可以在签名执行阶段传入相同属性。

gpg.keyname 是 GPG 后端的参数。使用 bc 后端时,需要按该后端的参数配置,不能直接套用。后端及参数适用范围见 Maven GPG Plugin 文档。

如果 GnuPG 直接签名成功,Maven 却失败,应检查插件版本、签名后端、GnuPG 可执行文件、密钥目录和口令交互配置。具体工具链可能存在兼容问题,不能据此认定 Maven 整体不支持子密钥。

私钥隔离时要检查什么

指定子密钥只决定本次签名使用哪把密钥,不会移除构建环境中已有的其他私钥。

准备发布环境时,需要:

  1. 离线备份主私钥,并准备撤销材料。
  2. 只向发布环境提供所需签名子私钥及相关公钥信息,排除核心主私钥和其他业务的子私钥。
  3. 在发布环境中重新检查私钥列表,核对实际签名使用的指纹。

GnuPG 的 --export-secret-subkeys 可以导出不包含可用主私钥的子密钥材料。导出后仍需核对范围:“不含主私钥”不代表“只含软件发布需要的那一把子私钥”。相关行为见 GnuPG 手册。

签名子密钥泄露后,攻击者仍可用它签署其他数据。因此,财务系统应只接受财务业务明确授权的密钥,不能仅凭 user ID 中出现 Acme 就认可签名。

公钥列表与命名的注意事项

公钥服务器上的名称、邮箱和 comment 都不能代替指纹核验。公司应通过可信渠道公布软件发布公钥的完整主密钥指纹,并说明认可哪些签名子密钥。验证子密钥签名时,还需要其所属主公钥及有效的绑定签名。

user ID 中的 comment 与 gpg --comment 是两回事。后者控制 ASCII armor 等输出中的注释,不会修改 user ID,相关注释头也不受签名保护。参见 GnuPG 的 --comment 说明。

Acme 可以保留 software 作为用途标签,无须为了让公钥列表更整齐而共享核心主私钥。先确定软件签名的授权范围和私钥保管方式,再选择容易识别的名称与邮箱。

备注:内容仅供参考。