Reputation: 88
Current state
I am working on web-based application which is used to manage PDF forms and allows users to fill in the form, process approval and sign the PDF.
Application uses Adobe LiveCycle to calculate document digest and runs external application (call signer application) for signing the hash. Then the hash is sent back to LiveCycle where it is embedded to PDF document.
Signer application receives precalculated message digest and generates PKCS#7 signature. Windows certificate store is used to find the signing certificate and private key. We have to support usage of USB eTokens where the private key is stored.
Currently we have fully functional signer application written in Java. It uses BouncyCastle library and SunMSCAPI cryptographic provider to access Windows certificate store. Following is the code snippet responsible for signing.
protected byte[] signCMS(byte[] hash, PrivateKey key, X509Certificate[] cert) throws CertStoreException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, CMSException, OperatorCreationException, CertificateException {
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
signedAttrs.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(hash))));
ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("SunMSCAPI").build(key);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(new BouncyCastleProvider()).build()).setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(signedAttrs))).build(sha256Signer, cert[0]));
gen.addCertificates(generatedCertStore(cert));
return gen.generate(new CMSAbsentContent()).toASN1Structure().getEncoded();
}
Above code uses SunMSCAPI cryptographic provider and fully supports usage of USB eTokens without need of exporting the private key.
Needed state
Customer does not agree with usage of Java on the client side. I'm working on C# variant of the signer application.
Problems
I don't know how to generate PKCS#7 signature from externally calculated message digest. I always looks like the message digest is hashed twice. Acrobat Reader then reports the document was modiffied after signature.
I tried using BouncyCastle for .NET and was able to process working signature when using BouncyCastle library for .NET, but it requires the private key to be exported from the Windows certificate store. This solution does not support usage of USB eTokens.
Current non-working C# variant
public static byte[] Sign(byte[] hash, X509Certificate2 cert2)
{
List<byte> encodedMessageDigestList = new List<byte>(hash.Length + 2);
encodedMessageDigestList.Add(4);
encodedMessageDigestList.Add(checked((byte)(hash.Length)));
encodedMessageDigestList.AddRange(hash);
byte[] encodedMessageDigest = encodedMessageDigestList.ToArray();
Pkcs9MessageDigest md = new Pkcs9MessageDigest();
Pkcs9AttributeObject pAttribute = new Pkcs9AttributeObject("1.2.840.113549.1.9.4", encodedMessageDigest);
md.CopyFrom(pAttribute);
SignedCms cms = new(new ContentInfo(md.Oid, md.RawData));
CmsSigner cmsSigner = new();
cmsSigner.Certificate = cert2;
cmsSigner.IncludeOption = X509IncludeOption.WholeChain;
cms.ComputeSignature(cmsSigner, false);
return cms.Encode();
}
Upvotes: 3
Views: 929
Reputation: 1638
We can provide a precomputed Pkcs9MessageDigest (with the digest hash already computed) into the SignedAttribues collection of the CmsSigner. This will override the default behavior of automatically generating one.
Upvotes: 0