I need to compute some data signature, using un-encapsulated pkcs7 with sha256 and RSA. No problem with raw content, using :
public byte[] signRawContent(final byte[] content)
throws CMSException, IOException, OperatorCreationException, CertificateEncodingException {
// Create generator of pkcs7-signature message
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build(privateKey);
generator.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(signer, certificate));
generator.addCertificate(new X509CertificateHolder(certificate.getEncoded()));
CMSTypedData cmsTypedData = new CMSProcessableByteArray(content);
CMSSignedData cmsSignedData = generator.generate(cmsTypedData, false);
return cmsSignedData.getEncoded();
}
But I have another user cases, where I don't have the raw content, only its hash (sha256) Bouncycastle doesn't support "NONEwithRSA" or "RSA" for pkcs7 signature, so I tried to use a custom ContentSigner, without obtaining the same signature that with the raw content version.
public byte[] signHash(final byte[] sha256) throws IOException,
OperatorCreationException, CertificateEncodingException, CMSException {
// Create generator of pkcs7-signature message
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
// custom content signer to bypass hash
ContentSigner signer = new ContentSigner() {
@Override public AlgorithmIdentifier getAlgorithmIdentifier() {
return new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WithRSA");
}
@Override public OutputStream getOutputStream() {
return new ByteArrayOutputStream();
}
@Override public byte[] getSignature() {
try {
Signature signer = Signature.getInstance("NONEwithRSA");
signer.initSign(privateKey);
signer.update(sha256);
return signer.sign();
} catch (Exception e){
throw new RuntimeOperatorException("exception obtaining signature: " + e.getMessage(), e);
}
}
};
generator.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(signer, certificate));
generator.addCertificate(new X509CertificateHolder(certificate.getEncoded()));
CMSTypedData cmsTypedData = new CMSProcessableByteArray(sha256);
CMSSignedData cmsSignedData = generator.generate(cmsTypedData, false);
return cmsSignedData.getEncoded();
}
I even tried to rebuild the content digest, no luck
// build digest
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(sha256);
byte[] outputDigest = messageDigest.digest();
AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);
DigestInfo di = new DigestInfo(sha256Aid, outputDigest);
//sign SHA256 with RSA
Signature rsaSignature = Signature.getInstance("RSA");
rsaSignature.initSign(privateKey);
byte[] encodedDigestInfo = di.toASN1Primitive().getEncoded();
rsaSignature.update(encodedDigestInfo);
return rsaSignature.sign();
So is there a way to get a pkcs7 form a sha256? Thanks
Found a working solution:
private static final String SIGNATURE_ALGO = "SHA256WithRSA";
/**
* Get the pkcs7-signature from a document hash (sha256Hex)
*
* @param contentSha256Hex
* the original document content hash (sha256Hex) to be signed
* @return the pkcs7 signature
*
* note: see TestSha1WithRsaAndAttributeTable() in bouncycastle/test/src/cms/test/SignedDataTest.cs
* */
public byte[] signSha256Hex(final String contentSha256Hex)
throws CertificateEncodingException, IOException, OperatorCreationException, CMSException, DecoderException {
byte[] hash = Hex.decodeHex(contentSha256Hex);
/*
* The trick is to manually set digest attribute with hash value,
* then generate signature without content.
*/
// CMS attributes
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(hash)))); // set digest (sha256)
return signCms_Sha256WithRsa(
new CMSAbsentContent(),
new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
}
private byte[] signCms_Sha256WithRsa(CMSTypedData content, CMSAttributeTableGenerator signedAttributes)
throws CMSException, IOException, CertificateEncodingException, OperatorCreationException {
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
// content signer
ContentSigner signer = new JcaContentSignerBuilder(SIGNATURE_ALGO).setProvider("BC").build(privateKey);
generator.addSignerInfoGenerator(
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.setSignedAttributeGenerator(signedAttributes)
.build(signer, certificate));
// add certificate
generator.addCertificate(new X509CertificateHolder(certificate.getEncoded()));
// sign
CMSSignedData cmsSignedData = generator.generate(content, false);
return cmsSignedData.getEncoded();
}