I'm implementing the j4sign code example of ExternalSignatureCMSSignedDataGenerator class documentation to generate CMSSignedData then I'm trying to validate the resulting object with CMSVerifier and basicVerify method but it is returning always false.
I',m using BC 1.54 and Java 8, here is the code:
ExternalSignatureCMSSignedDataGenerator gen = new ExternalSignatureCMSSignedDataGenerator();
ExternalSignatureSignerInfoGenerator signerGenerator = new ExternalSignatureSignerInfoGenerator(
CMSSignedDataGenerator.DIGEST_SHA1,
CMSSignedDataGenerator.ENCRYPTION_RSA);
//get X509 signer Certificate
FileInputStream fis = new FileInputStream("C:/myCertificate.cer");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf
.generateCertificates(fis).iterator().next();
//add certificate for buildSigningCertificateV2Attribute method used in getBytesToSign
signerGenerator.setCertificate(cert);
try {
// Obtain bytes to sign;
// note that this implementation includes a timestamp
// as an authenticated attribute, then bytesToSign is every time
// different,
// even if signing the same data.
// The timestamp should be notified and accepted by the signer along
// data to sign
// BEFORE he applies encryption with his private key.
// The timestamp is used during verification to check that signature
// time is
// in signing certificate validity time range.
//bytes of file to be signed in base64
String originalFile = "aG9sYQ0KYXNkYXMNCg0KYWZzDQo=";
CMSProcessable msg = new CMSProcessableByteArray(
DatatypeConverter.parseBase64Binary(originalFile));
byte[] bytesToSign = signerGenerator.getBytesToSign(
PKCSObjectIdentifiers.data, msg, "BC");
// Digest generation. Digest algorithm must match the one passed to
// ExternalSignatureSignerInfoGenerator
// constructor above (SHA1, in this case).
MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(bytesToSign);
byte[] digest = md.digest();
//sign digest
Signature sig = Signature.getInstance("SHA1withRSA");
//add signer private key
sig.initSign(myPrivateKey);
sig.update(digest);
byte[] signedBytes = sig.sign(); // will contain encripted
// digest
byte[] certBytes = cert.getEncoded(); // will contain DER encoded
// certificate
// Digest encryption and signer certificate retrieval (using a
// PKCS11 token, for example)
// Encryption algorithm must match the one passed to
// ExternalSignatureSignerInfoGenerator
// constructor above (RSA, in this case).
if ((certBytes != null) && (signedBytes != null)) {
// build java Certificate object.
// java.security.cert.CertificateFactory cf =
// java.security.cert.CertificateFactory.getInstance("X.509");
// java.io.ByteArrayInputStream bais = new
// java.io.ByteArrayInputStream(certBytes);
// java.security.cert.X509Certificate javaCert =
// (java.security.cert.X509Certificate)
// cf.generateCertificate(bais);
// pass encrypted digest and certificate to the SignerInfo
// generator
signerGenerator.setCertificate(cert);
signerGenerator.setSignedBytes(signedBytes);
// pass the signer info generator to the cms generator
gen.addSignerInf(signerGenerator);
// generating a cert store with signer certificate into.
// The store could contain also the root certificate and CRLS
ArrayList certList = new ArrayList();
certList.add(cert);
CertStore store = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(certList), "BC");
// pass cert store to the cms generator
gen.addCertificatesAndCRLs(store);
// Finally, generate CMS message.
CMSSignedData s = gen.generate(msg, true);
//verify data
CMSVerifier verifier = new CMSVerifier(s);
verifier.setDebug(true);
verifier.basicVerify();
//prints always false :(
System.out.println("valid?: " +verifier.isIntegrityChecked());
}
} catch (Exception ex) {
ex.printStackTrace();
}
What I'm doing wrong?
Thanks in advance.
Resolved. After reviewing this test class, I realized that the SHA1 digest method applied to bytesToSign wasn't necessary in my example. I just remove these 3 lines:
MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(bytesToSign);
byte[] digest = md.digest();
And sign bytesToSign directly:
sig.update(bytesToSign);