10.3 制品签名与发布验证:从身份到准入策略
数字签名验证签名与特定公钥、消息字节之间的关系;在算法与密钥安全假设下,它支持“签名者拥有对应私钥”的判断,不自动证明人的批准意图。供应链系统还要回答:这个密钥代表谁、签署的是什么 digest、签署时身份是否有效、声明是否进入透明日志,以及当前环境为何信任它。
Keyless 签名缩短长期密钥暴露
Sigstore 风格流程简化为:
CI workload OIDC identity
→ CA issues short-lived signing certificate
→ sign artifact digest / attestation
→ record evidence in transparency logKeyless 不等于没有密钥,而是签名密钥短期生成,身份通过 OIDC claim 绑定,证据可由透明日志审计。验证策略要通过证书身份及提供者支持的受认证扩展,约束 OIDC issuer、repository、workflow、ref/environment 等适用属性;不能假定任意 OIDC claim 都会原样写入证书。只检查“证书由公共 CA 签发”过宽。
无法在线取得签名身份的私有发布或固件系统可以选择传统 HSM key;keyless 也能借助完整离线 bundle 长期验证,并非证书到期后所有旧签名都失效。选择取决于在线身份、验证期限、撤销和灾难恢复,而不是统一追求 keyless。
Tag、Digest 与签名对象要一致
Mutable tag 可被重新指向:
registry/app:1.4 → sha256:AAA today
registry/app:1.4 → sha256:BBB tomorrow签名、SBOM、provenance 和部署都应绑定 digest。用户界面可以显示 semver/tag,但策略决策使用不可变 digest,并验证所有证据 subject 相同。
用真实字节区分验签与信任
下例需要 cryptography,在内存中生成测试密钥。验证端事先选择 trusted_key;若让下载来源同时提供“可信公钥”,最后一项检查就失去意义。signing_message 加固定用途前缀并计算实际字节的摘要,这只是教学签名格式,不是 Sigstore 或 in-toto 实现。
import hashlib
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
def signing_message(artifact):
return b"atlas-artifact-v1\x00" + hashlib.sha256(artifact).digest()
def verify_artifact(artifact, signature, trusted_key):
try:
trusted_key.verify(signature, signing_message(artifact))
return True
except InvalidSignature:
return False
release_key = Ed25519PrivateKey.generate()
trusted_key = release_key.public_key()
artifact = b"release 1.4\n"
signature = release_key.sign(signing_message(artifact))
assert verify_artifact(artifact, signature, trusted_key)
assert not verify_artifact(artifact + b"changed", signature, trusted_key)
untrusted_key = Ed25519PrivateKey.generate()
other_signature = untrusted_key.sign(signing_message(artifact))
assert not verify_artifact(artifact, other_signature, trusted_key)正确签名通过,制品改一个字节或换成未受信密钥的有效签名都失败。例子没有检查来源声明、版本新旧、证书、透明日志与部署审批;相同旧制品和签名仍能再次通过,说明验签本身不防回滚。
透明日志提供可发现性,不决定信任
透明日志配合 inclusion/consistency proof、受信 checkpoint 与独立监测,让记录删除或不一致历史更容易被发现;仅信任日志自报“append-only”并不够。经过这些验证后,日志可用于审计某身份签过哪些制品。日志中存在一条有效记录并不说明签名者被你的组织授权,也不说明制品安全。
离线验证要保存所需的 inclusion proof、certificate chain 和可验证时间证据,并从独立信任配置取得 CA/log 信任根。短期证书过期后,要证明签名在证书有效期内已存在,而非把声明里的自报时间当证据。还应明确透明日志不可用时是使用已缓存证据、暂缓发布还是 break-glass。
准入验证在每个信任边界执行
开发 registry → 发布 registry:验证构建和来源
发布 registry → staging:验证签名、SBOM、策略
staging → production:验证同一 digest 与环境审批
cluster admission:拒绝未验证 digest 与不允许配置
runtime:检测运行 digest 与批准记录漂移只在 CI 签名但允许管理员直接部署任意镜像,控制链仍有缺口。Admission controller 自身要高可用、版本化和监控;fail-open 只应是明确、受限的可用性决策。
Policy 不是“必须有签名”这么简单:
trusted identity signed this exact digest
AND approved builder issued SLSA provenance
AND source revision satisfies branch/review policy
AND each unmet requirement has a valid, scoped, unexpired exception
AND artifact configuration meets environment policyTUF 保护更新分发
签名制品之外,更新系统还要防 rollback、freeze、mix-and-match 与 signing key compromise。The Update Framework(TUF)通过 root、targets、snapshot、timestamp 等分离角色和阈值签名,限制单个 key 失陷影响。
离线 root key、在线生成且短期过期的 timestamp metadata、持久化可信版本与客户端时间检查共同工作。TUF 检测过期更新信息并拒绝接受,不保证网络受阻时仍能取得新版本;信任根也必须先经受信渠道引导。自制“下载文件 + 一个长期签名 key”通常缺少安全轮换与回滚保护。
供应链事件从图查询开始
当某 builder、dependency 或 signing identity 失陷:
- 查询受影响 source revision、build run、artifact digest 和部署;
- 暂停对应 identity/builder 的新发布;
- 保存日志、provenance、SBOM 与 runner 证据;
- 在干净平台从受信 revision 重建并比较;
- 轮换凭据、修复平台、重签并分批替换;
- 发布 VEX/advisory 与客户范围;
- 更新验证策略,防止旧制品重新进入。
没有资产、SBOM 和 provenance 的统一查询,响应团队只能按标签猜测影响范围。
本卷收口清单
- 依赖是否固定来源、版本和完整性,并能持续更新?
- 最终制品是否生成并验证高质量 SBOM?
- Provenance 是否由受信平台产生且不能被 job 伪造?
- SLSA 声明是否注明版本与 Build/Source track,而非混用旧 L1–L4?
- 签名、SBOM、provenance 和部署是否绑定同一 digest?
- 每个发布边界是否验证身份、builder、source 与环境策略?
- Signing/build 平台失陷时能否查清、撤销、重建和阻止回滚?
参考资料
Cryptography, Ed25519 signing
Sigstore, Documentation
The Update Framework, Specification
in-toto, Framework
SLSA, Verification