Add timestamp token from external service

Product:
Apryse SDK
Product Version:
Latest

Please describe your issue and provide steps to reproduce it:
Hello
we are trying to add a timestamp to a signature.
Both the signature and the timestamp come from an external API.
For the signature it works well with a CMSSignature

But I don’t see how I can add a timestamp token in the document / signature.
From our API we receive :
Base64 encoded DER representation of timestamp token according to RFC3161. It includes the TSA signing certificate in SignedData.CertificatesSet.

The documentation only shows how to call a TSA directly but this is not our use case.
Is there any way to do that with the SDK?

Current code to sign :

const page1 = await doc.getPage(1);
  const digsig_field = await doc.createDigitalSignatureField("test");
  const widgetAnnot = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(143, 287, 219, 306), digsig_field);
  await page1.annotPushBack(widgetAnnot);
  await digsig_field.createSigDictForCustomSigning('Adobe.PPKLite', PDFNet.DigitalSignatureField.SubFilterType.e_ETSI_CAdES_detached, 7500);
  let sigTime = new PDFNet.Date();
  await sigTime.setCurrentTime();
  digsig_field.setSigDictTimeOfSigning(sigTime);
  await doc.save('static/test5.pdf', PDFNet.SDFDoc.SaveOptions.e_incremental);

  const pdfDigest = await digsig_field.calculateDigest(PDFNet.DigestAlgorithm.Type.e_SHA256);
  const signedAttrs = await PDFNet.DigitalSignatureField.generateCMSSignedAttributes(pdfDigest);
  const signedAttrs_digest = await PDFNet.DigestAlgorithm.calculateDigest(PDFNet.DigestAlgorithm.Type.e_SHA256, signedAttrs);

  const result = await performDigitalSigning(toHexString(signedAttrs_digest));  
  let sig;
  let certificate;
  let timestampToken;
  if(!result ) {
    res.send('error');
    return;
  } else {
    sig = result.signature;
    certificate = result.certificate;
    timestampToken = result.timestampToken;
  }
  const signer_cert = await PDFNet.X509Certificate.createFromBuffer(pemToBuffer(certificate));

  var chain_certs = [];
  chain_certs.push(signer_cert);
  const digest_algorithm_oid = await PDFNet.ObjectIdentifier.createFromDigestAlgorithm(PDFNet.DigestAlgorithm.Type.e_SHA256);
  const signature_algorithm_oid = await PDFNet.ObjectIdentifier.createFromIntArray([1,2,840,113549,1,1]); //RSA encryption PKCS1
  const cms_signature = await PDFNet.DigitalSignatureField.generateCMSSignature(signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid, hexToUint8Array(sig), signedAttrs)
  await doc.saveCustomSignature(cms_signature,digsig_field,'static/test5.pdf');

Cheers

Hi MichaelL,

Can you please confirm if you are using WebViewer? If so, which version (I saw Product Version: latest, just need to confirm).

You might want to check the following forum posts:

and

Hello Bojan

As I mentioned in my post, we are using Apryse SDK, so it’s server side and not with webviewer. We use the latest SDK version with node.js

I had seen the two posts you mentionned but they do not answer my problem (or I don’t understand how it works).

  • GenerateContentsWithEmbeddedTimestamp and using TimestampingConfiguration assumes that we need to contact a TSA. But as I said, we already have the timestamp token so we do not need to contact the TSA

Please see this forum post which covers custom signing (HSM) using NodeJS.

Hello Ryan

As I mentioned in my first post, the signing is not the problem.
We have achieved it, and it works (the example you mentionned helped me). The whole code is in my first post…

I am trying now to add a timestamp to the signature, with the requirements I mentionned earlier.

@MichaelL sorry for the confusion, and for the delay in a response. I am still reviewing with my team on how you can accomplish this task, and will update you once I know more.

I got confirmation from the team that at this time there is no specific API to allow this, and no realistic way to do it yourself (working at a lower level). The team has a work item to add support for this, but no time estimate at this time.

we are trying to add a timestamp to a signature.

What is your timeline?

Hello Ryan,

Thanks for the followup!
I don’t have a precise timeline right now but we are expecting to produce an MVP in the next few months to integrate digital signatures in some processes. Keep me updated :slight_smile:

Best regards

Michaël

I am happy to report that the team has exposed a new API, and which you can try out using our latest developer preview builds.

Your code would look something like this.

CMSSignatureOptions options;
options.AddTimestampToken(token_buf);
// Then, put the CMS signature components together.
std::vector<UChar> cms_signature = DigitalSignatureField::GenerateCMSSignature(
	signer_cert, chain_certs.data(), chain_certs.size(), digest_algorithm_id, signature_algorithm_id,
	signature_value.data(), signature_value.size(), signedAttrs.data(), signedAttrs.size(), options);

And you can download the dev preview builds here:

https://dev.apryse.com/nightly/experimental/latest

Please let us know how this new API works for you.

Hello Ryan

i’ve tried with the latest nightly in node.js and I get this error when using generateCMSSignature :
RangeError: 7 arguments passed into function ‘generateCMSSignature’. Expected 6 argument. Function Signature: generateCMSSignature(PDFNet.X509Certificate, Array<PDFNet.X509Certificate>, PDFNet.ObjectIdentifier, PDFNet.ObjectIdentifier, ArrayBuffer|TypedArray, ArrayBuffer|TypedArray)

The signature of the function seems off :

  static generateCMSSignature(in_signer_cert: PDFNet.X509Certificate, in_chain_certs_list: PDFNet.X509Certificate[], in_digest_algorithm_oid: PDFNet.ObjectIdentifier, in_signature_algorithm_oid: PDFNet.ObjectIdentifier, in_signature_value_buf: ArrayBuffer | Int8Array | Uint8Array | Uint8ClampedArray, in_signedattributes_buf: ArrayBuffer | Int8Array | Uint8Array | Uint8ClampedArray): Promise<Uint8Array>;
        /**
         * Low-level function belonging to custom-signing APIs. Using low-level inputs that permit incorporation of
        remote key usage (cloud keystore, Hardware Security Module (HSM) device, etc.), generates bytes representing
        a Cryptographic Message Syntax (CMS)-format signature encoded in DER. The resulting data can be passed to
        SaveCustomSignature.
         * @param signer_cert - The X509 public-key certificate of the signature's signer (mathematically associated with private key used).
         * @param chain_certs_list - The intermediate and root certificates to include in the CMS to allow verifiers to establish the chain/path of trust.
         * @param digest_algorithm_id - The digest algorithm used, for embedding in the CMS.
         * @param signature_algorithm_id - The signature algorithm used, for embedding in the CMS.
         * @param signature_value_buf - A buffer containing the signature value to embed in the CMS.
         * @param signedattributes_buf - A buffer containing signedAttributes for embedding into the CMS (must exactly match those used when creating signature value).
         * @param [cms_options] - Optional extra data to store in the CMS.
         * @returns A promise that resolves to the finished CMS data for embedding into the document using SaveCustomSignature.
         */

Good news though, I’ve tried with the function generateCMSSignatureWithAlgoId and adapted my code for this and this one accepts the option parameter and it works!

Cheers