Multiple Custom Digital Signature

Product: PDFTron NodeJS

Product Version: 9.4.0

How to Add Multiple Custom Signatures to a PDF.

I’m using PDFTron to apply multiple custom signatures to PDF files.

As an input, I have:
1- a PDF document that contains 3 signature fields.
2- an array of 3 signers, each signer will sign one field.
each signer is an object that contains the following:

  • fieldName: the name of the signature field that this signer is going to sign
  • id: the id of the signer, I’m using this for internal purposes (irrelevant to my case here)
  • signatureImage: the image buffer that will be added to the field as appearance.
  • reason
  • location
  • contactInfo
    the last 3 are used to fill in the signature Dict for each field.

The method I use is as follows:
1- first, I open the PDF doc:
let doc = await PDFNet.PDFDoc.createFromFilePath(filePath);
2- then iterate over the signers, and add the signature appearance to each field, and then create the signature Dict and fill it:
`
for (let signer of signers) {
let sigField = await PDFNet.SignatureWidget.createFromObj(
await (await doc.getField(signer.fieldName)).getSDFObj()
);
await sigField.createSignatureAppearance(
await PDFNet.Image.createFromMemory2(doc, signer. signatureImage)
);
let apprField = await (await PDFNet.SignatureWidget.createFromObj(
await (await doc.getField(signer.fieldName)).getSDFObj()
)).getDigitalSignatureField();
await apprField.createSigDictForCustomSigning(
‘Adobe.PPKLiteSignature’,
PDFNet.DigitalSignatureField.SubFilterType.e_adbe_pkcs7_detached,
9000
);
await apprField.setReason(signer.reason);
await apprField.setLocation(signer.location);
await apprField.setContactInfo(signer.contactInfo);

    let sigTime = new PDFNet.Date();
    await sigTime.setCurrentTime();
    await apprField.setSigDictTimeOfSigning(sigTime);

    await doc.save(filePath, PDFNet.SDFDoc.SaveOptions.e_incremental);

}
3- then I iterate over the signers array again, and calculate the digest for each field, and then send it to a remote service, which returns the signature value and the signer's certificate:
for (let signer of signers) {
let apprField = await (await PDFNet.SignatureWidget.createFromObj(
await (await doc.getField(signer.fieldName)).getSDFObj()
)).getDigitalSignatureField();
digest = await apprField.calculateDigest(PDFNet.DigestAlgorithm.Type.e_SHA256);
cmsAttrs = await PDFNet.DigitalSignatureField.generateCMSSignedAttributes(digest);
cmsDigest = await PDFNet.DigestAlgorithm.calculateDigest(
PDFNet.DigestAlgorithm.Type.e_SHA256,
cmsAttrs
);

    let {signValue, signerCert} = await remoteService.sign(signer.id, cmsDigest);

    let cert = await PDFNet.X509Certificate.createFromBuffer(signerCert);

    let cmsData = await PDFNet.DigitalSignatureField.generateCMSSignature(
        cert,
        chainCerts, // has been predefined
        SHA256_OID, // has been predefined
        RSA_PKCS1_OID, // has been predefined
        signValue,
        cmsAttrs
    );

    await doc.saveCustomSignature(cmsData, apprField, filePath);

}
`

So far, it works as expected, and when I view the signed PDF document in Adobe Acrobat, it shows that the signature are validated.

But I’m trying to change the workflow such that the requests are sent in an un-orderly fashion. Meaning, that after I generate cmsDigest, I store it in an array along with the signer’s ID, and when I get the result of all the remoteService.sign calls (using promise.all), I resume the rest of the work (creating certificate instance, and generating cmsData) afterwards.

The problem is that when I view the file in Adobe Acrobat afterwards, only the first signature is validated.

I assume that’s because when you call apprField.calculateDigest, it includes the entire document including other signature fields, which will be filled with their respective signature values, hence the other signature fields are invalidated.

But if my assumption is correct, then how come in the first method I use (the orderly one), the first and second signature fields are not invalidated when the third field’s signature value is added to it?

And if my assumption is wrong, then there has to be a way to tell apprField.calculateDigest to exclude other signature fields’ values from the calculation.

In any case, I’d appreciate if anyone could help me with achieving my goal here.

Note: The remote service that I use, notifies the signer on their phone through their mobile app, and asks them to sign the document. So this step could take a while (up to 5 minutes).

Hi Husain,

Generally, when you want to sign multiple signatures, they need to be done sequentially while separated by an incremental save.

So you would sign the first signature and before you do work and start generating or handling the next, you would need to save the entire document using the incremental flag first.

Please try the sequential method using the incremental flag and if you still having issues, send over the files and complete code you are using so we can take a deeper look and do further testing on our end.