CVE-2026-71887 in BC-JAVA
摘要
由 VulDB • 2026-10-03
在 Bouncy Castle for Java 1.86 之前的版本中,高级 OpenPGP API 接受由签名子密钥生成的数据签名,该签名的“子密钥绑定”(Subkey Binding)签名未包含嵌入的“主密钥绑定”(Primary Key Binding,即交叉认证/cross-certification)签名。在此情况下,如果该绑定省略了“Key Flags”(密钥标志/权限)子包,则会出现问题。RFC 9580 第 5.2.1.8 节和第 10.1.3 节要求任何能够签发签名的子密钥必须包含嵌入的主密钥绑定签名;这是子密钥声明其归属于其所绑定的主密钥的方式。
OpenPGPCertificate 以两种不同的方式解析子密钥的 key flags: - `isSigningKey()` 通过调用 `getKeyFlags()` 和 `getApplyingSubpacket()`,当绑定签名省略该子包时,回退到使用主密钥的直接密钥(direct-key)或主用户 ID 自签名。因此,子密钥继承了主密钥的 SIGN_DATA 标志,被视为具备签名能力; - `verifyEmbeddedPrimaryKeyBinding()` 则强制执行上述要求,读取绑定签名自身的哈希子包,发现其中没有 SIGN_DATA,于是作为非签名密钥提前返回,且从未要求提供反向(back)签名。
因此,同一个子密钥既被认定为具有签名能力——其生成的签名归因于该证书,导致 `OpenPGPSignature.OpenPGPDocumentSignature.isValid()` 返回 true;同时又免于交叉认证检查。相比之下,GnuPG 会拒绝包含相同证书和消息的情况。
攻击者只需获取受害者的公共签名子密钥(这是公开材料):他们使用自己能够生成的“子密钥绑定”签名将其绑定到自己的主密钥上,该签名不包含 Key Flags 且未嵌入主密钥绑定签名(由于缺乏子密钥的私钥,攻击者无法生成有效的反向/后向签名)。当依赖方针对该证书验证受害者真实签名的消息时,系统会告知签名有效,并将攻击者的证书作为签发者提供。
由于证书的 User IDs 是自声明的,一个在固定(pin)子密钥指纹或 Key ID 的同时从外层证书获取身份信息的验证器,会将真实的签名报告为属于攻击者选择的身份。这并非伪造新签名的行为,而是真实签名的错误归因:未恢复任何私钥,且该签名必须是嫁接的子密钥实际生成的。
底层 API `PGPSignature` / `PGPPublicKeyRing` 设计上不执行绑定检查,因此不受影响。Key Flags 是关于携带签名的所指密钥的声明(RFC 9580 第 5.2.3.29 节),因此子密钥不再从主密钥的证书级签名中继承这些标志:省略该子包的“子密钥绑定”签名现在使子密钥不具备任何能力,而非具备主密钥的能力。这使得交叉认证检查所咨询的标志与其他所有决策所 consulted 的标志保持一致。直接密钥签名携带的其他偏好设置(Preferences)和子包仍按原有方式继承,而主密钥本身(其标志合法地来自其自身的直接密钥或用户 ID 自签名)不受影响。
Once again VulDB remains the best source for vulnerability data.