Custom signing with HSM?

Question:

How do I sign a PDF with a HSM?

Answer:
Our guide here shows the starting point of doing this.

However it leaves out the actual HSM signing, since that part will vary based on users particular HSM.

To help, the following C# code shows how to sign using .NET System.Security.Cryptography.RSA to sign.

Just place this code in our DigitalSignaturesTest.cs file and run the sample, as the code just uses the samples private key.

PDFDoc doc = new PDFDoc();
doc.PagePushBack(doc.PageCreate());

Page page1 = doc.GetPage(1);

var in_sig_field_name = "MySignature";
var in_PAdES_signing_mode = false;
var in_outpath = output_path + "test.pdf";
var in_cert_path = input_path + "pdftron.cer";
var in_private_key = input_path + "pdftron.pfx";
var in_appearance_img_path = input_path + "pdftron.bmp";
var pkcs12Password = "password";


// Create a digital signature field and associated widget.
DigitalSignatureField digsig_field = doc.CreateDigitalSignatureField(in_sig_field_name);
SignatureWidget widgetAnnot = SignatureWidget.Create(doc, new Rect(143, 287, 219, 306), digsig_field);
Image img = Image.Create(doc, in_appearance_img_path);
widgetAnnot.CreateSignatureAppearance(img);
page1.AnnotPushBack(widgetAnnot);

// Create a digital signature dictionary inside the digital signature field, in preparation for signing.
digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
	in_PAdES_signing_mode ? DigitalSignatureField.SubFilterType.e_ETSI_CAdES_detached : DigitalSignatureField.SubFilterType.e_adbe_pkcs7_detached,
	7500); // For security reasons, set the contents size to a value greater than but as close as possible to the size you expect your final signature to be, in bytes.
		   // ... or, if you want to apply a certification signature, use CreateSigDictForCustomCertification instead.

// (OPTIONAL) Set the signing time in the signature dictionary, if no secure embedded timestamping support is available from your signing provider.
Date current_date = new Date();
current_date.SetCurrentTime();
digsig_field.SetSigDictTimeOfSigning(current_date);

// Save the document incrementally to avoid invalidating any previous signatures.
doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental);

// Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
byte[] pdf_digest = digsig_field.CalculateDigest(DigestAlgorithm.Type.e_sha256);

X509Certificate signer_cert = new X509Certificate(in_cert_path);

/* Optionally, you can add a custom signed attribute at this point, such as one of the PAdES ESS attributes. The function we provide takes care of generating the correct PAdES ESS attribute depending on your digest algorithm. */
byte[] pades_versioned_ess_signing_cert_attribute = DigitalSignatureField.GenerateESSSigningCertPAdESAttribute(signer_cert, DigestAlgorithm.Type.e_sha256);

// Generate the signedAttrs component of CMS, passing any optional custom signedAttrs (e.g. PAdES ESS). The signedAttrs are certain attributes that become protected by their inclusion in the signature.
byte[] signedAttrs = DigitalSignatureField.GenerateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute);

// Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
byte[] signedAttrs_digest = DigestAlgorithm.CalculateDigest(DigestAlgorithm.Type.e_sha256, signedAttrs);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom signing here (e.g. HSM)
System.Security.Cryptography.X509Certificates.X509Certificate2 signerCert = new System.Security.Cryptography.X509Certificates.X509Certificate2(in_private_key, pkcs12Password,
	System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable);
var rsa = RSA.Create();
rsa.FromXmlString(signerCert.PrivateKey.ToXmlString(true));
var signature_value = rsa.SignHash(signedAttrs_digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Then, load all your chain certificates into a container of X509Certificate.
X509Certificate[] chain_certs = new X509Certificate[1];
chain_certs[0] = signer_cert; // here we just load any chain that might be in the public cert.
// any more here...

// Then, create ObjectIdentifiers for the algorithms you have used.
ObjectIdentifier digest_algorithm_oid = new ObjectIdentifier(ObjectIdentifier.Predefined.e_SHA256);
ObjectIdentifier signature_algorithm_oid = new ObjectIdentifier(ObjectIdentifier.Predefined.e_RSA_encryption_PKCS1);

// Then, put the CMS signature components together.
byte[] cms_signature = DigitalSignatureField.GenerateCMSSignature(
	signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid,
	signature_value, signedAttrs);

// Write the signature to the document.
doc.SaveCustomSignature(cms_signature, digsig_field, in_outpath);

Another user requested NodeJS sample, so here is one that uses NodeJS built-in crypto package.

Simply:

  1. place the attached pdftron.key private PEM key in the samples/TestFiles folder
  2. replace the samples/DigitalSignaturesTest/NODEJS/DigitalSignaturesTest.js file with the one attached.

pdftron.key (1.7 KB)
DigitalSignaturesTest.js.txt (5.9 KB)