CMSSignedData分离签名可通过Bouncy Castle验证但java.security.Signature验证失败
问题原因与解决方案
这问题我之前也碰到过!核心原因是你混淆了两种完全不同的签名格式:
CMSSignedDataGenerator生成的是PKCS#7/CMS格式的签名数据:这是一个包含签名值、签名证书、摘要算法信息甚至完整证书链的封装容器,不是单纯的签名字节。- Java自带的
Signature类验证的是原始PKCS#1签名值:也就是直接用私钥对消息摘要加密后的原始字节数组,没有任何额外封装。
这就是为什么BC自己的验证能过(它能解析CMS容器),但Signature类验证失败的原因——你把整个CMS容器传给它当签名值,当然不对。
两种解决思路
思路1:从CMS签名中提取原始PKCS#1签名值,再用Signature验证
如果你一定要用Java的Signature类验证,可以先解析CMS数据,提取出里面的原始签名值,再进行验证。修改你的shouldVerifyBySignature方法如下:
private static void shouldVerifyBySignature(byte[] signatureBytes) throws Exception { // 先解析CMS数据,提取原始签名值 CMSSignedData cms = new CMSSignedData(new CMSProcessableByteArray(DATA_TO_BE_SIGNED), signatureBytes); SignerInformationStore signers = cms.getSignerInfos(); SignerInformation signerInfo = (SignerInformation) signers.getSigners().iterator().next(); byte[] rawSignature = signerInfo.getSignature(); // 这才是PKCS#1格式的原始签名值 // 再用Signature类验证 Signature signatureVerifier = Signature.getInstance(SHA_1_WITH_RSA, PROVIDER_NAME); signatureVerifier.initVerify(signer.getPublicKey()); signatureVerifier.update(DATA_TO_BE_SIGNED); boolean verified = signatureVerifier.verify(rawSignature); System.out.println(verified ? "VERIFIED BY SIGNATURE CLASS" : "! Not verified by Signature class !"); }
思路2:推荐继续使用BouncyCastle的验证方式
CMS签名的设计初衷就是为了携带完整的签名上下文(比如证书链、签名算法、时间戳等),用BouncyCastle的SignerInformation.verify()方法验证是最适配的,它会自动处理证书链验证、摘要匹配等逻辑,比手动用Signature类更可靠。
额外说明
- 你提到的“验证流程期望PKCS1结构,生成器输出PKCS7结构”完全正确,这就是问题的核心。
- 流程中没有异常抛出是因为
Signature.verify()只是检查签名值是否匹配,不会因为传入的字节格式不对而抛出异常,只会返回false。
内容的提问来源于stack exchange,提问作者bobo
相关产品推荐
相关产品推荐

