BouncyCastle 属性 messageDigest 验证失败
BouncyCastle attribute messageDigest fail verification
我对 signing/veryfing CADES 签名使用 BouncyCastle 1.59 有疑问。
我必须添加 messageDigest 属性,但是当我添加它时我的程序失败了。
这是我的代码:
Signature signature = Signature.getInstance("SHA256withRSA", new BouncyCastleProvider());
byte[] test_data = "test".getBytes();
MessageDigest dig = MessageDigest.getInstance("SHA256", new BouncyCastleProvider());
byte[] digest = dig.digest(test_data);
byte[] privateKeyContent = archivePrivateKey.getBytes();
BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(privateKeyContent)));
Security.addProvider(new BouncyCastleProvider());
PEMParser pp = new PEMParser(br);
PEMKeyPair pemKeyPair = (PEMKeyPair) pp.readObject();
KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
pp.close();
signature.initSign(kp.getPrivate());
signature.update(test_data);
byte[] certContent = archiveCertificateContent.getBytes();
PemFile pemFile = new PemFile(certContent);
X509CertificateHolder cert = new X509CertificateHolder(pemFile.getPemObject().getContent());
X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(cert);
// Build CMS
List certList = new ArrayList();
CMSTypedData data = new CMSProcessableByteArray(signature.sign());
certList.add(certificate);
Store certs = new JcaCertStore(certList);
byte[] certHash = dig.digest(certificate.getEncoded());
AlgorithmIdentifier algId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
ESSCertIDv2 essCert = new ESSCertIDv2(algId, certHash);
SigningCertificateV2 scv2 = new SigningCertificateV2(new ESSCertIDv2[] { essCert });
ASN1EncodableVector signedAttributes = new ASN1EncodableVector();
signedAttributes.add(new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, new DERSet(scv2)));
//signedAttributes.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(digest))));
signedAttributes.add(new Attribute(CMSAttributes.contentType, new DERSet(PKCSObjectIdentifiers.data)));
AttributeTable signedAttributesTable = new AttributeTable(signedAttributes);
signedAttributesTable.toASN1EncodableVector();
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
JcaContentSignerBuilder contentSigner = new JcaContentSignerBuilder("SHA256withRSA");
contentSigner.setProvider(BC_PROVIDER);
SignerInfoGeneratorBuilder signerInfoBuilder = new SignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC_PROVIDER).build());
signerInfoBuilder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(signedAttributesTable));
gen.addSignerInfoGenerator(signerInfoBuilder.build(contentSigner.build(kp.getPrivate()), cert));
gen.addCertificates(certs);
CMSSignedData signedData = gen.generate(data, true);
// Verify signature
Store store = signedData.getCertificates();
SignerInformationStore signers = signedData.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = store.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
X509Certificate certFromSignedData = new JcaX509CertificateConverter().setProvider(BC_PROVIDER).getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC_PROVIDER).build(certFromSignedData))) {
System.out.println("Signature verified");
} else {
System.out.println("Signature verification failed");
}
}
当我取消注释第 35 行时:signedAttributes.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(digest))));
然后我得到错误:
org.bouncycastle.cms.CMSSignerDigestMismatchException: 消息摘要属性值与计算值不匹配
我尝试了很多选项和配置,但问题仍然存在。任何帮助都会非常有用。
谢谢
我自己没有尝试过您的代码,但我认为问题出在您添加的消息摘要上,因为 MessageDigest 属性并不是您真正放入 SignedData 中的内容。我看到您首先使用 JCA 对数据进行签名。
Signature signature = Signature.getInstance("SHA256withRSA", new BouncyCastleProvider());
byte[] test_data = "test".getBytes();
signature.initSign(kp.getPrivate());
signature.update(test_data);
CMSTypedData data = new CMSProcessableByteArray(signature.sign());
...
CMSSignedData signedData = gen.generate(data, true);
您在这里放置签名(而不是数据,这很奇怪)以生成 CMSSignedData。因此,对于 contentSigner,您的 signature 是要签名的实际 data。但是,在 MessageDigest 属性中,您放置了实际数据的摘要。这很可能是导致问题的原因。
现在来谈谈更重要的问题。您为什么首先使用上述代码对数据进行签名?这对我来说似乎是错误的,可能不是你想要的。如果您的目的是为“测试”数据生成一个 CMS signedData,您应该直接在 signedDataGenerator 中使用它,这将处理签名部分。
如果您还没有完成,请完成 RFC。
希望对您有所帮助。
我对 signing/veryfing CADES 签名使用 BouncyCastle 1.59 有疑问。 我必须添加 messageDigest 属性,但是当我添加它时我的程序失败了。
这是我的代码:
Signature signature = Signature.getInstance("SHA256withRSA", new BouncyCastleProvider());
byte[] test_data = "test".getBytes();
MessageDigest dig = MessageDigest.getInstance("SHA256", new BouncyCastleProvider());
byte[] digest = dig.digest(test_data);
byte[] privateKeyContent = archivePrivateKey.getBytes();
BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(privateKeyContent)));
Security.addProvider(new BouncyCastleProvider());
PEMParser pp = new PEMParser(br);
PEMKeyPair pemKeyPair = (PEMKeyPair) pp.readObject();
KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
pp.close();
signature.initSign(kp.getPrivate());
signature.update(test_data);
byte[] certContent = archiveCertificateContent.getBytes();
PemFile pemFile = new PemFile(certContent);
X509CertificateHolder cert = new X509CertificateHolder(pemFile.getPemObject().getContent());
X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(cert);
// Build CMS
List certList = new ArrayList();
CMSTypedData data = new CMSProcessableByteArray(signature.sign());
certList.add(certificate);
Store certs = new JcaCertStore(certList);
byte[] certHash = dig.digest(certificate.getEncoded());
AlgorithmIdentifier algId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
ESSCertIDv2 essCert = new ESSCertIDv2(algId, certHash);
SigningCertificateV2 scv2 = new SigningCertificateV2(new ESSCertIDv2[] { essCert });
ASN1EncodableVector signedAttributes = new ASN1EncodableVector();
signedAttributes.add(new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, new DERSet(scv2)));
//signedAttributes.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(digest))));
signedAttributes.add(new Attribute(CMSAttributes.contentType, new DERSet(PKCSObjectIdentifiers.data)));
AttributeTable signedAttributesTable = new AttributeTable(signedAttributes);
signedAttributesTable.toASN1EncodableVector();
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
JcaContentSignerBuilder contentSigner = new JcaContentSignerBuilder("SHA256withRSA");
contentSigner.setProvider(BC_PROVIDER);
SignerInfoGeneratorBuilder signerInfoBuilder = new SignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC_PROVIDER).build());
signerInfoBuilder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(signedAttributesTable));
gen.addSignerInfoGenerator(signerInfoBuilder.build(contentSigner.build(kp.getPrivate()), cert));
gen.addCertificates(certs);
CMSSignedData signedData = gen.generate(data, true);
// Verify signature
Store store = signedData.getCertificates();
SignerInformationStore signers = signedData.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = store.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
X509Certificate certFromSignedData = new JcaX509CertificateConverter().setProvider(BC_PROVIDER).getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC_PROVIDER).build(certFromSignedData))) {
System.out.println("Signature verified");
} else {
System.out.println("Signature verification failed");
}
}
当我取消注释第 35 行时:signedAttributes.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(digest))));
然后我得到错误:
org.bouncycastle.cms.CMSSignerDigestMismatchException: 消息摘要属性值与计算值不匹配
我尝试了很多选项和配置,但问题仍然存在。任何帮助都会非常有用。 谢谢
我自己没有尝试过您的代码,但我认为问题出在您添加的消息摘要上,因为 MessageDigest 属性并不是您真正放入 SignedData 中的内容。我看到您首先使用 JCA 对数据进行签名。
Signature signature = Signature.getInstance("SHA256withRSA", new BouncyCastleProvider());
byte[] test_data = "test".getBytes();
signature.initSign(kp.getPrivate());
signature.update(test_data);
CMSTypedData data = new CMSProcessableByteArray(signature.sign());
...
CMSSignedData signedData = gen.generate(data, true);
您在这里放置签名(而不是数据,这很奇怪)以生成 CMSSignedData。因此,对于 contentSigner,您的 signature 是要签名的实际 data。但是,在 MessageDigest 属性中,您放置了实际数据的摘要。这很可能是导致问题的原因。
现在来谈谈更重要的问题。您为什么首先使用上述代码对数据进行签名?这对我来说似乎是错误的,可能不是你想要的。如果您的目的是为“测试”数据生成一个 CMS signedData,您应该直接在 signedDataGenerator 中使用它,这将处理签名部分。
如果您还没有完成,请完成 RFC。
希望对您有所帮助。