> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/core/get-started/samples/digitalsignaturestest.md).

# Digitally Sign PDF Files - DigitalSignatures

Sample code to use Apryse SDK's high-level digital signature API for digitally signing and/or certifying PDF files.  Samples provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="/core/get-started/get-started.md" class="button primary">Server SDK</a><a href="https://apryse.com/capabilities#DigitalSignature" class="button primary">Package: Digital Signature</a><a href="https://showcase.apryse.com/digital-signatures" class="button primary">Live demo</a>
{% endhint %}

Sample code to use Apryse SDK's high-level digital signature API for digitally signing and/or certifying PDF files; provided in Python, C++, C#, Java, Node.js (JavaScript), PHP, Ruby and VB.

Learn more about our [Server SDK](/core/get-started/get-started.md) and [PDF Digital Signature Library](/core/digital-signature/signature.md).

### **Implementation steps**

To apply and validate digital signatures with Apryse Server SDK:

Step 1: Follow [get started with Server SDK in your preferred language or framework](/core/get-started/get-started.md) Step 2: Add the sample code provided in this guide. Make note of directions at top of code samples.

To use this feature in production, your license key will need the [Digital Signature Package](https://apryse.com/capabilities#DigitalSignature). Trial keys already include all packages.

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
//
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
//

//----------------------------------------------------------------------------------------------------------------------
// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
//
// The following steps reflect typical intended usage of the digital signatures API:
//
//	0.	Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
//	
//	1.	EITHER: 
//		(a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
//		-OR-
//		(b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
//		document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
//	
//	2.	Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
//		If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
//		NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
//		
//	[3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
//		or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
//		certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
//		is signed.]
//		
//	4. 	Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
//		a.	Taking a PKCS #12 keyfile path and its password
//		b.	Taking a buffer containing a PKCS #12 private keyfile and its password
//		c.	Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
//			in the following fashion: 
//			i)		Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
//					validate/check a digital signature.
//			ii)		Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
//					pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
//			iii)	Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
//		NOTE: It is only possible to sign/certify one signature per call to the Save function.
//	
//	5.	Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
//		IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
//		so as to not invalidate the other signature(s). 
//
// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
//
// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
//----------------------------------------------------------------------------------------------------------------------

// In order to use .NET Framework's Cryptography library, define "USE_DOTNET_CRYPTO" and then add System.Security to
// references list.

using System;
using System.Collections.Generic;
using System.IO;
#if USE_DOTNET_CRYPTO
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
#endif // USE_DOTNET_CRYPTO

using pdftron;
using pdftron.Common;
using pdftron.PDF;
using pdftron.PDF.Annots;
using pdftron.SDF;
using pdftron.Crypto;

namespace DigitalSignaturesTestCS
{
	//////////////////// Here follows an example of how to implement a custom signature handler. //////////
#if USE_DOTNET_CRYPTO
	class DotNetCryptoSignatureHandler : SignatureHandler
	{
		private List<byte> m_data;
		private string m_signingCert;
		private string m_certPassword;

		public DotNetCryptoSignatureHandler(string signingCert, string password)
		{
			m_signingCert = signingCert;
			m_certPassword = password;
			m_data = new List<byte>();
		}

		public override void AppendData(byte[] data)
		{
			m_data.AddRange(data);
		}

		public override bool Reset()
		{
			m_data.Clear();
			return (true);
		}

		public override byte[] CreateSignature()
		{
			try {
				ContentInfo ci = new ContentInfo(m_data.ToArray());
				SignedCms sc = new SignedCms(ci, true);
				X509Certificate2 cert = new X509Certificate2(m_signingCert, m_certPassword);
				CmsSigner cs = new CmsSigner();
				cs.Certificate = cert;
				cs.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1");//SHA256
				sc.ComputeSignature(cs);
				byte[] sig = sc.Encode();
				return (sig);
			}
			catch (Exception e) {
				Console.Error.WriteLine(e);
			}
			return (null);
		}

		public override string GetName()
		{
			return ("Adobe.PPKLite");
		}
	}
#endif // USE_DOTNET_CRYPTO
	////////// End of the DotNetCryptoSignatureHandler custom handler code. ////////////////////

	class Class1
	{
		static string input_path = "../../../../TestFiles/";
		static string output_path = "../../../../TestFiles/Output/";

		static bool VerifySimple (string in_docpath, string in_public_key_file_path)
		{
			using (PDFDoc doc = new PDFDoc(in_docpath))
			{
				Console.WriteLine("==========");
				VerificationOptions opts = new VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving);

				// Add trust root to store of trusted certificates contained in VerificationOptions.
				opts.AddTrustedCertificate(in_public_key_file_path, 
					(ushort)(VerificationOptions.CertificateTrustFlag.e_default_trust | VerificationOptions.CertificateTrustFlag.e_certification_trust));

				PDFDoc.SignaturesVerificationStatus result = doc.VerifySignedDigitalSignatures(opts);
				switch (result)
				{
					case PDFDoc.SignaturesVerificationStatus.e_unsigned:
						Console.WriteLine("Document has no signed signature fields.");
						return false;
					/* e_failure == bad doc status, digest status, or permissions status
					(i.e. does not include trust issues, because those are flaky due to being network/config-related) */
					case PDFDoc.SignaturesVerificationStatus.e_failure:
						Console.WriteLine("Hard failure in verification on at least one signature.");
						return false;
					case PDFDoc.SignaturesVerificationStatus.e_untrusted:
						Console.WriteLine("Could not verify trust for at least one signature.");
						return false;
					case PDFDoc.SignaturesVerificationStatus.e_unsupported:
						/* If necessary, call GetUnsupportedFeatures on VerificationResult to check which
						unsupported features were encountered (requires verification using 'detailed' APIs) */
						Console.WriteLine("At least one signature contains unsupported features.");
						return false;
					// unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
					case PDFDoc.SignaturesVerificationStatus.e_verified:
						Console.WriteLine("All signed signatures in document verified.");
						return true;
					default:
						throw new Exception("unrecognized document verification status");
				}
			}
		}   
			 
		static bool VerifyAllAndPrint(string in_docpath, string in_public_key_file_path)
		{
			using (PDFDoc doc = new PDFDoc(in_docpath))
			{
				Console.WriteLine("==========");
				VerificationOptions opts = new VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving);

				// Add trust root to store of trusted certificates contained in VerificationOptions.
				opts.AddTrustedCertificate(in_public_key_file_path, 
					(ushort) (VerificationOptions.CertificateTrustFlag.e_default_trust | VerificationOptions.CertificateTrustFlag.e_certification_trust));

				// Iterate over the signatures and verify all of them.
				DigitalSignatureFieldIterator digsig_fitr = doc.GetDigitalSignatureFieldIterator();
				bool verification_status = true;
				for (; digsig_fitr.HasNext(); digsig_fitr.Next())
				{
					DigitalSignatureField curr = digsig_fitr.Current();
					VerificationResult result = curr.Verify(opts);
					if (result.GetVerificationStatus())
					{
						Console.Write("Signature verified, ");
					}
					else
					{
						Console.Write("Signature verification failed, ");
						verification_status = false;
					}
					Console.WriteLine("objnum: {0}", curr.GetSDFObj().GetObjNum());

					switch (result.GetDigestAlgorithm())
					{
						case DigestAlgorithm.Type.e_sha1:
							Console.WriteLine("Digest algorithm: SHA-1");
							break;
						case DigestAlgorithm.Type.e_sha256:
							Console.WriteLine("Digest algorithm: SHA-256");
							break;
						case DigestAlgorithm.Type.e_sha384:
							Console.WriteLine("Digest algorithm: SHA-384");
							break;
						case DigestAlgorithm.Type.e_sha512:
							Console.WriteLine("Digest algorithm: SHA-512");
							break;
						case DigestAlgorithm.Type.e_ripemd160:
							Console.WriteLine("Digest algorithm: RIPEMD-160");
							break;
						case DigestAlgorithm.Type.e_unknown_digest_algorithm:
							Console.WriteLine("Digest algorithm: unknown");
							break;
						default:
							throw new Exception("unrecognized digest algorithm");
					}
					Console.WriteLine("Detailed verification result: \n\t{0}\n\t{1}\n\t{2}\n\t{3}", 
						result.GetDocumentStatusAsString(),
						result.GetDigestStatusAsString(),
						result.GetTrustStatusAsString(),
						result.GetPermissionsStatusAsString());


					DisallowedChange[] changes = result.GetDisallowedChanges();
					foreach (DisallowedChange it2 in changes)
					{
						Console.WriteLine("\tDisallowed change: {0}, objnum: {1}", it2.GetTypeAsString(), it2.GetObjNum());
					}

					// Get and print all the detailed trust-related results, if they are available.
					if (result.HasTrustVerificationResult())
					{
						TrustVerificationResult trust_verification_result = result.GetTrustVerificationResult();
						Console.WriteLine(trust_verification_result.WasSuccessful() ? "Trust verified." : "Trust not verifiable.");
						Console.WriteLine(trust_verification_result.GetResultString());

						long time_of_verification = trust_verification_result.GetTimeOfTrustVerification();
						switch (trust_verification_result.GetTimeOfTrustVerificationEnum())
						{
							case VerificationOptions.TimeMode.e_current:
								Console.WriteLine("Trust verification attempted with respect to current time (as epoch time): {0}", time_of_verification);
								break;
							case VerificationOptions.TimeMode.e_signing:
								Console.WriteLine("Trust verification attempted with respect to signing time (as epoch time): {0}", time_of_verification);
								break;
							case VerificationOptions.TimeMode.e_timestamp:
								Console.WriteLine("Trust verification attempted with respect to secure embedded timestamp (as epoch time): {0}", time_of_verification);
								break;
							default:
								throw new Exception("unrecognized time enum value");
						}

						if (trust_verification_result.GetCertPath().Length == 0)
						{
							Console.WriteLine("Could not print certificate path.");
						}
						else
						{
							Console.WriteLine("Certificate path:");
							X509Certificate[] cert_path = trust_verification_result.GetCertPath();
							for (int j = 0; j < cert_path.Length; j++)
							{
								Console.WriteLine("\tCertificate:");
								X509Certificate full_cert = cert_path[j];
								Console.WriteLine("\t\tIssuer names:");
								X501AttributeTypeAndValue[] issuer_dn = full_cert.GetIssuerField().GetAllAttributesAndValues();
								for (int i = 0; i < issuer_dn.Length; i++)
								{
									Console.WriteLine("\t\t\t" + issuer_dn[i].GetStringValue());
								}
								Console.WriteLine("\t\tSubject names:");
								X501AttributeTypeAndValue[] subject_dn = full_cert.GetSubjectField().GetAllAttributesAndValues();
								for (int i = 0; i < subject_dn.Length; i++)
								{
									Console.WriteLine("\t\t\t" + subject_dn[i].GetStringValue());
								}
								Console.WriteLine("\t\tExtensions:");
								for (int i = 0; i < full_cert.GetExtensions().Length; i++)
								{
									Console.WriteLine("\t\t\t" + full_cert.GetExtensions()[i].ToString());
								}
							}
						}
					}
					else
					{
						Console.WriteLine("No detailed trust verification result available.");
					}

					string[] unsupported_features = result.GetUnsupportedFeatures();
					if (unsupported_features.Length > 0)
					{
						Console.WriteLine("Unsupported features:");

						for (int i = 0; i < unsupported_features.Length; i++)
						{
							Console.WriteLine("\t" + unsupported_features[i]);
						}
					}
					Console.WriteLine("==========");
				}

				return verification_status;
			}
		}

		static void CertifyPDF(string in_docpath,
			string in_cert_field_name,
			string in_private_key_file_path,
			string in_keyfile_password,
			string in_appearance_image_path,
			string in_outpath)
		{
			Console.Out.WriteLine("================================================================================");
			Console.Out.WriteLine("Certifying PDF document");

			// Open an existing PDF
			using (PDFDoc doc = new PDFDoc(in_docpath))
			{
				Console.Out.WriteLine("PDFDoc has " + (doc.HasSignatures() ? "signatures" : "no signatures"));

				Page page1 = doc.GetPage(1);

				// Create a text field that we can lock using the field permissions feature.
				TextWidget annot1 = TextWidget.Create(doc, new Rect(143, 440, 350, 460), "asdf_test_field");
				page1.AnnotPushBack(annot1);

				/* Create a new signature form field in the PDFDoc. The name argument is optional;
				leaving it empty causes it to be auto-generated. However, you may need the name for later.
				Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
				Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
				DigitalSignatureField certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name);
				SignatureWidget widgetAnnot = SignatureWidget.Create(doc, new Rect(143, 287, 219, 306), certification_sig_field);
				page1.AnnotPushBack(widgetAnnot);

				// (OPTIONAL) Add an appearance to the signature field.
				Image img = Image.Create(doc, in_appearance_image_path);
				widgetAnnot.CreateSignatureAppearance(img);

				// Prepare the document locking permission level. It will be applied upon document certification.
				Console.Out.WriteLine("Adding document permissions.");
				certification_sig_field.SetDocumentPermissions(DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed);
				
				// Prepare to lock the text field that we created earlier.
				Console.Out.WriteLine("Adding field permissions.");
				string[] fields_to_lock = new string[1];
				fields_to_lock[0] = "asdf_test_field";
				certification_sig_field.SetFieldPermissions(DigitalSignatureField.FieldPermissions.e_include, fields_to_lock);

			#if USE_DOTNET_CRYPTO
				DotNetCryptoSignatureHandler sigHandler = new DotNetCryptoSignatureHandler(in_private_key_file_path, in_keyfile_password);
				SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
				certification_sig_field.CertifyOnNextSaveWithCustomHandler(sigHandlerId);
				/* Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format 
				for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, 
				so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this 
				when using the StdSignatureHandler. */
				Obj f_obj = certification_sig_field.GetSDFObj();
				f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached");
			#else
				certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password);
			#endif

				// (OPTIONAL) Add more information to the signature dictionary.
				certification_sig_field.SetLocation("Vancouver, BC");
				certification_sig_field.SetReason("Document certification.");
				certification_sig_field.SetContactInfo("www.pdftron.com");

				// Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
				doc.Save(in_outpath, 0);
			}

			Console.Out.WriteLine("================================================================================");
		}

		static void SignPDF(string in_docpath,
			string in_approval_field_name,
			string in_private_key_file_path,
			string in_keyfile_password,
			string in_appearance_img_path,
			string in_outpath)
		{
			Console.Out.WriteLine("================================================================================");
			Console.Out.WriteLine("Signing PDF document");

			// Open an existing PDF
			using (PDFDoc doc = new PDFDoc(in_docpath))
			{
				// Retrieve the unsigned approval signature field.
				Field found_approval_field = doc.GetField(in_approval_field_name);
				DigitalSignatureField found_approval_signature_digsig_field = new DigitalSignatureField(found_approval_field);
				
				// (OPTIONAL) Add an appearance to the signature field.
				Image img = Image.Create(doc, in_appearance_img_path);
				SignatureWidget found_approval_signature_widget = new SignatureWidget(found_approval_field.GetSDFObj());
				found_approval_signature_widget.CreateSignatureAppearance(img);

				// Prepare the signature and signature handler for signing.
			#if USE_DOTNET_CRYPTO
				DotNetCryptoSignatureHandler sigHandler = new DotNetCryptoSignatureHandler(in_private_key_file_path, in_keyfile_password);
				SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
				found_approval_signature_digsig_field.SignOnNextSaveWithCustomHandler(sigHandlerId);
				/* Add a SubFilter name that uniquely identifies the signature format for verification tools. As an 
				example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, so we embed 
				one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this 
				when using the StdSignatureHandler.*/
				Obj f_obj = found_approval_signature_digsig_field.GetSDFObj();
				f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached");
			#else
				found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password);
			#endif

				// The actual approval signing will be done during the following incremental save operation.
				doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental);
			}
			Console.Out.WriteLine("================================================================================");
		}

		static void ClearSignature(string in_docpath,
			string in_digsig_field_name,
			string in_outpath)
		{
			Console.Out.WriteLine("================================================================================");
			Console.Out.WriteLine("Clearing certification signature");

			using (PDFDoc doc = new PDFDoc(in_docpath))
			{
				DigitalSignatureField digsig = new DigitalSignatureField(doc.GetField(in_digsig_field_name));
				
				Console.Out.WriteLine("Clearing signature: " + in_digsig_field_name);
				digsig.ClearSignature();

				if (!digsig.HasCryptographicSignature())
				{
					Console.Out.WriteLine("Cryptographic signature cleared properly.");
				}

				// Save incrementally so as to not invalidate other signatures from previous saves.
				doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental);
			}

			Console.Out.WriteLine("================================================================================");
		}

		static void PrintSignaturesInfo(string in_docpath)
		{
			Console.Out.WriteLine("================================================================================");
			Console.Out.WriteLine("Reading and printing digital signature information");

			using (PDFDoc doc = new PDFDoc(in_docpath))
			{
				if (!doc.HasSignatures())
				{
					Console.Out.WriteLine("Doc has no signatures.");
					Console.Out.WriteLine("================================================================================");
					return;
				}
				else
				{
					Console.Out.WriteLine("Doc has signatures.");
				}

				
				for (FieldIterator fitr = doc.GetFieldIterator(); fitr.HasNext(); fitr.Next())
				{
					if (fitr.Current().IsLockedByDigitalSignature())
					{
						Console.Out.WriteLine("==========\nField locked by a digital signature");
					}
					else
					{
						Console.Out.WriteLine("==========\nField not locked by a digital signature");
					}

					Console.Out.WriteLine("Field name: " + fitr.Current().GetName());
					Console.Out.WriteLine("==========");
				}

				Console.Out.WriteLine("====================\nNow iterating over digital signatures only.\n====================");

				DigitalSignatureFieldIterator digsig_fitr = doc.GetDigitalSignatureFieldIterator();
				for (; digsig_fitr.HasNext(); digsig_fitr.Next())
				{
					Console.Out.WriteLine("==========");
					Console.Out.WriteLine("Field name of digital signature: " + new Field(digsig_fitr.Current().GetSDFObj()).GetName());

					DigitalSignatureField digsigfield = digsig_fitr.Current();
					if (!digsigfield.HasCryptographicSignature())
					{
						Console.Out.WriteLine("Either digital signature field lacks a digital signature dictionary, " +
							"or digital signature dictionary lacks a cryptographic Contents entry. " +
							"Digital signature field is not presently considered signed.\n" +
							"==========");
						continue;
					}

					int cert_count = digsigfield.GetCertCount();
					Console.Out.WriteLine("Cert count: " + cert_count);
					for (int i = 0; i < cert_count; ++i)
					{
						byte[] cert = digsigfield.GetCert(i);
						Console.Out.WriteLine("Cert #" + i + " size: " + cert.Length);
					}

					DigitalSignatureField.SubFilterType subfilter = digsigfield.GetSubFilter();

					Console.Out.WriteLine("Subfilter type: " + (int)subfilter);

					if (subfilter != DigitalSignatureField.SubFilterType.e_ETSI_RFC3161)
					{
						Console.Out.WriteLine("Signature's signer: " + digsigfield.GetSignatureName());

						Date signing_time = digsigfield.GetSigningTime();
						if (signing_time.IsValid())
						{
							Console.Out.WriteLine("Signing time is valid.");
						}

						Console.Out.WriteLine("Location: " + digsigfield.GetLocation());
						Console.Out.WriteLine("Reason: " + digsigfield.GetReason());
						Console.Out.WriteLine("Contact info: " + digsigfield.GetContactInfo());
					}
					else
					{
						Console.Out.WriteLine("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)\n");
					}

					Console.Out.WriteLine(((digsigfield.HasVisibleAppearance()) ? "Visible" : "Not visible"));

					DigitalSignatureField.DocumentPermissions digsig_doc_perms = digsigfield.GetDocumentPermissions();
					string[] locked_fields = digsigfield.GetLockedFields();
					foreach (string field_name in locked_fields)
					{
						Console.Out.WriteLine("This digital signature locks a field named: " + field_name);
					}

					switch (digsig_doc_perms)
					{
					case DigitalSignatureField.DocumentPermissions.e_no_changes_allowed:
						Console.Out.WriteLine("No changes to the document can be made without invalidating this digital signature.");
						break;
					case DigitalSignatureField.DocumentPermissions.e_formfilling_signing_allowed:
						Console.Out.WriteLine("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
						break;
					case DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed:
						Console.Out.WriteLine("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
						break;
					case DigitalSignatureField.DocumentPermissions.e_unrestricted:
						Console.Out.WriteLine("Document not restricted by this digital signature.");
						break;
					default:
						throw new Exception("Unrecognized digital signature document permission level.");
					}
					Console.Out.WriteLine("==========");
				}
			}

			Console.Out.WriteLine("================================================================================");
		}
 
		static void CustomSigningAPI(string doc_path,
			string cert_field_name,
			string private_key_file_path,
			string keyfile_password,
			string public_key_file_path,
			string appearance_image_path,
			DigestAlgorithm.Type digest_algorithm_type,
			bool PAdES_signing_mode,
			string output_path)
		{
			Console.Out.WriteLine("================================================================================");
			Console.Out.WriteLine("Custom signing PDF document");
			using (PDFDoc doc = new PDFDoc(doc_path))
			{
				Page page1 = doc.GetPage(1);

				DigitalSignatureField digsig_field = doc.CreateDigitalSignatureField(cert_field_name);
				SignatureWidget widgetAnnot = SignatureWidget.Create(doc, new Rect(143, 287, 219, 306), digsig_field);
				page1.AnnotPushBack(widgetAnnot);

				// (OPTIONAL) Add an appearance to the signature field.
				Image img = Image.Create(doc, appearance_image_path);
				widgetAnnot.CreateSignatureAppearance(img);

				// Create a digital signature dictionary inside the digital signature field, in preparation for signing.
				digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
					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);

				doc.Save(output_path, SDFDoc.SaveOptions.e_incremental);

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

				X509Certificate signer_cert = new X509Certificate(public_key_file_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, digest_algorithm_type);

				// 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(digest_algorithm_type, signedAttrs);

				//////////////////////////// custom digest signing starts ////////////////////////////
				// At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
				// which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
				byte[] signature_value = DigestAlgorithm.SignDigest(
					signedAttrs_digest,
					digest_algorithm_type,
					private_key_file_path,
					keyfile_password);
				//////////////////////////// custom digest signing ends //////////////////////////////

				// Then, load all your chain certificates into a container of X509Certificate.
				X509Certificate[] chain_certs = {};

				// Then, create ObjectIdentifiers for the algorithms you have used.
				// Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
				ObjectIdentifier digest_algorithm_oid = new ObjectIdentifier(digest_algorithm_type);
				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, output_path);
			}
			Console.Out.WriteLine("================================================================================");
		}

		static bool TimestampAndEnableLTV(string doc_path, 
			string tsa_url,
			string trusted_cert_path, 
			string appearance_img_path,
			string output_path)
		{
			using (PDFDoc doc = new PDFDoc(doc_path))
			{
				DigitalSignatureField doctimestamp_signature_field = doc.CreateDigitalSignatureField();
				TimestampingConfiguration tst_config = new TimestampingConfiguration(tsa_url);
				VerificationOptions opts = new VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving);
				/* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
				the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
				response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
				function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
				in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
				opts.AddTrustedCertificate(trusted_cert_path);
				/* By default, we only check online for revocation of certificates using the newer and lighter 
				OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
				it may be necessary to enable online CRL revocation checking in order to verify some timestamps
				(i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
				opts.EnableOnlineCRLRevocationChecking(true);

				SignatureWidget widgetAnnot = SignatureWidget.Create(doc, new Rect(0, 100, 200, 150), doctimestamp_signature_field);
				doc.GetPage(1).AnnotPushBack(widgetAnnot);
				Obj widgetObj = widgetAnnot.GetSDFObj();

				// (OPTIONAL) Add an appearance to the signature field.
				Image img = Image.Create(doc, appearance_img_path);
				widgetAnnot.CreateSignatureAppearance(img);

				Console.WriteLine("Testing timestamping configuration.");
				TimestampingResult config_result = tst_config.TestConfiguration(opts);
				if (config_result.GetStatus())
				{
					Console.WriteLine("Success: timestamping configuration usable. Attempting to timestamp.");
				}
				else
				{
					// Print details of timestamping failure.
					Console.WriteLine(config_result.GetString());
					if (config_result.HasResponseVerificationResult())
					{
						EmbeddedTimestampVerificationResult tst_result = config_result.GetResponseVerificationResult();
						Console.WriteLine("CMS digest status: {0}\n", tst_result.GetCMSDigestStatusAsString());
						Console.WriteLine("Message digest status:{0}\n", tst_result.GetMessageImprintDigestStatusAsString());
						Console.WriteLine("Trust status: {0}\n", tst_result.GetTrustStatusAsString());
					}
					return false;
				}

				doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts);

				// Save/signing throws if timestamping fails.
				doc.Save(output_path, SDFDoc.SaveOptions.e_incremental);

				Console.WriteLine("Timestamping successful. Adding LTV information for DocTimeStamp signature.");

				// Add LTV information for timestamp signature to document.
				VerificationResult timestamp_verification_result = doctimestamp_signature_field.Verify(opts);
				if (!doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result))
				{
					Console.WriteLine("Could not enable LTV for DocTimeStamp.");
					return false;
				}
				doc.Save(output_path, SDFDoc.SaveOptions.e_incremental);
				Console.WriteLine("Added LTV information for DocTimeStamp signature successfully.");

				return true;
			}
		}
		
		private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
		static Class1() {}

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			// Initialize PDFNetC
			PDFNet.Initialize(PDFTronLicense.Key);

			bool result = true;

			//////////////////// TEST 0: 
			/* Create an approval signature field that we can sign after certifying.
			(Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
			try
			{
				using (PDFDoc doc = new PDFDoc(input_path + "waiver.pdf")) 
				{
					DigitalSignatureField approval_signature_field = doc.CreateDigitalSignatureField("PDFTronApprovalSig");
					SignatureWidget widgetAnnotApproval = SignatureWidget.Create(doc, new Rect(300, 287, 376, 306), approval_signature_field);
					Page page1 = doc.GetPage(1);
					page1.AnnotPushBack(widgetAnnotApproval);
					doc.Save(output_path + "waiver_withApprovalField_output.pdf", SDFDoc.SaveOptions.e_remove_unused);
				}				
			}
			catch (Exception e)
			{
				Console.Error.WriteLine(e);
				result = false;
			}

			//////////////////// TEST 1: certify a PDF.
			try
			{
				CertifyPDF(input_path + "waiver_withApprovalField.pdf",
					"PDFTronCertificationSig",
					input_path + "pdftron.pfx",
					"password",
					input_path + "pdftron.bmp",
					output_path + "waiver_withApprovalField_certified_output.pdf");
				PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_output.pdf");
			}
			catch (Exception e)
			{
				Console.Error.WriteLine(e);
				result = false;
			}

			//////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
			try
			{
				SignPDF(input_path + "waiver_withApprovalField_certified.pdf",
					"PDFTronApprovalSig",
					input_path + "pdftron.pfx",
					"password",
					input_path + "signature.jpg",
					output_path + "waiver_withApprovalField_certified_approved_output.pdf");
				PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_output.pdf");
			}
			catch (Exception e)
			{
				Console.Error.WriteLine(e);
				result = false;
			}

			//////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
			try
			{
				ClearSignature(input_path + "waiver_withApprovalField_certified_approved.pdf",
					"PDFTronCertificationSig",
					output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
				PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
			}
			catch (Exception e)
			{
				Console.Error.WriteLine(e);
				result = false;
			}

			//////////////////// TEST 4: Verify a document's digital signatures.
			try
			{
				if (!VerifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf",
						input_path + "pdftron.cer"))
				{
					result = false;
				}
			}
			catch (Exception e)
			{
				Console.Error.WriteLine(e);
				result = false;
			}
			//////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
			try
			{
				if (!VerifySimple(input_path + "waiver_withApprovalField_certified_approved.pdf",
				input_path + "pdftron.cer"))
				{
					result = false;
				}
			}
			catch (Exception e)
			{
				Console.Error.WriteLine(e);
				result = false;
			}

			//////////////////// TEST 6: Custom signing API.
			// The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
			// which allows users to customize the process of signing documents. Among other things, this
			// includes the capability to allow for easy integration of PDF-specific signing-related operations
			// with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
			// to system keystores, etc.
			try
			{
				CustomSigningAPI(input_path + "waiver.pdf",
					"PDFTronApprovalSig",
					input_path + "pdftron.pfx",
					"password",
					input_path + "pdftron.cer",
					input_path + "signature.jpg",
					DigestAlgorithm.Type.e_sha256,
					true,
					output_path + "waiver_custom_signed.pdf");
			}
			catch (Exception e)
			{
				Console.Error.WriteLine(e);
				result = false;
			}

			//////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
			// try
			// {
			// 	// Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
			// 	// For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
			// 	// Note that this url may not work in the future. A reliable solution requires using your own TSA.
			// 	string tsa_url = "YOUR_URL_OF_TSA";
			// 	if (tsa_url == "YOUR_URL_OF_TSA")
			// 	{
			// 		throw new Exception("Error: The URL of your timestamp authority was not specified.");
			// 	}
			//
			// 	// Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
			// 	// For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
			// 	// Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
			// 	string trusted_cert_path = "YOUR_CERTIFICATE";
			// 	if (trusted_cert_path == "YOUR_CERTIFICATE")
			// 	{
			// 		throw new Exception("Error: The path to your timestamp authority trusted root certificate was not specified.");
			// 	}
			//
			// 	if (!TimestampAndEnableLTV(input_path + "waiver.pdf",
			// 		tsa_url,
			// 		trusted_cert_path,
			// 		input_path + "signature.jpg",
			// 		output_path+ "waiver_DocTimeStamp_LTV.pdf"))
			// 	{
			// 		result = false;
			// 	}
			// }
			// catch (Exception e)
			// {
			// 	Console.Error.WriteLine(e);
			// 	result = false;
			// }

			//////////////////// End of tests. ////////////////////
			PDFNet.Terminate();
			if (result)
			{
				Console.Out.WriteLine("Tests successful.\n==========");
			}
			else
			{
				Console.Out.WriteLine("Tests FAILED!!!\n==========");
			}
		}
	}
}
```

{% endcode %}
{% endtab %}

{% tab title="Go" %}
{% code lineNumbers="true" %}

```go
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult LICENSE.txt regarding license information.
//---------------------------------------------------------------------------------------

////----------------------------------------------------------------------------------------------------------------------
//// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
////
//// The following steps reflect typical intended usage of the digital signatures API:
////
////  0.  Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
////  
////  1.  EITHER: 
////      (a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
////      -OR-
////      (b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
////      document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
////  
////  2.  Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
////      If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
////      NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
////      
////  [3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
////      or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
////      certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
////      is signed.]
////      
////  4.  Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
////      a.  Taking a PKCS //12 keyfile path and its password
////      b.  Taking a buffer containing a PKCS //12 private keyfile and its password
////      c.  Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
////          in the following fashion: 
////          i)      Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
////                  validate/check a digital signature.
////          ii)     Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
////                  pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
////          iii)    Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
////      NOTE: It is only possible to sign/certify one signature per call to the Save function.
////  
////  5.  Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
////      IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
////      so as to not invalidate the other signature(s). 
////
//// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
//// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
//// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
////
//// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
//// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
////----------------------------------------------------------------------------------------------------------------------

package main
import (
    "fmt"
    "strconv"
    "flag"
    "testing"
    . "github.com/ApryseSDK/pdftron-go/v2"
)

var licenseKey string
var modulePath string

func init() {
    flag.StringVar(&licenseKey, "license", "", "License key for Apryse SDK")
    flag.StringVar(&modulePath, "modulePath", "", "Module path for Apryse SDK")
}

func VerifySimple(inDocpath string, inPublicKeyFilePath string) bool{
    doc := NewPDFDoc(inDocpath)
    fmt.Println("==========")
    opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)

    // Add trust root to store of trusted certificates contained in VerificationOptions.
    opts.AddTrustedCertificate(inPublicKeyFilePath, uint16(VerificationOptionsE_default_trust | VerificationOptionsE_certification_trust))

    result := doc.VerifySignedDigitalSignatures(opts)
        
    if result == PDFDocE_unsigned{
        fmt.Println("Document has no signed signature fields.")
        return false
        // e_failure == bad doc status, digest status, or permissions status
        // (i.e. does not include trust issues, because those are flaky due to being network/config-related)
    }else if result == PDFDocE_failure{
        fmt.Println("Hard failure in verification on at least one signature.")
        return false
    }else if result == PDFDocE_untrusted{
        fmt.Println("Could not verify trust for at least one signature.")
        return false
    }else if result == PDFDocE_unsupported{
        // If necessary, call GetUnsupportedFeatures on VerificationResult to check which
        // unsupported features were encountered (requires verification using 'detailed' APIs)
        fmt.Println("At least one signature contains unsupported features.")
        return false
        // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
    }else if result == PDFDocE_verified{
        fmt.Println("All signed signatures in document verified.")
        return true
    }else{
        fmt.Println("unrecognized document verification status")
        return false
    }
}

func VerifyAllAndPrint(inDocpath string, inPublicKeyFilePath string) bool{
    doc := NewPDFDoc(inDocpath)
    fmt.Println("==========")
    opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)
    
    // Trust the public certificate we use for signing.
    trustedCertFile := NewMappedFile(inPublicKeyFilePath)
    fileSz := trustedCertFile.FileSize()
    fileReader := NewFilterReader(trustedCertFile)
    trustedCertBuf := fileReader.Read(fileSz)
    trustedCertBytes := make([]byte, int(trustedCertBuf.Size()))
    for i := 0; i < int(trustedCertBuf.Size()); i ++ {
        trustedCertBytes[i] = trustedCertBuf.Get(i)
    }
    opts.AddTrustedCertificate(&trustedCertBytes[0], int64(len(trustedCertBytes)), uint16(VerificationOptionsE_default_trust | VerificationOptionsE_certification_trust))

    // Iterate over the signatures and verify all of them.
    digsigFitr := doc.GetDigitalSignatureFieldIterator()
    verificationStatus := true
    for (digsigFitr.HasNext()){
        curr := digsigFitr.Current()
        result := curr.Verify(opts)
        if result.GetVerificationStatus(){
            fmt.Printf("Signature verified, objnum: %d\n", curr.GetSDFObj().GetObjNum())
        }else{
            fmt.Printf("Signature verification failed, objnum: %d\n", curr.GetSDFObj().GetObjNum())
            verificationStatus = false
        }
        digest_algorithm := result.GetDigestAlgorithm()
        if digest_algorithm == DigestAlgorithmE_SHA1{
            fmt.Println("Digest algorithm: SHA-1")
        }else if digest_algorithm == DigestAlgorithmE_SHA256{
            fmt.Println("Digest algorithm: SHA-256")
        }else if digest_algorithm == DigestAlgorithmE_SHA384{
            fmt.Println("Digest algorithm: SHA-384")
        }else if digest_algorithm == DigestAlgorithmE_SHA512{
            fmt.Println("Digest algorithm: SHA-512")
        }else if digest_algorithm == DigestAlgorithmE_RIPEMD160{
            fmt.Println("Digest algorithm: RIPEMD-160")
        }else if digest_algorithm == DigestAlgorithmE_unknown_digest_algorithm{
            fmt.Println("Digest algorithm: unknown")
        }else{
            //unrecognized document status
        }
        fmt.Printf("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s\n", 
            result.GetDocumentStatusAsString(),
            result.GetDigestStatusAsString(),
            result.GetTrustStatusAsString(),
            result.GetPermissionsStatusAsString())
            
        changes := result.GetDisallowedChanges()
        for i := 0; i < int(changes.Size()); i++ {
            fmt.Printf("\tDisallowed change: %s, objnum: %d\n", changes.Get(i).GetTypeAsString(), changes.Get(i).GetObjNum())
        }

        // Get and print all the detailed trust-related results, if they are available.
        if result.HasTrustVerificationResult(){
            trustVerificationResult := result.GetTrustVerificationResult()
            var msg string
            if trustVerificationResult.WasSuccessful(){
                msg = "Trust verified."
            } else {
                msg = "Trust not verifiable."
            }
            fmt.Println(msg)
            fmt.Println(trustVerificationResult.GetResultString())
            
            tmpTimeTv := trustVerificationResult.GetTimeOfTrustVerification()
            
            trustVerificationTimeEnum := trustVerificationResult.GetTimeOfTrustVerificationEnum()
            
            if trustVerificationTimeEnum == VerificationOptionsE_current{
                fmt.Println("Trust verification attempted with respect to current time (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
            }else if trustVerificationTimeEnum == VerificationOptionsE_signing{
                fmt.Println("Trust verification attempted with respect to signing time (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
            }else if trustVerificationTimeEnum == VerificationOptionsE_timestamp{
                fmt.Println("Trust verification attempted with respect to secure embedded timestamp (as epoch time): " + strconv.Itoa(int(tmpTimeTv)))
            }else{
                //unrecognized time enum value
            }

            if trustVerificationResult.GetCertPath().Size() == 0{
                fmt.Println("Could not print certificate path.")
            }else{
                fmt.Println("Certificate path:")
                certPath := trustVerificationResult.GetCertPath()
                for i := 0; i < int(certPath.Size()); i ++{
                    fmt.Println("\tCertificate:")
                    fmt.Println("\t\tIssuer names:")
                    issuerDn := certPath.Get(i).GetIssuerField().GetAllAttributesAndValues()
                    for j := 0; j < int(issuerDn.Size()); j ++{ 
                        fmt.Println("\t\t\t" + issuerDn.Get(j).GetStringValue())
                    }
                    fmt.Println("\t\tSubject names:")
                    subjectDn := certPath.Get(i).GetSubjectField().GetAllAttributesAndValues()
                    for k := 0; k < int(subjectDn.Size()); k ++{
                        fmt.Println("\t\t\t" + subjectDn.Get(k).GetStringValue())
                    }
                    fmt.Println("\t\tExtensions:")
                    for m := 0; m < int(certPath.Get(i).GetExtensions().Size()); m ++{
                        fmt.Println("\t\t\t" + certPath.Get(i).GetExtensions().Get(m).ToString())
                    }
                }
            }        
        }else{
            fmt.Println("No detailed trust verification result available.")
        
            unsupportedFeatures := result.GetUnsupportedFeatures()
            if unsupportedFeatures.Size() > 0 {
                fmt.Println("Unsupported features:")
                for i := 0; i < int(unsupportedFeatures.Size()); i ++{
                    fmt.Println("\t" + unsupportedFeatures.Get(i))
                }
            }
        }
        fmt.Println("==========")
        
        digsigFitr.Next()
    }
    return verificationStatus
}

func CertifyPDF(inDocpath string,
    inCertFieldName string ,
    inPrivateKeyFilePath string,
    inKeyfilePassword string,
    inAppearanceImagePath string,
    inOutpath string){
    
    fmt.Println("================================================================================")
    fmt.Println("Certifying PDF document")

    // Open an existing PDF
    doc := NewPDFDoc(inDocpath)

    if doc.HasSignatures(){
        fmt.Println("PDFDoc has signatures")
    }else{
        fmt.Println("PDFDoc has no signatures")
    }
    page1 := doc.GetPage(1)

    // Create a text field that we can lock using the field permissions feature.
    annot1 := TextWidgetCreate(doc, NewRect(143.0, 440.0, 350.0, 460.0), "asdf_test_field")
    page1.AnnotPushBack(annot1)

    // Create a new signature form field in the PDFDoc. The name argument is optional;
    // leaving it empty causes it to be auto-generated. However, you may need the name for later.
    // Acrobat doesn"t show digsigfield in side panel if it's without a widget. Using a
    // Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. 
    certificationSigField := doc.CreateDigitalSignatureField(inCertFieldName)
    widgetAnnot := SignatureWidgetCreate(doc, NewRect(143.0, 287.0, 219.0, 306.0), certificationSigField)
    page1.AnnotPushBack(widgetAnnot)

    // (OPTIONAL) Add an appearance to the signature field.
    img := ImageCreate(doc.GetSDFDoc(), inAppearanceImagePath)
    widgetAnnot.CreateSignatureAppearance(img)

    // Add permissions. Lock the random text field.
    fmt.Println("Adding document permissions.")
    certificationSigField.SetDocumentPermissions(DigitalSignatureFieldE_annotating_formfilling_signing_allowed)
    
    // Prepare to lock the text field that we created earlier.
    fmt.Println("Adding field permissions.")
    testField := NewVectorString()
    testField.Add("asdf_test_field")
    certificationSigField.SetFieldPermissions(DigitalSignatureFieldE_include, testField)

    certificationSigField.CertifyOnNextSave(inPrivateKeyFilePath, inKeyfilePassword)

    // (OPTIONAL) Add more information to the signature dictionary.
    certificationSigField.SetLocation("Vancouver, BC")
    certificationSigField.SetReason("Document certification.")
    certificationSigField.SetContactInfo("www.pdftron.com")

    // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
    doc.Save(inOutpath, uint(0))

    fmt.Println("================================================================================")
}

func SignPDF(inDocpath string, 
    inApprovalFieldName string, 
    inPrivateKeyFilePath string, 
    inKeyfilePassword string, 
    inAppearanceImgPath string, 
    inOutpath string){
    
    fmt.Println("================================================================================")
    fmt.Println("Signing PDF document")

    // Open an existing PDF
    doc := NewPDFDoc(inDocpath)

    // Retrieve the unsigned approval signature field.
    foundApprovalField := doc.GetField(inApprovalFieldName)
    foundApprovalSignatureDigsigField := NewDigitalSignatureField(foundApprovalField)
    
    // (OPTIONAL) Add an appearance to the signature field.
    img := ImageCreate(doc.GetSDFDoc(), inAppearanceImgPath)
    foundApprovalSignatureWidget := NewSignatureWidget(foundApprovalField.GetSDFObj())
    foundApprovalSignatureWidget.CreateSignatureAppearance(img)

    // Prepare the signature and signature handler for signing.
    foundApprovalSignatureDigsigField.SignOnNextSave(inPrivateKeyFilePath, inKeyfilePassword)

    // The actual approval signing will be done during the following incremental save operation.
    doc.Save(inOutpath, uint(SDFDocE_incremental))

    fmt.Println("================================================================================")
}

func ClearSignature(inDocpath string,
    inDigsigFieldName string,
    inOutpath string){

    fmt.Println("================================================================================")
    fmt.Println("Clearing certification signature")

    doc := NewPDFDoc(inDocpath)

    digsig := NewDigitalSignatureField(doc.GetField(inDigsigFieldName))
    
    fmt.Println("Clearing signature: " + inDigsigFieldName)
    digsig.ClearSignature()

    if !digsig.HasCryptographicSignature(){
        fmt.Println("Cryptographic signature cleared properly.")
    }
    // Save incrementally so as to not invalidate other signatures from previous saves.
    doc.Save(inOutpath, uint(SDFDocE_incremental))

    fmt.Println("================================================================================")
}

func PrintSignaturesInfo(inDocpath string){
    fmt.Println("================================================================================")
    fmt.Println("Reading and printing digital signature information")

    doc := NewPDFDoc(inDocpath)
    if !doc.HasSignatures(){
        fmt.Println("Doc has no signatures.")
        fmt.Println("================================================================================")
        return
    }else{
        fmt.Println("Doc has signatures.")
    }

    fitr := doc.GetFieldIterator()
    for fitr.HasNext(){
        current := fitr.Current()
        if (current.IsLockedByDigitalSignature()){
            fmt.Println("==========\nField locked by a digital signature")
        }else{
            fmt.Println("==========\nField not locked by a digital signature")
        }
        fmt.Println("Field name: " + current.GetName())
        fmt.Println("==========")
        
        fitr.Next()
    }

    fmt.Println("====================\nNow iterating over digital signatures only.\n====================")

    digsigFitr := doc.GetDigitalSignatureFieldIterator()
    for digsigFitr.HasNext(){
        current := digsigFitr.Current()
        fmt.Println("==========")
        fmt.Println("Field name of digital signature: " + NewField(current.GetSDFObj()).GetName())

        digsigfield := current
        if !digsigfield.HasCryptographicSignature(){
            fmt.Println("Either digital signature field lacks a digital signature dictionary, " +
                "or digital signature dictionary lacks a cryptographic Contents entry. " +
                "Digital signature field is not presently considered signed.\n" +
                "==========")
            digsigFitr.Next()
            continue
        }
        certCount := digsigfield.GetCertCount()
        fmt.Println("Cert count: " + strconv.Itoa(int(certCount)))
        for i := uint(0); i < certCount; i ++{
            cert := digsigfield.GetCert(i)
            fmt.Println("Cert //" + strconv.Itoa(int(i)) + " size: " + strconv.Itoa(int(cert.Size())))
        }
        subfilter := digsigfield.GetSubFilter()

        fmt.Println("Subfilter type: " + strconv.Itoa(int(subfilter)))

        if subfilter != DigitalSignatureFieldE_ETSI_RFC3161{
            fmt.Println("Signature's signer: " + digsigfield.GetSignatureName())

            signingTime := digsigfield.GetSigningTime()
            if signingTime.IsValid(){
                fmt.Println("Signing time is valid.")
            }
            fmt.Println("Location: " + digsigfield.GetLocation())
            fmt.Println("Reason: " + digsigfield.GetReason())
            fmt.Println("Contact info: " + digsigfield.GetContactInfo())
        }else{
            fmt.Println("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)")
        }
        if digsigfield.HasVisibleAppearance(){
            fmt.Println("Visible")
        }else{
            fmt.Println("Not visible")
        }
        digsigDocPerms := digsigfield.GetDocumentPermissions()
        lockedFields := digsigfield.GetLockedFields()
        for i := 0; i < int(lockedFields.Size()); i ++{
            fmt.Println("This digital signature locks a field named: " + lockedFields.Get(i))
        }
        if digsigDocPerms == DigitalSignatureFieldE_no_changes_allowed{
            fmt.Println("No changes to the document can be made without invalidating this digital signature.")
        }else if digsigDocPerms == DigitalSignatureFieldE_formfilling_signing_allowed{
            fmt.Println("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
        }else if digsigDocPerms == DigitalSignatureFieldE_annotating_formfilling_signing_allowed{
            fmt.Println("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
        }else if digsigDocPerms == DigitalSignatureFieldE_unrestricted{
            fmt.Println("Document not restricted by this digital signature.")
        }else{
            fmt.Println("Unrecognized digital signature document permission level.")
        }
        fmt.Println("==========")
        digsigFitr.Next()
    }

    fmt.Println("================================================================================")
}

func CustomSigningAPI(doc_path string,
        cert_field_name string,
        private_key_file_path string,
        keyfile_password string,
        public_key_file_path string,
        appearance_image_path string,
        digest_algorithm_type PdftronCryptoDigestAlgorithmType,
        PAdES_signing_mode bool,
        output_path string){
    fmt.Println("================================================================================")
    fmt.Println("Custom signing PDF document")

    doc := NewPDFDoc(doc_path)

    page1 := doc.GetPage(1)

    digsig_field := doc.CreateDigitalSignatureField(cert_field_name)
    widgetAnnot := SignatureWidgetCreate(doc, NewRect(143.0, 287.0, 219.0, 306.0), digsig_field)
    page1.AnnotPushBack(widgetAnnot)

    // (OPTIONAL) Add an appearance to the signature field.
    img := ImageCreate(doc.GetSDFDoc(), appearance_image_path)
    widgetAnnot.CreateSignatureAppearance(img)

    signing_mode := DigitalSignatureFieldE_adbe_pkcs7_detached
    if PAdES_signing_mode{
    signing_mode = DigitalSignatureFieldE_ETSI_CAdES_detached
    }
    // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
    digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
        &signing_mode,
        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.
    current_date := NewDate()
    current_date.SetCurrentTime()
    digsig_field.SetSigDictTimeOfSigning(current_date)

    doc.Save(output_path, uint(SDFDocE_incremental))

    // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
    pdf_digest := digsig_field.CalculateDigest(digest_algorithm_type)

    signer_cert := NewX509Certificate(public_key_file_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.
    pades_versioned_ess_signing_cert_attribute := DigitalSignatureFieldGenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type)

    // 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.
    signedAttrs := DigitalSignatureFieldGenerateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute)

    // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
    signedAttrs_digest := DigestAlgorithmCalculateDigest(digest_algorithm_type, signedAttrs)

    ///////////////////////////// custom digest signing starts ////////////////////////////
    // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
    // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
    signature_value := DigestAlgorithmSignDigest(
        signedAttrs_digest,
        digest_algorithm_type,
        private_key_file_path,
        keyfile_password)
    ///////////////////////////// custom digest signing ends ///////////////////////////////

    // Then, load all your chain certificates into a container of X509Certificate.
    chain_certs := NewVectorX509Certificate()

    // Then, create ObjectIdentifiers for the algorithms you have used.
    // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
    digest_algorithm_oid := NewObjectIdentifier(digest_algorithm_type)
    signature_algorithm_oid := NewObjectIdentifier(ObjectIdentifierE_RSA_encryption_PKCS1)

    // Then, put the CMS signature components together.
    cms_signature := DigitalSignatureFieldGenerateCMSSignature(
        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, output_path)

    fmt.Println("================================================================================")
}

func TimestampAndEnableLTV(inDocpath string,
    inTsaUrl string,
    inTrustedCertPath string, 
    inAppearanceImgPath string,
    inOutpath string) bool{
    doc := NewPDFDoc(inDocpath)
    doctimestampSignatureField := doc.CreateDigitalSignatureField()
    tstConfig := NewTimestampingConfiguration(inTsaUrl)
    opts := NewVerificationOptions(VerificationOptionsE_compatibility_and_archiving)
//   It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
//   the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
//   response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
//   function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
//   in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part.

    opts.AddTrustedCertificate(inTrustedCertPath)
//       By default, we only check online for revocation of certificates using the newer and lighter 
//   OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
//   it may be necessary to enable online CRL revocation checking in order to verify some timestamps
//   (i.e. those that do not have an OCSP responder URL for all non-trusted certificates).

    opts.EnableOnlineCRLRevocationChecking(true)

    widgetAnnot := SignatureWidgetCreate(doc, NewRect(0.0, 100.0, 200.0, 150.0), doctimestampSignatureField)
    doc.GetPage(1).AnnotPushBack(widgetAnnot)

    // (OPTIONAL) Add an appearance to the signature field.
    img := ImageCreate(doc.GetSDFDoc(), inAppearanceImgPath)
    widgetAnnot.CreateSignatureAppearance(img)

    fmt.Println("Testing timestamping configuration.")
    configResult := tstConfig.TestConfiguration(opts)
    if configResult.GetStatus(){
        fmt.Println("Success: timestamping configuration usable. Attempting to timestamp.")
    }else{
        // Print details of timestamping failure.
        fmt.Println(configResult.GetString())
        if configResult.HasResponseVerificationResult(){
            tstResult := configResult.GetResponseVerificationResult()
            fmt.Println("CMS digest status: "+ tstResult.GetCMSDigestStatusAsString())
            fmt.Println("Message digest status: " + tstResult.GetMessageImprintDigestStatusAsString())
            fmt.Println("Trust status: " + tstResult.GetTrustStatusAsString())
        }
        return false
    }

    doctimestampSignatureField.TimestampOnNextSave(tstConfig, opts)

    // Save/signing throws if timestamping fails.
    doc.Save(inOutpath, uint(SDFDocE_incremental))

    fmt.Println("Timestamping successful. Adding LTV information for DocTimeStamp signature.")

    // Add LTV information for timestamp signature to document.
    timestampVerificationResult := doctimestampSignatureField.Verify(opts)
    if !doctimestampSignatureField.EnableLTVOfflineVerification(timestampVerificationResult){
        fmt.Println("Could not enable LTV for DocTimeStamp.")
        return false
    }
    doc.Save(inOutpath, uint(SDFDocE_incremental))
    fmt.Println("Added LTV information for DocTimeStamp signature successfully.")

    return true
}

func TestDigitalSignatures(t *testing.T){
    // Initialize PDFNet
    PDFNetInitialize(licenseKey)
    
    result := true
    inputPath := "../TestFiles/"
    outputPath := "../TestFiles/Output/"
    
    //////////////////////////////////////// TEST 0:
    // Create an approval signature field that we can sign after certifying.
    // (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
    // Open an existing PDF
    doc := NewPDFDoc(inputPath + "waiver.pdf")
    widgetAnnotApproval := SignatureWidgetCreate(doc, NewRect(300.0, 287.0, 376.0, 306.0), "PDFTronApprovalSig")
    page1 := doc.GetPage(1)
    page1.AnnotPushBack(widgetAnnotApproval)
    doc.Save(outputPath + "waiver_withApprovalField_output.pdf", uint(SDFDocE_remove_unused))

    //////////////////////////////////////// TEST 1: certify a PDF.
    CertifyPDF(inputPath + "waiver_withApprovalField.pdf",
            "PDFTronCertificationSig",
            inputPath + "pdftron.pfx",
            "password",
            inputPath + "pdftron.bmp",
            outputPath + "waiver_withApprovalField_certified_output.pdf")
    PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_output.pdf")

    //////////////////////////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
    SignPDF(inputPath + "waiver_withApprovalField_certified.pdf",
            "PDFTronApprovalSig",
            inputPath + "pdftron.pfx",
            "password",
            inputPath + "signature.jpg",
            outputPath + "waiver_withApprovalField_certified_approved_output.pdf")
    PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_approved_output.pdf")

    //////////////////////////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
    ClearSignature(inputPath + "waiver_withApprovalField_certified_approved.pdf",
            "PDFTronCertificationSig",
            outputPath + "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
    PrintSignaturesInfo(outputPath + "waiver_withApprovalField_certified_approved_certcleared_output.pdf")

    //////////////////////////////////////// TEST 4: Verify a document's digital signatures.
    if !VerifyAllAndPrint(inputPath + "waiver_withApprovalField_certified_approved.pdf", inputPath + "pdftron.cer"){
        result = false
    }

    //////////////////////////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
    if !VerifySimple(inputPath + "waiver_withApprovalField_certified_approved.pdf", inputPath + "pdftron.cer"){
       result = false
    }

    //////////////////// TEST 6: Custom signing API.
    // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
    // which allows users to customize the process of signing documents. Among other things, this
    // includes the capability to allow for easy integration of PDF-specific signing-related operations
    // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
    // to system keystores, etc.
    CustomSigningAPI(inputPath + "waiver.pdf",
            "PDFTronApprovalSig",
            inputPath + "pdftron.pfx",
            "password",
            inputPath + "pdftron.cer",
            inputPath + "signature.jpg",
            DigestAlgorithmE_SHA256,
            true,
            outputPath + "waiver_custom_signed.pdf")

    //////////////////////////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
    
    // // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
    // // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
    // // Note that this url may not work in the future. A reliable solution requires using your own TSA.
    // tsaUrl := "YOUR_URL_OF_TSA"
    //
    // // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
    // // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
    // // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
    // trustedCertPath := "YOUR_CERTIFICATE";
    //
    // if tsaUrl == "YOUR_URL_OF_TSA" {
    //     fmt.Println("Error: The URL of your timestamp authority was not specified.")
    //     result = false
    // } else if trustedCertPath == "YOUR_CERTIFICATE" {
    //     fmt.Println("Error: The path to your timestamp authority trusted root certificate was not specified.")
    //     result = false
    // } else if !TimestampAndEnableLTV(inputPath + "waiver.pdf",
    //    tsaUrl,
    //    trustedCertPath,
    //    inputPath + "signature.jpg",
    //    outputPath + "waiver_DocTimeStamp_LTV.pdf") {
    //    result = false
    // }
     
    //////////////////////////////////////// End of tests. ////////////////////////////////////////

    if !result{
        fmt.Println("Tests FAILED!!!\n==========")
        PDFNetTerminate()
        return
    }
    PDFNetTerminate()
    fmt.Println("Tests successful.\n==========")
}
```

{% endcode %}
{% endtab %}

{% tab title="C++" %}
{% code lineNumbers="true" %}

```cpp
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------------------
// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
//
// The following steps reflect typical intended usage of the digital signatures API:
//
//	0.	Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
//	
//	1.	EITHER: 
//		(a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
//		-OR-
//		(b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
//		document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
//	
//	2.	Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
//		If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
//		NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
//		
//	[3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
//		or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
//		certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
//		is signed.]
//		
//	4. 	Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
//		a.	Taking a PKCS #12 keyfile path and its password
//		b.	Taking a buffer containing a PKCS #12 private keyfile and its password
//		c.	Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
//			in the following fashion: 
//			i)		Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
//					validate/check a digital signature.
//			ii)		Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
//					pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
//			iii)	Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
//		NOTE: It is only possible to sign/certify one signature per call to the Save function.
//	
//	5.	Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
//		IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
//		so as to not invalidate the other signature(s). 
//
// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
//
// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
//----------------------------------------------------------------------------------------------------------------------

// To build and run this sample with OpenSSL, please specify OpenSSL include & lib paths to project settings.
//
// In MSVC, this can be done by opening the DigitalSignatureTest project's properties. Go to Configuration Properties ->
// C/C++ -> General -> Additional Include Directories. Add the path to the OpenSSL headers here. Next, go to
// Configuration Properties -> Linker -> General -> Additional Library Directories. Add the path to the OpenSSL libraries
// here. Finally, under Configuration Properties -> Linker -> Input -> Additional Dependencies, add libeay32.lib,
// crypt32.lib, and advapi32.lib in the list.
//
// For GCC, modify the Makefile, add -lcrypto to the $(LIBS) variable. If OpenSSL is installed elsewhere, it may be
// necessary to add the path to the headers in the $(INCLUDE) variable as well as the location of either libcrypto.a or
// libcrypto.so/libcrypto.dylib.
//

#define USE_STD_SIGNATURE_HANDLER 1 // Comment out this line if you intend to use OpenSSLSignatureHandler rather than StdSignatureHandler.

// standard library includes
#include <cstdio>
#include <iostream>
#include <vector>

// PDFNetC includes
#include <Common/Exception.h>
#include <Common/UString.h>
#include <PDF/Page.h>
#include <PDF/Annot.h>
#include <PDF/Annots/TextWidget.h>
#include <PDF/Date.h>
#include <PDF/Element.h>
#include <PDF/ElementBuilder.h>
#include <PDF/ElementWriter.h>
#include <PDF/Field.h>
#include <PDF/Image.h>
#include <PDF/PDFDoc.h>
#include <PDF/PDFNet.h>
#include <SDF/SignatureHandler.h>
#include <PDF/Annots/SignatureWidget.h>
#include <PDF/VerificationResult.h>
#include <PDF/TrustVerificationResult.h>
#include <PDF/DisallowedChange.h>
#include <Filters/MappedFile.h>
#include <Crypto/X501AttributeTypeAndValue.h>
#include "../../LicenseKey/CPP/LicenseKey.h"

#if (!USE_STD_SIGNATURE_HANDLER)
// OpenSSL includes
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pkcs12.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#endif // (!USE_STD_SIGNATURE_HANDLER)

using namespace std;
using namespace pdftron;
using namespace pdftron::SDF;
using namespace pdftron::PDF::Annots;
using namespace pdftron::PDF;

//////////////////// Here follows an example of how to implement a custom signature handler. //////////
#if (!USE_STD_SIGNATURE_HANDLER)
//
// Extend SignatureHandler by using OpenSSL signing utilities.
//
class OpenSSLSignatureHandler : public SignatureHandler
{
public:
	OpenSSLSignatureHandler(const char* in_pfxfile, const char* in_password) : m_pfxfile(in_pfxfile), m_password(in_password)
	{
		// Please note: this code would require changes to support non-US-ASCII paths on platforms where UTF-8 is not usable within fopen (e.g. Windows).
		FILE* fp = fopen(in_pfxfile, "rb");
		if (fp == NULL)
			throw (Common::Exception("Cannot open private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot open private key."));

		PKCS12* p12 = d2i_PKCS12_fp(fp, NULL);
		fclose(fp);

		if (p12 == NULL)
			throw (Common::Exception("Cannot parse private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot parse private key."));

		mp_pkey = NULL;
		mp_x509 = NULL;
		mp_ca = NULL;
		int parseResult = PKCS12_parse(p12, in_password, &mp_pkey, &mp_x509, &mp_ca);
		PKCS12_free(p12);

		if (parseResult == 0)
			throw (Common::Exception("Cannot parse private key.", __LINE__, __FILE__, "PKCS7Signature::PKCS7Signature", "Cannot parse private key."));

		Reset();
	}

	virtual UString GetName() const
	{
		return "Adobe.PPKLite";
	}

	virtual void AppendData(const std::vector<pdftron::UInt8>& in_data)
	{
		SHA256_Update(&m_sha_ctx, (const void*) &(in_data[0]), in_data.size());
		return;
	}

	virtual bool Reset()
	{
		m_digest.resize(0);
		m_digest.clear();
		SHA256_Init(&m_sha_ctx);
		return (true);
	}

	virtual std::vector<pdftron::UInt8> CreateSignature()
	{
		if (m_digest.size() == 0) {
			m_digest.resize(SHA256_DIGEST_LENGTH);
			SHA256_Final(&(m_digest[0]), &m_sha_ctx);
		}

		PKCS7* p7 = PKCS7_new();
		PKCS7_set_type(p7, NID_pkcs7_signed);

		PKCS7_SIGNER_INFO* p7Si = PKCS7_add_signature(p7, mp_x509, mp_pkey, EVP_sha256());
		PKCS7_add_attrib_content_type(p7Si, OBJ_nid2obj(NID_pkcs7_data));
		PKCS7_add0_attrib_signing_time(p7Si, NULL);
		PKCS7_add1_attrib_digest(p7Si, &(m_digest[0]), (int)m_digest.size());
		PKCS7_add_certificate(p7, mp_x509);

		for (int c = 0; c < sk_X509_num(mp_ca); c++) {
			X509* cert = sk_X509_value(mp_ca, c);
			PKCS7_add_certificate(p7, cert);
		}
		PKCS7_set_detached(p7, 1);
		PKCS7_content_new(p7, NID_pkcs7_data);

		PKCS7_SIGNER_INFO_sign(p7Si);

		int p7Len = i2d_PKCS7(p7, NULL);
		std::vector<unsigned char> result(p7Len);
		UInt8* pP7Buf = &(result[0]);
		i2d_PKCS7(p7, &pP7Buf);

		PKCS7_free(p7);

		return (result);
	}

	virtual OpenSSLSignatureHandler* Clone() const
	{
		return (new OpenSSLSignatureHandler(m_pfxfile.c_str(), m_password.c_str()));
	}

	virtual ~OpenSSLSignatureHandler()
	{
		sk_X509_free(mp_ca);
		X509_free(mp_x509);
		EVP_PKEY_free(mp_pkey);
	}

private:
	std::vector<UInt8> m_digest;
	std::string m_pfxfile;
	std::string m_password;

	SHA256_CTX m_sha_ctx;
	EVP_PKEY* mp_pkey;      // private key
	X509* mp_x509;          // signing certificate
	STACK_OF(X509)* mp_ca;  // certificate chain up to the CA
}; // class OpenSSLSignatureHandler
#endif // (!USE_STD_SIGNATURE_HANDLER)
////////// End of the OpenSSLSignatureHandler custom handler code. ////////////////////


string input_path = "../../TestFiles/";
string output_path = "../../TestFiles/Output/";

bool VerifySimple(const UString& in_docpath, const UString& in_public_key_file_path)
{
	PDFDoc doc(in_docpath);
	puts("==========");
	VerificationOptions opts(VerificationOptions::e_compatibility_and_archiving);

	// Add trust root to store of trusted certificates contained in VerificationOptions.
	opts.AddTrustedCertificate(in_public_key_file_path, VerificationOptions::e_default_trust | VerificationOptions::e_certification_trust);

	enum PDFDoc::SignaturesVerificationStatus result = doc.VerifySignedDigitalSignatures(opts);
	switch (result)
	{
	case PDFDoc::e_unsigned:
		puts("Document has no signed signature fields.");
		return false;
		/* e_failure == bad doc status, digest status, or permissions status
		(i.e. does not include trust issues, because those are flaky due to being network/config-related) */
	case PDFDoc::e_failure:
		puts("Hard failure in verification on at least one signature.");
		return false;
	case PDFDoc::e_untrusted:
		puts("Could not verify trust for at least one signature.");
		return false;
	case PDFDoc::e_unsupported:
		/* If necessary, call GetUnsupportedFeatures on VerificationResult to check which
		unsupported features were encountered (requires verification using 'detailed' APIs) */
		puts("At least one signature contains unsupported features.");
		return false;
		// unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
	case PDFDoc::e_verified:
		puts("All signed signatures in document verified.");
		return true;
	default:
		BASE_ASSERT(false, "unrecognized document verification status");
	}
}

bool VerifyAllAndPrint(const UString& in_docpath, const UString& in_public_key_file_path)
{
	PDFDoc doc(in_docpath);
	cout << "==========" << endl;
	PDF::VerificationOptions opts(PDF::VerificationOptions::e_compatibility_and_archiving);
	
	/* Add trust root to store of trusted certificates contained in VerificationOptions. 
	Use trust level corresponding to an identity trusted even for certification signatures. */
	opts.AddTrustedCertificate(in_public_key_file_path, VerificationOptions::e_default_trust | VerificationOptions::e_certification_trust);

	// Iterate over the signatures and verify all of them.
	DigitalSignatureFieldIterator digsig_fitr = doc.GetDigitalSignatureFieldIterator();
	bool verification_status = true;
	for (; digsig_fitr.HasNext(); digsig_fitr.Next())
	{
		DigitalSignatureField curr = digsig_fitr.Current();
		VerificationResult result = curr.Verify(opts);
		if (result.GetVerificationStatus())
		{
			cout << "Signature verified, ";
		}
		else
		{
			cout << "Signature verification failed, ";
			verification_status = false;
		}
		cout << "objnum: " << curr.GetSDFObj().GetObjNum() << endl;
		
		switch (result.GetDigestAlgorithm())
		{
		case Crypto::DigestAlgorithm::e_SHA1:
			cout << "Digest algorithm: SHA-1" << endl;
			break;
		case Crypto::DigestAlgorithm::e_SHA256:
			cout << "Digest algorithm: SHA-256" << endl;
			break;
		case Crypto::DigestAlgorithm::e_SHA384:
			cout << "Digest algorithm: SHA-384" << endl;
			break;
		case Crypto::DigestAlgorithm::e_SHA512:
			cout << "Digest algorithm: SHA-512" << endl;
			break;
		case Crypto::DigestAlgorithm::e_RIPEMD160:
			cout << "Digest algorithm: RIPEMD-160" << endl;
			break;
		case Crypto::DigestAlgorithm::e_unknown_digest_algorithm:
			cout << "Digest algorithm: unknown" << endl;
			break;
		default:
			BASE_ASSERT(false, "unrecognized digest algorithm");
		}
		printf("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s\n", 
			result.GetDocumentStatusAsString().ConvertToUtf8().c_str(),
			result.GetDigestStatusAsString().ConvertToUtf8().c_str(),
			result.GetTrustStatusAsString().ConvertToUtf8().c_str(),
			result.GetPermissionsStatusAsString().ConvertToUtf8().c_str());


		std::vector<DisallowedChange> changes = result.GetDisallowedChanges();
		for (std::vector<DisallowedChange>::iterator it2 = changes.begin(); it2 != changes.end(); ++it2)
		{
			cout << "\tDisallowed change: " << it2->GetTypeAsString().ConvertToUtf8() << ", objnum: " << it2->GetObjNum() << endl;
		}
		
		// Get and print all the detailed trust-related results, if they are available.
		if (result.HasTrustVerificationResult())
		{
			const TrustVerificationResult trust_verification_result = result.GetTrustVerificationResult();
			trust_verification_result.WasSuccessful()? cout << "Trust verified." << endl : cout << "Trust not verifiable." << endl;
			cout << trust_verification_result.GetResultString().ConvertToUtf8() << endl;

			Int64 tmp_time_t = trust_verification_result.GetTimeOfTrustVerification();
			switch (trust_verification_result.GetTimeOfTrustVerificationEnum())
			{
			case VerificationOptions::e_current:
				cout << "Trust verification attempted with respect to current time (as epoch time): " << tmp_time_t << endl;
				break;
			case VerificationOptions::e_signing:
				cout << "Trust verification attempted with respect to signing time (as epoch time): " << tmp_time_t << endl;
				break;
			case VerificationOptions::e_timestamp:
				cout << "Trust verification attempted with respect to secure embedded timestamp (as epoch time): " << tmp_time_t << endl;
				break;
			default:
				BASE_ASSERT(false, "unrecognized time enum value");
			}

			if (trust_verification_result.GetCertPath().empty())
			{
				cout << "Could not print certificate path.\n";
			}
			else
			{
				cout << "Certificate path:\n";
				std::vector<Crypto::X509Certificate>  cert_path(trust_verification_result.GetCertPath());
				for (std::vector<Crypto::X509Certificate>::iterator cert_path_begin = cert_path.begin();
					cert_path_begin != cert_path.end(); ++cert_path_begin)
				{
					cout << "\tCertificate:\n";
					Crypto::X509Certificate& full_cert(*cert_path_begin);
					cout << "\t\tIssuer names:\n";
					std::vector<Crypto::X501AttributeTypeAndValue> issuer_dn (full_cert.GetIssuerField().GetAllAttributesAndValues());
					for (size_t i = 0; i < issuer_dn.size(); i++)
					{
						cout << "\t\t\t" << issuer_dn[i].GetStringValue().ConvertToUtf8() << "\n";
					}
					cout << "\t\tSubject names:\n";
					std::vector<Crypto::X501AttributeTypeAndValue > subject_dn(full_cert.GetSubjectField().GetAllAttributesAndValues());
					for (size_t i = 0; i < subject_dn.size(); i++)
					{
						cout << "\t\t\t" << subject_dn[i].GetStringValue().ConvertToUtf8() << "\n";
					}
					cout << "\t\tExtensions:\n";
					for (size_t i = 0; i < full_cert.GetExtensions().size(); i++)
					{
						cout << "\t\t\t" << full_cert.GetExtensions()[i].ToString() << "\n";
					}
				}
			}
		}
		else
		{
			cout << "No detailed trust verification result available." << endl;
		}

		const std::vector<UString> unsupported_features(result.GetUnsupportedFeatures());
		if (unsupported_features.size() > 0)
		{
			cout << "Unsupported features:" << "\n";
			for (size_t i = 0; i < unsupported_features.size(); i++)
			{
				cout << "\t" << unsupported_features[i].ConvertToUtf8() << "\n";
			}
		}
		cout << "==========" << endl;
	}

	return verification_status;
}

void CertifyPDF(const UString& in_docpath,
	const UString& in_cert_field_name,
	const UString& in_private_key_file_path,
	const UString& in_keyfile_password,
	const UString& in_appearance_image_path,
	const UString& in_outpath)
{
	cout << "================================================================================" << endl;
	cout << "Certifying PDF document" << endl;

	// Open an existing PDF
	PDFDoc doc(in_docpath);

	cout << "PDFDoc has " << (doc.HasSignatures() ? "signatures" : "no signatures") << endl;

	Page page1 = doc.GetPage(1);

	// Create a text field that we can lock using the field permissions feature.
	Annots::TextWidget annot1 = Annots::TextWidget::Create(doc, Rect(143, 440, 350, 460), "asdf_test_field");
	page1.AnnotPushBack(annot1);

	/* Create a new signature form field in the PDFDoc. The name argument is optional;
	leaving it empty causes it to be auto-generated. However, you may need the name for later.
	Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
	Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
	PDF::DigitalSignatureField certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name);
	Annots::SignatureWidget widgetAnnot = Annots::SignatureWidget::Create(doc, Rect(143, 287, 219, 306), certification_sig_field);
	page1.AnnotPushBack(widgetAnnot);

	// (OPTIONAL) Add an appearance to the signature field.
	PDF::Image img = PDF::Image::Create(doc, in_appearance_image_path);
	widgetAnnot.CreateSignatureAppearance(img);

	// Prepare the document locking permission level. It will be applied upon document certification.
	cout << "Adding document permissions." << endl;
	certification_sig_field.SetDocumentPermissions(DigitalSignatureField::e_annotating_formfilling_signing_allowed);
	
	// Prepare to lock the text field that we created earlier.
	cout << "Adding field permissions." << endl;
	vector<UString> fields_to_lock;
	fields_to_lock.push_back("asdf_test_field");
	certification_sig_field.SetFieldPermissions(DigitalSignatureField::e_include, fields_to_lock);

#ifdef USE_STD_SIGNATURE_HANDLER
	certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password);
#else
	OpenSSLSignatureHandler sigHandler(in_private_key_file_path.ConvertToUtf8().c_str(), in_keyfile_password.ConvertToUtf8().c_str());
	SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
	certification_sig_field.CertifyOnNextSaveWithCustomHandler(sigHandlerId);
	/* Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format 
	for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, 
	so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this 
	when using the StdSignatureHandler. */
	Obj f_obj = certification_sig_field.GetSDFObj();
	f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached");
#endif

	// (OPTIONAL) Add more information to the signature dictionary.
	certification_sig_field.SetLocation("Vancouver, BC");
	certification_sig_field.SetReason("Document certification.");
	certification_sig_field.SetContactInfo("www.pdftron.com");

	// Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
	doc.Save(in_outpath, 0, NULL);

	cout << "================================================================================" << endl;
}

void SignPDF(const UString& in_docpath,
	const UString& in_approval_field_name,
	const UString& in_private_key_file_path,
	const UString& in_keyfile_password,
	const UString& in_appearance_img_path,
	const UString& in_outpath)
{
	cout << "================================================================================" << endl;
	cout << "Signing PDF document" << endl;

	// Open an existing PDF
	PDFDoc doc(in_docpath);

	// Retrieve the unsigned approval signature field.
	Field found_approval_field(doc.GetField(in_approval_field_name));
	PDF::DigitalSignatureField found_approval_signature_digsig_field(found_approval_field);
	
	// (OPTIONAL) Add an appearance to the signature field.
	PDF::Image img = PDF::Image::Create(doc, in_appearance_img_path);
	Annots::SignatureWidget found_approval_signature_widget(found_approval_field.GetSDFObj());
	found_approval_signature_widget.CreateSignatureAppearance(img);

	// Prepare the signature and signature handler for signing.
#ifdef USE_STD_SIGNATURE_HANDLER
	found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password);
#else
	OpenSSLSignatureHandler sigHandler(in_private_key_file_path.ConvertToUtf8().c_str(), in_keyfile_password.ConvertToUtf8().c_str());
	SignatureHandlerId sigHandlerId = doc.AddSignatureHandler(sigHandler);
	found_approval_signature_digsig_field.SignOnNextSaveWithCustomHandler(sigHandlerId);
	/* Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format 
	for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, 
	so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this 
	when using the StdSignatureHandler. */
	Obj f_obj = found_approval_signature_digsig_field.GetSDFObj();
	f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached");
#endif

	// The actual approval signing will be done during the following incremental save operation.
	doc.Save(in_outpath, SDFDoc::e_incremental, NULL);

	cout << "================================================================================" << endl;
}

void ClearSignature(const UString& in_docpath,
	const UString& in_digsig_field_name,
	const UString& in_outpath)
{
	cout << "================================================================================" << endl;
	cout << "Clearing certification signature" << endl;

	PDFDoc doc(in_docpath);

	DigitalSignatureField digsig(doc.GetField(in_digsig_field_name));
	
	cout << "Clearing signature: " << in_digsig_field_name << endl;
	digsig.ClearSignature();

	if (!digsig.HasCryptographicSignature())
	{
		cout << "Cryptographic signature cleared properly." << endl;
	}

	// Save incrementally so as to not invalidate other signatures from previous saves.
	doc.Save(in_outpath, SDFDoc::e_incremental, NULL);

	cout << "================================================================================" << endl;
}

void PrintSignaturesInfo(const UString& in_docpath)
{
	cout << "================================================================================" << endl;
	cout << "Reading and printing digital signature information" << endl;

	PDFDoc doc(in_docpath);
	if (!doc.HasSignatures())
	{
		cout << "Doc has no signatures." << endl;
		cout << "================================================================================" << endl;
		return;
	}
	else
	{
		cout << "Doc has signatures." << endl;
	}

	
	for (FieldIterator fitr = doc.GetFieldIterator(); fitr.HasNext(); fitr.Next())
	{
		fitr.Current().IsLockedByDigitalSignature() ? cout << "==========" << endl << "Field locked by a digital signature" << endl :
			cout << "==========" << endl << "Field not locked by a digital signature" << endl;

		cout << "Field name: " << fitr.Current().GetName() << endl;
		cout << "==========" << endl;
	}

	cout << "====================" << endl << "Now iterating over digital signatures only." << endl << "====================" << endl;

	DigitalSignatureFieldIterator digsig_fitr = doc.GetDigitalSignatureFieldIterator();
	for (; digsig_fitr.HasNext(); digsig_fitr.Next())
	{
		cout << "==========" << endl;
		cout << "Field name of digital signature: " << Field(digsig_fitr.Current().GetSDFObj()).GetName() << endl;

		DigitalSignatureField digsigfield(digsig_fitr.Current());
		if (!digsigfield.HasCryptographicSignature())
		{
			cout << "Either digital signature field lacks a digital signature dictionary, "
				"or digital signature dictionary lacks a cryptographic Contents entry. "
				"Digital signature field is not presently considered signed." << endl 
				<< "==========" << endl;
			continue;
		}

		UInt32 cert_count = digsigfield.GetCertCount();
		cout << "Cert count: " << cert_count << endl;
		for (UInt32 i = 0; i < cert_count; ++i)
		{
			std::vector<unsigned char> cert = digsigfield.GetCert(i);
			cout << "Cert #" << i << " size: " << cert.size() << endl;
		}

		DigitalSignatureField::SubFilterType subfilter = digsigfield.GetSubFilter();

		cout << "Subfilter type: " << (int)subfilter << endl;

		if (subfilter != DigitalSignatureField::e_ETSI_RFC3161)
		{
			cout << "Signature's signer: " << digsigfield.GetSignatureName() << endl;

			Date signing_time(digsigfield.GetSigningTime());
			if (signing_time.IsValid())
			{
				cout << "Signing time is valid." << endl;
			}

			cout << "Location: " << digsigfield.GetLocation() << endl;
			cout << "Reason: " << digsigfield.GetReason() << endl;
			cout << "Contact info: " << digsigfield.GetContactInfo() << endl;
		}
		else
		{
			cout << "SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)" << endl;
		}

		cout << ((digsigfield.HasVisibleAppearance()) ? "Visible" : "Not visible") << endl;

		DigitalSignatureField::DocumentPermissions digsig_doc_perms = digsigfield.GetDocumentPermissions();
		vector<UString> locked_fields(digsigfield.GetLockedFields());
		for (vector<UString>::iterator it = locked_fields.begin(); it != locked_fields.end(); ++it)
		{
			cout << "This digital signature locks a field named: " << it->ConvertToAscii() << endl;
		}

		switch (digsig_doc_perms)
		{
		case DigitalSignatureField::e_no_changes_allowed:
			cout << "No changes to the document can be made without invalidating this digital signature." << endl;
			break;
		case DigitalSignatureField::e_formfilling_signing_allowed:
			cout << "Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature." << endl;
			break;
		case DigitalSignatureField::e_annotating_formfilling_signing_allowed:
			cout << "Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature." << endl;
			break;
		case DigitalSignatureField::e_unrestricted:
			cout << "Document not restricted by this digital signature." << endl;
			break;
		default:
			BASE_ASSERT(false, "Unrecognized digital signature document permission level.");
		}
		cout << "==========" << endl;
	}

	cout << "================================================================================" << endl;
}

void CustomSigningAPI(const UString& doc_path,
	const UString& cert_field_name,
	const UString& private_key_file_path,
	const UString& keyfile_password,
	const UString& public_key_file_path,
	const UString& appearance_image_path,
	const Crypto::DigestAlgorithm::Type digest_algorithm_type,
	const bool PAdES_signing_mode,
	const UString& output_path)
{
	cout << "================================================================================" << endl;
	cout << "Custom signing PDF document" << endl;

	PDFDoc doc(doc_path);

	Page page1 = doc.GetPage(1);

	DigitalSignatureField digsig_field = doc.CreateDigitalSignatureField(cert_field_name);
	Annots::SignatureWidget widgetAnnot = Annots::SignatureWidget::Create(doc, Rect(143, 287, 219, 306), digsig_field);
	page1.AnnotPushBack(widgetAnnot);

	// (OPTIONAL) Add an appearance to the signature field.
	PDF::Image img = PDF::Image::Create(doc, appearance_image_path);
	widgetAnnot.CreateSignatureAppearance(img);

	// Create a digital signature dictionary inside the digital signature field, in preparation for signing.
	digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
		PAdES_signing_mode ? DigitalSignatureField::e_ETSI_CAdES_detached : DigitalSignatureField::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;
	current_date.SetCurrentTime();
	digsig_field.SetSigDictTimeOfSigning(current_date);

	doc.Save(output_path, SDFDoc::e_incremental, NULL);

	// Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
	std::vector<UChar> pdf_digest = digsig_field.CalculateDigest(digest_algorithm_type);

	Crypto::X509Certificate signer_cert(public_key_file_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.
	std::vector<UChar> pades_versioned_ess_signing_cert_attribute = DigitalSignatureField::GenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type);

	// 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.
	std::vector<UChar> signedAttrs = DigitalSignatureField::GenerateCMSSignedAttributes(pdf_digest.data(), pdf_digest.size(),
		pades_versioned_ess_signing_cert_attribute.data(), pades_versioned_ess_signing_cert_attribute.size());

	// Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
	std::vector<UChar> signedAttrs_digest = Crypto::DigestAlgorithm::CalculateDigest(digest_algorithm_type, signedAttrs.data(), signedAttrs.size());

	//////////////////////////// custom digest signing starts ////////////////////////////
	// At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
	// which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
	std::vector<UChar> signature_value = Crypto::DigestAlgorithm::SignDigest(
		signedAttrs_digest.data(),
		signedAttrs_digest.size(),
		digest_algorithm_type,
		private_key_file_path,
		keyfile_password);
	//////////////////////////// custom digest signing ends //////////////////////////////

	// Then, load all your chain certificates into a container of X509Certificate.
	std::vector<Crypto::X509Certificate> chain_certs;

	// Then, create ObjectIdentifiers for the algorithms you have used.
	// Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
	Crypto::ObjectIdentifier digest_algorithm_oid(digest_algorithm_type);
	Crypto::ObjectIdentifier signature_algorithm_oid(Crypto::ObjectIdentifier::e_RSA_encryption_PKCS1);

	// Then, put the CMS signature components together.
	std::vector<UChar> cms_signature = DigitalSignatureField::GenerateCMSSignature(
		signer_cert, chain_certs.data(), chain_certs.size(), digest_algorithm_oid, signature_algorithm_oid,
		signature_value.data(), signature_value.size(), signedAttrs.data(), signedAttrs.size());

	// Write the signature to the document.
	doc.SaveCustomSignature(cms_signature.data(), cms_signature.size(), digsig_field, output_path);

	cout << "================================================================================" << endl;
}

bool TimestampAndEnableLTV(const UString& doc_path, 
	const UString& tsa_url,
	const UString& trusted_cert_path, 
	const UString& appearance_img_path,
	const UString& output_path)
{
	PDFDoc doc(doc_path);
	DigitalSignatureField doctimestamp_signature_field = doc.CreateDigitalSignatureField();
	TimestampingConfiguration tst_config(tsa_url);
	VerificationOptions opts(VerificationOptions::e_compatibility_and_archiving);
	/* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
	the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
	response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
	function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
	in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
	opts.AddTrustedCertificate(trusted_cert_path);
	/* By default, we only check online for revocation of certificates using the newer and lighter 
	OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
	it may be necessary to enable online CRL revocation checking in order to verify some timestamps
	(i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
	opts.EnableOnlineCRLRevocationChecking(true);

	Annots::SignatureWidget widgetAnnot = Annots::SignatureWidget::Create(doc, Rect(0, 0, 0, 0), doctimestamp_signature_field);
	doc.GetPage(1).AnnotPushBack(widgetAnnot);

	puts("Testing timestamping configuration.");
	const TimestampingResult config_result(tst_config.TestConfiguration(opts));
	if (config_result.GetStatus())
	{
		puts("Success: timestamping configuration usable. Attempting to timestamp.");
	}
	else
	{
		// Print details of timestamping failure.
		puts(config_result.GetString().ConvertToUtf8().c_str());
		if (config_result.HasResponseVerificationResult())
		{
			EmbeddedTimestampVerificationResult tst_result(config_result.GetResponseVerificationResult());
			printf("CMS digest status: %s\n", tst_result.GetCMSDigestStatusAsString().ConvertToUtf8().c_str());
			printf("Message digest status: %s\n", tst_result.GetMessageImprintDigestStatusAsString().ConvertToUtf8().c_str());
			printf("Trust status: %s\n", tst_result.GetTrustStatusAsString().ConvertToUtf8().c_str());
		}
		return false;
	}

	doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts);

	// Save/signing throws if timestamping fails.
	doc.Save(output_path, SDFDoc::e_incremental, 0);

	puts("Timestamping successful. Adding LTV information for DocTimeStamp signature.");

	// Add LTV information for timestamp signature to document.
	VerificationResult timestamp_verification_result = doctimestamp_signature_field.Verify(opts);
	if (!doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result))
	{
		puts("Could not enable LTV for DocTimeStamp.");
		return false;
	}
	doc.Save(output_path, SDFDoc::e_incremental, 0);
	puts("Added LTV information for DocTimeStamp signature successfully.");

	return true;
}
int main(void)
{
	// Initialize PDFNetC
	PDFNet::Initialize(LicenseKey);

#if (!USE_STD_SIGNATURE_HANDLER)
	// Initialize OpenSSL library
	CRYPTO_malloc_init();
	ERR_load_crypto_strings();
	OpenSSL_add_all_algorithms();
#endif // (!USE_STD_SIGNATURE_HANDLER)

	int ret = 0;

	//////////////////// TEST 0: 
	/* Create an approval signature field that we can sign after certifying.
	(Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
	try
	{
		PDFDoc doc(input_path + "waiver.pdf");
		DigitalSignatureField approval_signature_field = doc.CreateDigitalSignatureField("PDFTronApprovalSig");
		Annots::SignatureWidget widgetAnnotApproval = Annots::SignatureWidget::Create(doc, Rect(300, 287, 376, 306), approval_signature_field);
		Page page1 = doc.GetPage(1);
		page1.AnnotPushBack(widgetAnnotApproval);
		doc.Save(output_path + "waiver_withApprovalField_output.pdf", SDFDoc::e_remove_unused, 0);
	}
	catch (Common::Exception& e)
	{
		cerr << e << endl;
		ret = 1;
	}
	catch (exception& e)
	{
		cerr << e.what() << endl;
		ret = 1;
	}
	catch (...)
	{
		cerr << "Unknown exception." << endl;
		ret = 1;
	}

	//////////////////// TEST 1: certify a PDF.
	try
	{
		CertifyPDF(input_path + "waiver_withApprovalField.pdf",
			"PDFTronCertificationSig",
			input_path + "pdftron.pfx",
			"password",
			input_path + "pdftron.bmp",
			output_path + "waiver_withApprovalField_certified_output.pdf");
		PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_output.pdf");
	}
	catch (Common::Exception& e)
	{
		cerr << e << endl;
		ret = 1;
	}
	catch (exception& e)
	{
		cerr << e.what() << endl;
		ret = 1;
	}
	catch (...)
	{
		cerr << "Unknown exception." << endl;
		ret = 1;
	}

	//////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
	try
	{
		SignPDF(input_path + "waiver_withApprovalField_certified.pdf",
			"PDFTronApprovalSig",
			input_path + "pdftron.pfx",
			"password",
			input_path + "signature.jpg",
			output_path + "waiver_withApprovalField_certified_approved_output.pdf");
		PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_output.pdf");
	}
	catch (Common::Exception& e)
	{
		cerr << e << endl;
		ret = 1;
	}
	catch (exception& e)
	{
		cerr << e.what() << endl;
		ret = 1;
	}
	catch (...)
	{
		cerr << "Unknown exception." << endl;
		ret = 1;
	}

	//////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
	try
	{
		ClearSignature(input_path + "waiver_withApprovalField_certified_approved.pdf",
			"PDFTronCertificationSig",
			output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
		PrintSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
	}
	catch (Common::Exception& e)
	{
		cerr << e << endl;
		ret = 1;
	}
	catch (exception& e)
	{
		cerr << e.what() << endl;
		ret = 1;
	}
	catch (...)
	{
		cerr << "Unknown exception." << endl;
		ret = 1;
	}

	//////////////////// TEST 4: Verify a document's digital signatures.
	try
	{
		if (!VerifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer"))
		{
			ret = 1;
		}
	}
	catch (Common::Exception& e)
	{
		cerr << e << endl;
		ret = 1;
	}
	catch (exception& e)
	{
		cerr << e.what() << endl;
		ret = 1;
	}
	catch (...)
	{
		cerr << "Unknown exception." << endl;
		ret = 1;
	}

	//////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
	try
	{
		if (!VerifySimple(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer"))
		{
			ret = 1;
		}
	}
	catch (Common::Exception& e)
	{
		cerr << e << endl;
		ret = 1;
	}
	catch (exception& e)
	{
		cerr << e.what() << "\n";
		ret = 1;
	}
	catch (...)
	{
		cerr << "Unknown exception.\n";
		ret = 1;
	}

	//////////////////// TEST 6: Custom signing API.
	// The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
	// which allows users to customize the process of signing documents. Among other things, this
	// includes the capability to allow for easy integration of PDF-specific signing-related operations
	// with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
	// to system keystores, etc.
	try
	{
		CustomSigningAPI(input_path + "waiver.pdf",
			"PDFTronApprovalSig",
			input_path + "pdftron.pfx",
			"password",
			input_path + "pdftron.cer",
			input_path + "signature.jpg",
			Crypto::DigestAlgorithm::e_SHA256,
			true,
			output_path + "waiver_custom_signed.pdf");
	}
	catch (Common::Exception& e)
	{
		cerr << e << endl;
		ret = 1;
	}
	catch (exception& e)
	{
		cerr << e.what() << "\n";
		ret = 1;
	}
	catch (...)
	{
		cerr << "Unknown exception.\n";
		ret = 1;
	}

	//////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
	//try
	//{
	//	// Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
	//	// For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
	//	// Note that this url may not work in the future. A reliable solution requires using your own TSA.
	//	const UString tsa_url = "YOUR_URL_OF_TSA";
	//	if (tsa_url == "YOUR_URL_OF_TSA")
	//	{
	//		throw exception("Error: The URL of your timestamp authority was not specified.");
	//	}
	//
	//	// Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
	//	// For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
	//	// Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
	//	const UString trusted_cert_path = "YOUR_CERTIFICATE";
	//	if (trusted_cert_path == "YOUR_CERTIFICATE")
	//	{
	//		throw exception("Error: The path to your timestamp authority trusted root certificate was not specified.");
	//	}
	//
	//	if (!TimestampAndEnableLTV(input_path + "waiver.pdf",
	//		tsa_url,
	//		trusted_cert_path,
	//		input_path + "signature.jpg",
	//		output_path+ "waiver_DocTimeStamp_LTV.pdf"))
	//	{
	//		ret = 1;
	//	}
	//}
	//catch (Common::Exception& e)
	//{
	//	cerr << e << endl;
	//	ret = 1;
	//}
	//catch (exception& e)
	//{
	//	cerr << e.what() << "\n";
	//	ret = 1;
	//}
	//catch (...)
	//{
	//	cerr << "Unknown exception.\n";
	//	ret = 1;
	//}

	//////////////////// End of tests. ////////////////////

	if (!ret)
	{
		cout << "Tests successful." << endl  << "==========" << endl;
	}
	else
	{
		cout << "Tests FAILED!!!" << endl << "==========" << endl;
	}

	PDFNet::Terminate();

#if (!USE_STD_SIGNATURE_HANDLER)
	ERR_free_strings();
	EVP_cleanup();
#endif // (!USE_STD_SIGNATURE_HANDLER)

	return ret;
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------------------
// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
//
// The following steps reflect typical intended usage of the digital signatures API:
//
//	0.	Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
//	
//	1.	EITHER: 
//		(a) Call doc.createDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
//		-OR-
//		(b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
//		document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.getField to get it by its fully qualified name.
//	
//	2.	Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
//		If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
//		NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
//		
//	[3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
//		or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
//		certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
//		is signed.]
//		
//	4. 	Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
//		a.	Taking a PKCS #12 keyfile path and its password
//		b.	Taking a buffer containing a PKCS #12 private keyfile and its password
//		c.	Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
//			in the following fashion: 
//			i)		Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
//					validate/check a digital signature.
//			ii)		Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
//					pdfdoc.addSignatureHandler(). The method returns a SignatureHandlerId.
//			iii)	Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
//		NOTE: It is only possible to sign/certify one signature per call to the Save function.
//	
//	5.	Call pdfdoc.save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
//		IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
//		so as to not invalidate the other signature(s). 
//
// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
//
// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
//----------------------------------------------------------------------------------------------------------------------

import java.util.Locale;
import java.io.IOException;
import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.annots.TextWidget;
import com.pdftron.pdf.annots.SignatureWidget;
import com.pdftron.pdf.FieldIterator;
import com.pdftron.pdf.DigitalSignatureFieldIterator;
import com.pdftron.pdf.Field;
import com.pdftron.pdf.Image;
import com.pdftron.pdf.PDFDoc;
import com.pdftron.pdf.PDFNet;
import com.pdftron.pdf.Page;
import com.pdftron.pdf.Rect;
import com.pdftron.pdf.Date;
import com.pdftron.pdf.DigitalSignatureField;
import com.pdftron.pdf.VerificationOptions;
import com.pdftron.pdf.VerificationResult;
import com.pdftron.pdf.TrustVerificationResult;
import com.pdftron.crypto.DigestAlgorithm;
import com.pdftron.pdf.DisallowedChange;
import com.pdftron.sdf.Obj;
import com.pdftron.sdf.SignatureHandler;
import com.pdftron.sdf.SDFDoc;
import com.pdftron.filters.FilterReader;
import com.pdftron.filters.MappedFile;
import com.pdftron.crypto.X509Certificate;
import com.pdftron.crypto.X501AttributeTypeAndValue;
import com.pdftron.crypto.X501DistinguishedName;
import com.pdftron.crypto.ObjectIdentifier;
import com.pdftron.pdf.TimestampingConfiguration;
import com.pdftron.pdf.TimestampingResult;
import com.pdftron.pdf.EmbeddedTimestampVerificationResult;
public class DigitalSignaturesTest 
{
	public static boolean verifySimple(String in_docpath, String in_public_key_file_path) throws PDFNetException
	{
		PDFDoc doc = new PDFDoc(in_docpath);
		System.out.println("==========");
		VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving);

		// Add trust root to store of trusted certificates contained in VerificationOptions.
		opts.addTrustedCertificate(in_public_key_file_path, 
			VerificationOptions.CertificateTrustFlag.e_default_trust.value | VerificationOptions.CertificateTrustFlag.e_certification_trust.value);

		PDFDoc.SignaturesVerificationStatus result = doc.verifySignedDigitalSignatures(opts);
		
		switch (result)
		{
		case e_unsigned:
			System.out.println("Document has no signed signature fields.");
			return false;
			/*e_failure == bad doc status, digest status, or permissions status
			(i.e. does not include trust issues, because those are flaky due to being network/config-related)*/
		case e_failure:
			System.out.println("Hard failure in verification on at least one signature.");
			return false;
		case e_untrusted:
			System.out.println("Could not verify trust for at least one signature.");
			return false;
		case e_unsupported:
			/*If necessary, call GetUnsupportedFeatures on VerificationResult to check which
			unsupported features were encountered (requires verification using 'detailed' APIs) */
			System.out.println("At least one signature contains unsupported features.");
			return false;
			// unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
		case e_verified:
			System.out.println("All signed signatures in document verified.");
			return true;
		default:
			System.err.println("unrecognized document verification status");
			assert(false);
		}
		return false;
	}

	public static boolean verifyAllAndPrint(String in_docpath, String in_public_key_file_path) throws PDFNetException
	{
		PDFDoc doc = new PDFDoc(in_docpath);
		System.out.println("==========");
		VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving);

		// Add trust root to store of trusted certificates contained in VerificationOptions.
		opts.addTrustedCertificate(in_public_key_file_path, 
			VerificationOptions.CertificateTrustFlag.e_default_trust.value | VerificationOptions.CertificateTrustFlag.e_certification_trust.value);

		// Iterate over the signatures and verify all of them.
		DigitalSignatureFieldIterator digsig_fitr = doc.getDigitalSignatureFieldIterator();
		boolean verification_status = true;
		for (; digsig_fitr.hasNext(); )
		{
			DigitalSignatureField curr = digsig_fitr.next();
			VerificationResult result = curr.verify(opts);
			if (result.getVerificationStatus())
			{
				System.out.print("Signature verified, ");
			}
			else
			{
				System.out.print("Signature verification failed, ");
				verification_status = false;
			}
			System.out.println(String.format(Locale.US, "objnum: %d", curr.getSDFObj().getObjNum()));

			switch (result.getDigestAlgorithm())
			{
				case e_sha1:
					System.out.println("Digest algorithm: SHA-1");
					break;
				case e_sha256:
					System.out.println("Digest algorithm: SHA-256");
					break;
				case e_sha384:
					System.out.println("Digest algorithm: SHA-384");
					break;
				case e_sha512:
					System.out.println("Digest algorithm: SHA-512");
					break;
				case e_ripemd160:
					System.out.println("Digest algorithm: RIPEMD-160");
					break;
				case e_unknown_digest_algorithm:
					System.out.println("Digest algorithm: unknown");
					break;
				default:
					System.err.println("unrecognized digest algorithm");
					assert(false);
			}
			System.out.println(String.format("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s", 
				result.getDocumentStatusAsString(),
				result.getDigestStatusAsString(),
				result.getTrustStatusAsString(),
				result.getPermissionsStatusAsString()));


			DisallowedChange[] changes = result.getDisallowedChanges();
			for (DisallowedChange it2 : changes)
			{
				System.out.println(String.format(Locale.US, "\tDisallowed change: %s, objnum: %d", it2.getTypeAsString(), it2.getObjNum()));
			}

			// Get and print all the detailed trust-related results, if they are available.
			if (result.hasTrustVerificationResult())
			{
				TrustVerificationResult trust_verification_result = result.getTrustVerificationResult();
				System.out.println(trust_verification_result.wasSuccessful() ? "Trust verified." : "Trust not verifiable.");
				System.out.println(trust_verification_result.getResultString());

				long time_of_verification = trust_verification_result.getTimeOfTrustVerification();
				switch (trust_verification_result.getTimeOfTrustVerificationEnum())
				{
					case e_current:
						System.out.println(String.format(Locale.US, "Trust verification attempted with respect to current time (as epoch time): %d", time_of_verification));
						break;
					case e_signing:
						System.out.println(String.format(Locale.US, "Trust verification attempted with respect to signing time (as epoch time): %d", time_of_verification));
						break;
					case e_timestamp:
						System.out.println(String.format(Locale.US, "Trust verification attempted with respect to secure embedded timestamp (as epoch time): %d", time_of_verification));
						break;
					default:
						System.err.println("unrecognized time enum value");
						assert(false);
				}

				if(trust_verification_result.getCertPath().length == 0 )
				{
					System.out.println("Could not print certificate path.");
				}
				else
				{
					System.out.println("Certificate path:");
					X509Certificate[] cert_path = trust_verification_result.getCertPath();
					for (int j = 0; j < cert_path.length; j++)
					{
						System.out.println("\tCertificate:"); 
						X509Certificate full_cert = cert_path[j];
						System.out.println("\t\tIssuer names:");

						X501AttributeTypeAndValue[] issuer_dn  = full_cert.getIssuerField().getAllAttributesAndValues();
						for (int i = 0; i < issuer_dn.length; i++)
						{
							System.out.println("\t\t\t" + issuer_dn[i].getStringValue());
						}
						System.out.println("\t\tSubject names:");
						X501AttributeTypeAndValue[] subject_dn = full_cert.getSubjectField().getAllAttributesAndValues();
						for (int i = 0; i < subject_dn.length; i++)
						{
							System.out.println("\t\t\t" + subject_dn[i].getStringValue());
						}
						System.out.println("\t\tExtensions:");
						for (int i = 0; i < full_cert.getExtensions().length; i++)
						{
							System.out.println("\t\t\t" + full_cert.getExtensions()[i].toString());
						}
					}
				}
			}
			else
			{
				System.out.println("No detailed trust verification result available.");
			}

			String[] unsupported_features = result.getUnsupportedFeatures();
			if (unsupported_features.length > 0)
			{
				System.out.println("Unsupported features:");

				for (String unsupported_feature : unsupported_features)
				{
					System.out.println("\t" + unsupported_feature);
				}
			}
			System.out.println("==========");
		}

		return verification_status;
	}
	
	public static void certifyPDF(String in_docpath,
		String in_cert_field_name,
		String in_private_key_file_path,
		String in_keyfile_password,
		String in_appearance_image_path,
		String in_outpath) throws PDFNetException
	{
		System.out.println("================================================================================");
		System.out.println("Certifying PDF document");

		// Open an existing PDF
		PDFDoc doc = new PDFDoc(in_docpath);

		if (doc.hasSignatures())
		{
			System.out.println("PDFDoc has signatures");
		}
		else
		{
			System.out.println("PDFDoc has no signatures");
		}

		Page page1 = doc.getPage(1);

		// Create a text field that we can lock using the field permissions feature.
		TextWidget annot1 = TextWidget.create(doc, new Rect(143, 440, 350, 460), "asdf_test_field");
		page1.annotPushBack(annot1);

		/* Create a new signature form field in the PDFDoc. The name argument is optional;
		leaving it empty causes it to be auto-generated. However, you may need the name for later.
		Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
		Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
		DigitalSignatureField certification_sig_field = doc.createDigitalSignatureField(in_cert_field_name);
		SignatureWidget widgetAnnot = SignatureWidget.create(doc, new Rect(143, 287, 219, 306), certification_sig_field);
		page1.annotPushBack(widgetAnnot);

		// (OPTIONAL) Add an appearance to the signature field.
		Image img = Image.create(doc, in_appearance_image_path);
		widgetAnnot.createSignatureAppearance(img);

		// Prepare the document locking permission level. It will be applied upon document certification.
		System.out.println("Adding document permissions.");
		certification_sig_field.setDocumentPermissions(DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed);
		
		// Prepare to lock the text field that we created earlier.
		System.out.println("Adding field permissions.");
		String[] fields_to_lock = {"asdf_test_field"};
		certification_sig_field.setFieldPermissions(DigitalSignatureField.FieldPermissions.e_include, fields_to_lock);

		certification_sig_field.certifyOnNextSave(in_private_key_file_path, in_keyfile_password);
		
		// (OPTIONAL) Add more information to the signature dictionary.
		certification_sig_field.setLocation("Vancouver, BC");
		certification_sig_field.setReason("Document certification.");
		certification_sig_field.setContactInfo("www.pdftron.com");

		// Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
		doc.save(in_outpath, SDFDoc.SaveMode.NO_FLAGS, null);

		System.out.println("================================================================================");
	}

	public static void signPDF(String in_docpath,
		String in_approval_field_name,
		String in_private_key_file_path,
		String in_keyfile_password,
		String in_appearance_img_path,
		String in_outpath) throws PDFNetException
	{
		System.out.println("================================================================================");
		System.out.println("Signing PDF document");

		// Open an existing PDF
		PDFDoc doc = new PDFDoc(in_docpath);

		// Retrieve the unsigned approval signature field.
		Field found_approval_field = doc.getField(in_approval_field_name);
		DigitalSignatureField found_approval_signature_digsig_field = new DigitalSignatureField(found_approval_field);
		
		// (OPTIONAL) Add an appearance to the signature field.
		Image img = Image.create(doc, in_appearance_img_path);
		SignatureWidget found_approval_signature_widget = new SignatureWidget(found_approval_field.getSDFObj());
		found_approval_signature_widget.createSignatureAppearance(img);

		// Prepare the signature and signature handler for signing.
		found_approval_signature_digsig_field.signOnNextSave(in_private_key_file_path, in_keyfile_password);

		// The actual approval signing will be done during the following incremental save operation.
		doc.save(in_outpath, SDFDoc.SaveMode.INCREMENTAL, null);

		System.out.println("================================================================================");
	}

	public static void clearSignature(String in_docpath,
		String in_digsig_field_name,
		String in_outpath) throws PDFNetException
	{
		System.out.println("================================================================================");
		System.out.println("Clearing certification signature");

		PDFDoc doc = new PDFDoc(in_docpath);

		DigitalSignatureField digsig = new DigitalSignatureField(doc.getField(in_digsig_field_name));
		
		System.out.println("Clearing signature: " + in_digsig_field_name);
		digsig.clearSignature();

		if (!digsig.hasCryptographicSignature())
		{
			System.out.println("Cryptographic signature cleared properly.");
		}

		// Save incrementally so as to not invalidate other signatures from previous saves.
		doc.save(in_outpath, SDFDoc.SaveMode.INCREMENTAL, null);

		System.out.println("================================================================================");
	}

	public static void printSignaturesInfo(String in_docpath) throws PDFNetException
	{
		System.out.println("================================================================================");
		System.out.println("Reading and printing digital signature information");

		PDFDoc doc = new PDFDoc(in_docpath);
		if (!doc.hasSignatures())
		{
			System.out.println("Doc has no signatures.");
			System.out.println("================================================================================");
			return;
		}
		else
		{
			System.out.println("Doc has signatures.");
		}

		
		for (FieldIterator fitr = doc.getFieldIterator(); fitr.hasNext(); )
		{
			Field current = fitr.next();
			if (current.isLockedByDigitalSignature())
			{
				System.out.println("==========\nField locked by a digital signature");
			}
			else
			{
				System.out.println("==========\nField not locked by a digital signature");
			}

			System.out.println("Field name: " + current.getName());
			System.out.println("==========");
		}

		System.out.println("====================\nNow iterating over digital signatures only.\n====================");

		DigitalSignatureFieldIterator digsig_fitr = doc.getDigitalSignatureFieldIterator();
		for (; digsig_fitr.hasNext(); )
		{
			DigitalSignatureField current = digsig_fitr.next();
			System.out.println("==========");
			System.out.println("Field name of digital signature: " + new Field(current.getSDFObj()).getName());

			DigitalSignatureField digsigfield = current;
			if (!digsigfield.hasCryptographicSignature())
			{
				System.out.println("Either digital signature field lacks a digital signature dictionary, " +
					"or digital signature dictionary lacks a cryptographic Contents entry. " +
					"Digital signature field is not presently considered signed.\n" +
					"==========");
				continue;
			}

			int cert_count = digsigfield.getCertCount();
			System.out.println("Cert count: " + cert_count);
			for (int i = 0; i < cert_count; ++i)
			{
				byte[] cert = digsigfield.getCert(i);
				System.out.println("Cert #" + i + " size: " + cert.length);
			}

			DigitalSignatureField.SubFilterType subfilter = digsigfield.getSubFilter();

			System.out.println("Subfilter type: " + subfilter.ordinal());

			if (subfilter != DigitalSignatureField.SubFilterType.e_ETSI_RFC3161)
			{
				System.out.println("Signature's signer: " + digsigfield.getSignatureName());

				Date signing_time = digsigfield.getSigningTime();
				if (signing_time.isValid())
				{
					System.out.println("Signing time is valid.");
				}

				System.out.println("Location: " + digsigfield.getLocation());
				System.out.println("Reason: " + digsigfield.getReason());
				System.out.println("Contact info: " + digsigfield.getContactInfo());
			}
			else
			{
				System.out.println("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)");
			}

			if (digsigfield.hasVisibleAppearance())
			{
				System.out.println("Visible");
			}
			else
			{
				System.out.println("Not visible");
			}
			
			DigitalSignatureField.DocumentPermissions digsig_doc_perms = digsigfield.getDocumentPermissions();
			String[] locked_fields = digsigfield.getLockedFields();
			for (String it : locked_fields)
			{
				System.out.println("This digital signature locks a field named: " + it);
			}

			switch (digsig_doc_perms)
			{
			case e_no_changes_allowed:
				System.out.println("No changes to the document can be made without invalidating this digital signature.");
				break;
			case e_formfilling_signing_allowed:
				System.out.println("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
				break;
			case e_annotating_formfilling_signing_allowed:
				System.out.println("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.");
				break;
			case e_unrestricted:
				System.out.println("Document not restricted by this digital signature.");
				break;
			default:
				System.err.println("Unrecognized digital signature document permission level.");
				assert(false);
			}
			System.out.println("==========");
		}

		System.out.println("================================================================================");
	}

	public static void CustomSigningAPI(String doc_path,
		String cert_field_name,
		String private_key_file_path,
		String keyfile_password,
		String public_key_file_path,
		String appearance_image_path,
		DigestAlgorithm digest_algorithm_type,
		boolean PAdES_signing_mode,
		String output_path) throws PDFNetException, IOException
	{
		System.out.println("================================================================================");
		System.out.println("Custom signing PDF document");

		PDFDoc doc = new PDFDoc(doc_path);

		Page page1 = doc.getPage(1);

		DigitalSignatureField digsig_field = doc.createDigitalSignatureField(cert_field_name);
		SignatureWidget widgetAnnot = SignatureWidget.create(doc, new Rect(143, 287, 219, 306), digsig_field);
		page1.annotPushBack(widgetAnnot);

		// (OPTIONAL) Add an appearance to the signature field.
		Image img = Image.create(doc, appearance_image_path);
		widgetAnnot.createSignatureAppearance(img);

		// Create a digital signature dictionary inside the digital signature field, in preparation for signing.
		digsig_field.createSigDictForCustomSigning("Adobe.PPKLite",
			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);

		doc.save(output_path, SDFDoc.SaveMode.INCREMENTAL, null);

		// Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
		byte[] pdf_digest = digsig_field.calculateDigest(digest_algorithm_type);

		X509Certificate signer_cert = new X509Certificate(public_key_file_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, digest_algorithm_type);

		// 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(digest_algorithm_type, signedAttrs);

		//////////////////////////// custom digest signing starts ////////////////////////////
		// At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
		// which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
		byte[] signature_value = DigestAlgorithm.signDigest(
			signedAttrs_digest,
			digest_algorithm_type,
			private_key_file_path,
			keyfile_password);
		//////////////////////////// custom digest signing ends //////////////////////////////

		// Then, load all your chain certificates into a container of X509Certificate.
		X509Certificate[] chain_certs = {};

		// Then, create ObjectIdentifiers for the algorithms you have used.
		// Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
		ObjectIdentifier digest_algorithm_oid = new ObjectIdentifier(digest_algorithm_type);
		ObjectIdentifier signature_algorithm_oid = new ObjectIdentifier(ObjectIdentifier.Predefined.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, output_path);

		System.out.println("================================================================================");
	}

	public static boolean timestampAndEnableLTV(String doc_path,
		String tsa_url,
		String trusted_cert_path, 
		String appearance_img_path,
		String output_path) throws PDFNetException
	{
		PDFDoc doc = new PDFDoc(doc_path); 
		DigitalSignatureField doctimestamp_signature_field = doc.createDigitalSignatureField();  
		TimestampingConfiguration tst_config = new TimestampingConfiguration(tsa_url);
		VerificationOptions opts = new VerificationOptions(VerificationOptions.SecurityLevel.e_compatibility_and_archiving); 
		/* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
		the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
		response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
		function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
		in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
		opts.addTrustedCertificate(trusted_cert_path);
		/* By default, we only check online for revocation of certificates using the newer and lighter 
		OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
		it may be necessary to enable online CRL revocation checking in order to verify some timestamps
		(i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
		opts.enableOnlineCRLRevocationChecking(true);

		SignatureWidget widgetAnnot = SignatureWidget.create(doc, new Rect(0, 100, 200, 150), doctimestamp_signature_field);
		doc.getPage(1).annotPushBack(widgetAnnot);

		// (OPTIONAL) Add an appearance to the signature field.
		Image img = Image.create(doc, appearance_img_path);
		widgetAnnot.createSignatureAppearance(img);

		System.out.println("Testing timestamping configuration.");
		TimestampingResult config_result = tst_config.testConfiguration(opts);
		if (config_result.getStatus())
		{
			System.out.println("Success: timestamping configuration usable. Attempting to timestamp.");
		}
		else
		{
			// Print details of timestamping failure.
			System.out.println(config_result.getString());
			if (config_result.hasResponseVerificationResult())
			{
				EmbeddedTimestampVerificationResult tst_result = config_result.getResponseVerificationResult();
				System.out.println(String.format("CMS digest status: %s", tst_result.getCMSDigestStatusAsString()));
				System.out.println(String.format("Message digest status: %s", tst_result.getMessageImprintDigestStatusAsString()));
				System.out.println(String.format("Trust status: %s", tst_result.getTrustStatusAsString()));
			}
			return false;
		}

		doctimestamp_signature_field.timestampOnNextSave(tst_config, opts);

		// Save/signing throws if timestamping fails.
		doc.save(output_path, SDFDoc.SaveMode.INCREMENTAL, null);

		System.out.println("Timestamping successful. Adding LTV information for DocTimeStamp signature.");

		// Add LTV information for timestamp signature to document.
		VerificationResult timestamp_verification_result = doctimestamp_signature_field.verify(opts);
		if (!doctimestamp_signature_field.enableLTVOfflineVerification(timestamp_verification_result))
		{
			System.out.println("Could not enable LTV for DocTimeStamp.");
			return false;
		}
		doc.save(output_path, SDFDoc.SaveMode.INCREMENTAL, null);
		System.out.println("Added LTV information for DocTimeStamp signature successfully.");

		return true;
	}
	
	public static void main(String[] args) 
	{
		// Initialize PDFNet
		PDFNet.initialize(PDFTronLicense.Key());
		
		boolean result = true;
		String input_path = "../../TestFiles/";
		String output_path = "../../TestFiles/Output/";


		//////////////////// TEST 0: 
		/* Create an approval signature field that we can sign after certifying.
		(Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
		try (PDFDoc doc = new PDFDoc(input_path + "waiver.pdf"))
		{
			DigitalSignatureField approval_signature_field = doc.createDigitalSignatureField("PDFTronApprovalSig");
			SignatureWidget widgetAnnotApproval = SignatureWidget.create(doc, new Rect(300, 287, 376, 306), approval_signature_field);
			Page page1 = doc.getPage(1);
			page1.annotPushBack(widgetAnnotApproval);
			doc.save(output_path + "waiver_withApprovalField_output.pdf", SDFDoc.SaveMode.REMOVE_UNUSED, null);
		}
		catch (Exception e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace(System.err);
			result = false;
		}

		//////////////////// TEST 1: certify a PDF.
		try
		{
			certifyPDF(input_path + "waiver_withApprovalField.pdf",
				"PDFTronCertificationSig",
				input_path + "pdftron.pfx",
				"password",
				input_path + "pdftron.bmp",
				output_path + "waiver_withApprovalField_certified_output.pdf");
			printSignaturesInfo(output_path + "waiver_withApprovalField_certified_output.pdf");
		}
		catch (Exception e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace(System.err);
			result = false;
		}

		//////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
		try
		{
			signPDF(input_path + "waiver_withApprovalField_certified.pdf",
				"PDFTronApprovalSig",
				input_path + "pdftron.pfx",
				"password",
				input_path + "signature.jpg",
				output_path + "waiver_withApprovalField_certified_approved_output.pdf");
			printSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_output.pdf");
		}
		catch (Exception e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace(System.err);
			result = false;
		}

		//////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
		try
		{
			clearSignature(input_path + "waiver_withApprovalField_certified_approved.pdf",
				"PDFTronCertificationSig",
				output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
			printSignaturesInfo(output_path + "waiver_withApprovalField_certified_approved_certcleared_output.pdf");
		}
		catch (Exception e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace(System.err);
			result = false;
		}
		
		//////////////////// TEST 4: Verify a document's digital signatures.
		try
		{
			if (!verifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf",
				input_path + "pdftron.cer"))
			{
				result = false;
			}
		}
		catch (Exception e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace(System.err);
			result = false;
		}

		//////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
		try
		{
			if (!verifySimple(input_path + "waiver_withApprovalField_certified_approved.pdf",
				input_path + "pdftron.cer"))
			{
				result = false;
			}
		}
		catch (Exception e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace(System.err);
			result = false;

		}
		
		//////////////////// TEST 6: Custom signing API.
		// The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
		// which allows users to customize the process of signing documents. Among other things, this
		// includes the capability to allow for easy integration of PDF-specific signing-related operations
		// with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
		// to system keystores, etc.
		try
		{
			CustomSigningAPI(input_path + "waiver.pdf",
				"PDFTronApprovalSig",
				input_path + "pdftron.pfx",
				"password",
				input_path + "pdftron.cer",
				input_path + "signature.jpg",
				DigestAlgorithm.e_sha256,
				true,
				output_path + "waiver_custom_signed.pdf");
		}
		catch (Exception e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace(System.err);
			result = false;
		}

		//////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
		// try
		// {
		// 	// Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
		//     // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
		//     // Note that this url may not work in the future. A reliable solution requires using your own TSA.
		// 	String tsa_url = "YOUR_URL_OF_TSA";
		// 	if (tsa_url == "YOUR_URL_OF_TSA")
		// 	{
		// 		throw new Exception("Error: The URL of your timestamp authority was not specified.");
		// 	}
		//
		// 	// Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
		//     // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
		//     // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
		// 	String trusted_cert_path = "YOUR_CERTIFICATE";
		// 	if (trusted_cert_path == "YOUR_CERTIFICATE")
		// 	{
		// 		throw new Exception("Error: The path to your timestamp authority trusted root certificate was not specified.");
		// 	}
		//
		// 	if (!timestampAndEnableLTV(input_path + "waiver.pdf",
		// 	tsa_url,
		// 	trusted_cert_path,
		// 	input_path + "signature.jpg",
		// 	output_path+ "waiver_DocTimeStamp_LTV.pdf"))
		// 	{
		// 		result = false;
		// 	}
		// }
		// catch (Exception e)
		// {
		// 	System.err.println(e.getMessage());
		// 	e.printStackTrace(System.err);
		// 	result = false;
		//
		// }

		//////////////////// End of tests. ////////////////////

		if (result)
		{
			System.out.println("Tests successful.\n==========");
		}
		else
		{
			System.out.println("Tests FAILED!!!\n==========");
		}
		PDFNet.terminate();		
	}
}
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------


const { PDFNet } = require('@pdftron/pdfnet-node');
const PDFTronLicense = require('../LicenseKey/LicenseKey');

((exports) => {
  'use strict';

  exports.runDigitalSignatureTest = () => {

    const input_path = '../TestFiles/';
    const output_path = '../TestFiles/Output/';
    
    const VerifySimple = async (in_docpath, in_public_key_file_path) => {
      const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
      doc.initSecurityHandler();
      console.log('==========');
      const opts = await PDFNet.VerificationOptions.create(PDFNet.VerificationOptions.SecurityLevel.e_compatibility_and_archiving);

      // Add trust root to store of trusted certificates contained in VerificationOptions.
      await opts.addTrustedCertificateUString(in_public_key_file_path,
         PDFNet.VerificationOptions.CertificateTrustFlag.e_default_trust + PDFNet.VerificationOptions.CertificateTrustFlag.e_certification_trust);

      const result = await doc.verifySignedDigitalSignatures(opts);
      switch (result) {
        case PDFNet.PDFDoc.SignaturesVerificationStatus.e_unsigned:
          console.log('Document has no signed signature fields.');
          return false;
        /* e_failure == bad doc status, digest status, or permissions status 
        (i.e. does not include trust issues, because those are flaky due to being network/config-related) */
        case PDFNet.PDFDoc.SignaturesVerificationStatus.e_failure:
          console.log('Hard failure in verification on at least one signature.');
          return false;
        case PDFNet.PDFDoc.SignaturesVerificationStatus.e_untrusted:
          console.log('Could not verify trust for at least one signature.');
          return false;
        case PDFNet.PDFDoc.SignaturesVerificationStatus.e_unsupported:
          /* If necessary, call GetUnsupportedFeatures on VerificationResult to check which 
          unsupported features were encountered (requires verification using 'detailed' APIs) */
          console.log('At least one signature contains unsupported features.');
          return false;
        // unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
        case PDFNet.PDFDoc.SignaturesVerificationStatus.e_verified:
          console.log('All signed signatures in document verified.');
          return true;
      }

      return false;
    }

    const VerifyAllAndPrint = async (in_docpath, in_public_key_file_path) => {
      const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
      doc.initSecurityHandler();
      console.log('==========');
      const opts = await PDFNet.VerificationOptions.create(PDFNet.VerificationOptions.SecurityLevel.e_compatibility_and_archiving);

      // Add trust root to store of trusted certificates contained in VerificationOptions.
      // Use trust level corresponding to an identity trusted even for certification signatures.
      await opts.addTrustedCertificateUString(in_public_key_file_path,
         PDFNet.VerificationOptions.CertificateTrustFlag.e_default_trust + PDFNet.VerificationOptions.CertificateTrustFlag.e_certification_trust);

      // Iterate over the signatures and verify all of them.
      const digsig_fitr = await doc.getDigitalSignatureFieldIteratorBegin();
      var verification_status = true;
      for (; await digsig_fitr.hasNext(); await digsig_fitr.next()) {
        const curr = await digsig_fitr.current();
        const result = await curr.verify(opts);
        if (await result.getVerificationStatus()) {
          console.log('Signature verified, objnum: ' + await (await curr.getSDFObj()).getObjNum());
        } else {
          console.log('Signature verification failed, objnum: ' + await (await curr.getSDFObj()).getObjNum());
          verification_status = false;
        }

        switch (await result.getDigestAlgorithm()) {
          case PDFNet.DigestAlgorithm.Type.e_SHA1:
            console.log('Digest algorithm: SHA-1');
            break;
          case PDFNet.DigestAlgorithm.Type.e_SHA256:
            console.log('Digest algorithm: SHA-256');
            break;
          case PDFNet.DigestAlgorithm.Type.e_SHA384:
            console.log('Digest algorithm: SHA-384');
            break;
          case PDFNet.DigestAlgorithm.Type.e_SHA512:
            console.log('Digest algorithm: SHA-512');
            break;
          case PDFNet.DigestAlgorithm.Type.e_RIPEMD160:
            console.log('Digest algorithm: RIPEMD-160');
            break;
          case PDFNet.DigestAlgorithm.Type.e_unknown_digest_algorithm:
            console.log('Digest algorithm: unknown');
            break;
        }

        console.log('Detailed verification result: \n\t'
          + await result.getDocumentStatusAsString() + '\n\t'
          + await result.getDigestStatusAsString() + '\n\t'
          + await result.getTrustStatusAsString() + '\n\t'
          + await result.getPermissionsStatusAsString());

        const changes = await result.getDisallowedChanges();
        for (var i = 0; i < changes.length; ++i) {
          const change = changes[i];
          console.log('\tDisallowed change: ' + await change.getTypeAsString() + ', objnum: ' + await change.getObjNum());
        }

        // Get and print all the detailed trust-related results, if they are available.
        if (await result.hasTrustVerificationResult()) {
          const trust_verification_result = await result.getTrustVerificationResult();
          console.log(await trust_verification_result.wasSuccessful() ? 'Trust verified.' : 'Trust not verifiable.');
          console.log(await trust_verification_result.getResultString());

          const tmp_time_t = await trust_verification_result.getTimeOfTrustVerification();
          switch (await trust_verification_result.getTimeOfTrustVerificationEnum()) {
            case PDFNet.VerificationOptions.TimeMode.e_current:
              console.log('Trust verification attempted with respect to current time (as epoch time):' + tmp_time_t);
              break;
            case PDFNet.VerificationOptions.TimeMode.e_signing:
              console.log('Trust verification attempted with respect to signing time (as epoch time): ' + tmp_time_t);
              break;
            case PDFNet.VerificationOptions.TimeMode.e_timestamp:
              console.log('Trust verification attempted with respect to secure embedded timestamp (as epoch time): ' + tmp_time_t);
              break;
          }

          const cert_path = await trust_verification_result.getCertPath();
          if (cert_path.length == 0) {
            console.log('Could not print certificate path.');
          } else {
            console.log('Certificate path:');
            for (var i = 0; i < cert_path.length; i++) {
              console.log('\tCertificate:');
              const full_cert = cert_path[i];
              console.log('\t\tIssuer names:');
              const issuer_dn = await (await full_cert.getIssuerField()).getAllAttributesAndValues();
              for (var j = 0; j < issuer_dn.length; j++) {
                console.log('\t\t\t' + await issuer_dn[j].getStringValue());
              }
              console.log('\t\tSubject names:');
              const subject_dn = await (await full_cert.getSubjectField()).getAllAttributesAndValues();
              for (var j = 0; j < subject_dn.length; j++) {
                console.log('\t\t\t' + await subject_dn[j].getStringValue());
              }
              console.log('\t\tExtensions:');
              const extension_dn = await full_cert.getExtensions();
              for (var j = 0; j < extension_dn.length; j++) {
                console.log('\t\t\t' + await extension_dn[j].toString());
              }
            }
          }
        }
        else {
          console.log('No detailed trust verification result available.');
        }

        console.log('==========');
      }

      return verification_status;
    }

    const CertifyPDF = async (in_docpath, in_cert_field_name, in_private_key_file_path, in_keyfile_password, in_appearance_image_path, in_outpath) => {
      console.log('================================================================================');
      console.log('Certifying PDF document');

      // Open existing PDF.
      const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
      doc.initSecurityHandler();

      console.log('PDFDoc has ' + (await doc.hasSignatures() ? 'signatures' : 'no signatures'));

      const page1 = await doc.getPage(1);

      // Create a text field that we can lock using the field permissions feature.
      const annot1 = await PDFNet.TextWidget.create(doc, new PDFNet.Rect(143, 440, 350, 460), 'asdf_test_field');
      await page1.annotPushBack(annot1);

      /* Create a new signature form field in the PDFDoc. The name argument is optional;
      leaving it empty causes it to be auto-generated. However, you may need the name for later.
      Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
      Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. */
      const certification_sig_field = await doc.createDigitalSignatureField(in_cert_field_name);
      const widgetAnnot = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(143, 287, 219, 306), certification_sig_field);
      await page1.annotPushBack(widgetAnnot);

      // (OPTIONAL) Add an appearance to the signature field.
      const img = await PDFNet.Image.createFromFile(doc, in_appearance_image_path);
      await widgetAnnot.createSignatureAppearance(img);

      // Prepare the document locking permission level. It will be applied upon document certification.
      console.log('Adding document permissions.');
      await certification_sig_field.setDocumentPermissions(PDFNet.DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed);

      // Prepare to lock the text field that we created earlier.
      console.log('Adding field permissions.');
      var fields_to_lock = ['asdf_test_field'];
      await certification_sig_field.setFieldPermissions(PDFNet.DigitalSignatureField.FieldPermissions.e_include, fields_to_lock);

      await certification_sig_field.certifyOnNextSave(in_private_key_file_path, in_keyfile_password);

      // (OPTIONAL) Add more information to the signature dictionary.
      await certification_sig_field.setLocation('Vancouver, BC');
      await certification_sig_field.setReason('Document certification.');
      await certification_sig_field.setContactInfo('www.pdftron.com');

      // Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
      await doc.save(in_outpath, 0);

      console.log('================================================================================');
    }

    const SignPDF = async (in_docpath, in_approval_field_name, in_private_key_file_path, in_keyfile_password, in_appearance_img_path, in_outpath) => {
      console.log('================================================================================');
      console.log('Signing PDF document');

      // Open an existing PDF
      const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
      doc.initSecurityHandler();

      // Retrieve the unsigned approval signature field.
      const found_approval_field = await doc.getField(in_approval_field_name);
      const found_approval_signature_digsig_field = await PDFNet.DigitalSignatureField.createFromField(found_approval_field);

      // (OPTIONAL) Add an appearance to the signature field.
      const img = await PDFNet.Image.createFromFile(doc, in_appearance_img_path);
      const found_approval_signature_widget = await PDFNet.SignatureWidget.createFromObj(await found_approval_field.getSDFObj());
      await found_approval_signature_widget.createSignatureAppearance(img);

      // Prepare the signature and signature handler for signing.
      await found_approval_signature_digsig_field.signOnNextSave(in_private_key_file_path, in_keyfile_password);

      // The actual approval signing will be done during the following incremental save operation.
      await doc.save(in_outpath, PDFNet.SDFDoc.SaveOptions.e_incremental);

      console.log('================================================================================');
    }

    const ClearSignature = async (in_docpath, in_digsig_field_name, in_outpath) => {
      console.log('================================================================================');
      console.log('Clearing certification signature');

      const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
      doc.initSecurityHandler();

      const digsig = await PDFNet.DigitalSignatureField.createFromField(await doc.getField(in_digsig_field_name));

      console.log('Clearing signature: ' + in_digsig_field_name);
      await digsig.clearSignature();

      if (!(await digsig.hasCryptographicSignature())) {
        console.log('Cryptographic signature cleared properly.');
      }

      // Save incrementally so as to not invalidate other signatures from previous saves.
      await doc.save(in_outpath, PDFNet.SDFDoc.SaveOptions.e_incremental);

      console.log('================================================================================');
    }

    const PrintSignaturesInfo = async (in_docpath) => {
      console.log('================================================================================');
      console.log('Reading and printing digital signature information');

      const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
      doc.initSecurityHandler();
      if (!(await doc.hasSignatures())) {
        console.log('Doc has no signatures.');
        console.log('================================================================================');
        return;
      } else {
        console.log('Doc has signatures.');
      }


      for (const fitr = await doc.getFieldIteratorBegin(); await fitr.hasNext(); await fitr.next()) {
        const field = await fitr.current();
        (await field.isLockedByDigitalSignature()) ? console.log('==========\nField locked by a digital signature') :
          console.log('==========\nField not locked by a digital signature');

        console.log('Field name: ' + await field.getName());
        console.log('==========');
      }

      console.log('====================\nNow iterating over digital signatures only.\n====================');

      const digsig_fitr = await doc.getDigitalSignatureFieldIteratorBegin();
      for (; await digsig_fitr.hasNext(); await digsig_fitr.next()) {
        console.log('==========');
        const digsigfield = await digsig_fitr.current();
        console.log('Field name of digital signature: ' + await (await PDFNet.Field.create(await digsigfield.getSDFObj())).getName());

        if (!(await digsigfield.hasCryptographicSignature())) {
          console.log('Either digital signature field lacks a digital signature dictionary, ' +
            'or digital signature dictionary lacks a cryptographic Contents entry. ' +
            'Digital signature field is not presently considered signed.\n' +
            '==========');
          continue;
        }

        const cert_count = await digsigfield.getCertCount();
        console.log('Cert count: ' + cert_count);
        for (var i = 0; i < cert_count; i++) {
          const cert = await digsigfield.getCert(i);
          console.log('Cert #' + i + ' size: ' + cert.byteLength);
        }

        const subfilter = await digsigfield.getSubFilter();

        console.log('Subfilter type: ' + subfilter);

        if (subfilter != PDFNet.DigitalSignatureField.SubFilterType.e_ETSI_RFC3161) {
          console.log("Signature's signer: " + await digsigfield.getSignatureName());

          const signing_time = await digsigfield.getSigningTime();
          if (await signing_time.isValid()) {
            console.log('Signing time is valid.');
          }

          console.log('Location: ' + await digsigfield.getLocation());
          console.log('Reason: ' + await digsigfield.getReason());
          console.log('Contact info: ' + await digsigfield.getContactInfo());
        } else {
          console.log('SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)');
        }

        console.log((await digsigfield.hasVisibleAppearance()) ? 'Visible' : 'Not visible');

        const digsig_doc_perms = await digsigfield.getDocumentPermissions();
        const locked_fields = await digsigfield.getLockedFields();
        for (var i = 0; i < locked_fields.length; i++) {
          console.log('This digital signature locks a field named: ' + locked_fields[i]);
        }

        switch (digsig_doc_perms) {
          case PDFNet.DigitalSignatureField.DocumentPermissions.e_no_changes_allowed:
            console.log('No changes to the document can be made without invalidating this digital signature.');
            break;
          case PDFNet.DigitalSignatureField.DocumentPermissions.e_formfilling_signing_allowed:
            console.log('Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.');
            break;
          case PDFNet.DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed:
            console.log('Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.');
            break;
          case PDFNet.DigitalSignatureField.DocumentPermissions.e_unrestricted:
            console.log('Document not restricted by this digital signature.');
            break;
        }
        console.log('==========');
      }

      console.log('================================================================================');
    }

    const CustomSigningAPI = async (doc_path, cert_field_name, private_key_file_path, keyfile_password, public_key_file_path, appearance_image_path, digest_algorithm_type, PAdES_signing_mode, output_path) => {
      console.log('================================================================================');
      console.log('Custom signing PDF document');

      const doc = await PDFNet.PDFDoc.createFromFilePath(doc_path);
      doc.initSecurityHandler();

      const page1 = await doc.getPage(1);

      const digsig_field = await doc.createDigitalSignatureField(cert_field_name);
      const widgetAnnot = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(143, 287, 219, 306), digsig_field);
      await page1.annotPushBack(widgetAnnot);

      // (OPTIONAL) Add an appearance to the signature field.
      const img = await PDFNet.Image.createFromFile(doc, appearance_image_path);
      await widgetAnnot.createSignatureAppearance(img);

      // Create a digital signature dictionary inside the digital signature field, in preparation for signing.
      await digsig_field.createSigDictForCustomSigning('Adobe.PPKLite',
        PAdES_signing_mode ? PDFNet.DigitalSignatureField.SubFilterType.e_ETSI_CAdES_detached : PDFNet.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.
      const current_date = new PDFNet.Date();
      await current_date.setCurrentTime();
      await digsig_field.setSigDictTimeOfSigning(current_date);

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

      // Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
      const pdf_digest = await digsig_field.calculateDigest(digest_algorithm_type);

      const signer_cert = await PDFNet.X509Certificate.createFromFile(public_key_file_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.
      const pades_versioned_ess_signing_cert_attribute = await PDFNet.DigitalSignatureField.generateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type);

      // 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.
      const signedAttrs = await PDFNet.DigitalSignatureField.generateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute);

      // Calculate the digest of the signedAttrs (i.e. not the PDF digest, this time).
      const signedAttrs_digest = await PDFNet.DigestAlgorithm.calculateDigest(digest_algorithm_type, signedAttrs);

      //////////////////////////// custom digest signing starts ////////////////////////////
      // At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
      // which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
      const signature_value = await PDFNet.DigestAlgorithm.signDigest(
        signedAttrs_digest,
        digest_algorithm_type,
        private_key_file_path,
        keyfile_password);
      //////////////////////////// custom digest signing ends //////////////////////////////

      // Then, load all your chain certificates into a container of X509Certificate.
      const chain_certs = [];

      // Then, create ObjectIdentifiers for the algorithms you have used.
      // Here we use digest_algorithm_type (usually SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
      const digest_algorithm_oid = await PDFNet.ObjectIdentifier.createFromDigestAlgorithm(digest_algorithm_type);
      const signature_algorithm_oid = await PDFNet.ObjectIdentifier.createFromPredefined(PDFNet.ObjectIdentifier.Predefined.e_RSA_encryption_PKCS1);

      // Then, put the CMS signature components together.
      const cms_signature = await PDFNet.DigitalSignatureField.generateCMSSignature(
        signer_cert, chain_certs, digest_algorithm_oid, signature_algorithm_oid, signature_value, signedAttrs);

      // Write the signature to the document.
      await doc.saveCustomSignature(cms_signature, digsig_field, output_path);

      console.log('================================================================================');
    };

    const TimestampAndEnableLTV = async (in_docpath, in_tsa_url, in_trusted_cert_path, in_appearance_img_path, in_outpath) => {
      const doc = await PDFNet.PDFDoc.createFromFilePath(in_docpath);
      doc.initSecurityHandler();
      const doctimestamp_signature_field = await doc.createDigitalSignatureField();
      const tst_config = await PDFNet.TimestampingConfiguration.createFromURL(in_tsa_url);
      const opts = await PDFNet.VerificationOptions.create(PDFNet.VerificationOptions.SecurityLevel.e_compatibility_and_archiving);
      /* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
      the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
      response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
      function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
      in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
      await opts.addTrustedCertificateUString(in_trusted_cert_path);
      /* By default, we only check online for revocation of certificates using the newer and lighter 
      OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
      it may be necessary to enable online CRL revocation checking in order to verify some timestamps
      (i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
      await opts.enableOnlineCRLRevocationChecking(true);

      const widgetAnnot = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(0, 100, 200, 150), doctimestamp_signature_field);
      await (await doc.getPage(1)).annotPushBack(widgetAnnot);

      // (OPTIONAL) Add an appearance to the signature field.
      const img = await PDFNet.Image.createFromFile(doc, in_appearance_img_path);
      await widgetAnnot.createSignatureAppearance(img);

      console.log('Testing timestamping configuration.');
      const config_result = await tst_config.testConfiguration(opts);
      if (await config_result.getStatus()) {
        console.log('Success: timestamping configuration usable. Attempting to timestamp.');
      } else {
        // Print details of timestamping failure.
        console.log(await config_result.getString());
        if (await config_result.hasResponseVerificationResult()) {
          const tst_result = await config_result.getResponseVerificationResult();
          console.log('CMS digest status: ' + await tst_result.getCMSDigestStatusAsString());
          console.log('Message digest status: ' + await tst_result.getMessageImprintDigestStatusAsString());
          console.log('Trust status: ' + await tst_result.getTrustStatusAsString());
        }
        return false;
      }

      await doctimestamp_signature_field.timestampOnNextSave(tst_config, opts);

      // Save/signing throws if timestamping fails.
      await doc.save(in_outpath, PDFNet.SDFDoc.SaveOptions.e_incremental);

      console.log('Timestamping successful. Adding LTV information for DocTimeStamp signature.');

      // Add LTV information for timestamp signature to document.
      const timestamp_verification_result = await doctimestamp_signature_field.verify(opts);
      if (!(await doctimestamp_signature_field.enableLTVOfflineVerification(timestamp_verification_result))) {
        console.log('Could not enable LTV for DocTimeStamp.');
        return false;
      }
      await doc.save(in_outpath, PDFNet.SDFDoc.SaveOptions.e_incremental);
      console.log('Added LTV information for DocTimeStamp signature successfully.');

      return true;
    }

    const main = async () => {
      var ret = 0;

      //////////////////// TEST 0: 
      /* Create an approval signature field that we can sign after certifying.
      (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.) */
      try {
        const doc = await PDFNet.PDFDoc.createFromFilePath(input_path + 'waiver.pdf');
        doc.initSecurityHandler();
        const approval_signature_field = await doc.createDigitalSignatureField('PDFTronApprovalSig');
        const widgetAnnotApproval = await PDFNet.SignatureWidget.createWithDigitalSignatureField(doc, new PDFNet.Rect(300, 287, 376, 306), approval_signature_field);
        const page1 = await doc.getPage(1);
        await page1.annotPushBack(widgetAnnotApproval);
        await doc.save(output_path + 'waiver_withApprovalField_output.pdf', PDFNet.SDFDoc.SaveOptions.e_remove_unused);
      } catch (err) {
        console.log(err);
        ret = 1;
      }

      //////////////////// TEST 1: certify a PDF.
      try {
        await CertifyPDF(input_path + 'waiver_withApprovalField.pdf',
        'PDFTronCertificationSig',
        input_path + 'pdftron.pfx',
        'password',
        input_path + 'pdftron.bmp',
        output_path + 'waiver_withApprovalField_certified_output.pdf');
        await PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_output.pdf');
      } catch (err) {
        console.log(err);
        ret = 1;
      }

      //////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
      try {
        await SignPDF(input_path + 'waiver_withApprovalField_certified.pdf',
        'PDFTronApprovalSig',
        input_path + 'pdftron.pfx',
        'password',
        input_path + 'signature.jpg',
        output_path + 'waiver_withApprovalField_certified_approved_output.pdf');
        await PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_output.pdf');
      } catch (err) {
        console.log(err);
        ret = 1;
      }

      //////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
      try {
        await ClearSignature(input_path + 'waiver_withApprovalField_certified_approved.pdf',
        'PDFTronCertificationSig',
        output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
        await PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
      } catch (err) {
        console.log(err);
        ret = 1;
      }

      //////////////////// TEST 4: Verify a document's digital signatures.
      // EXPERIMENTAL. Digital signature verification is undergoing active development, but currently does not support a number of features. If we are missing a feature that is important to you, or if you have files that do not act as expected, please contact us using one of the following forms: https://apryse.com/form/trial-support or https://apryse.com/form/request
      try {
        if (!(await VerifyAllAndPrint(input_path + 'waiver_withApprovalField_certified_approved.pdf', input_path + 'pdftron.cer'))) {
          ret = 1;
        }
      } catch (err) {
        console.log(err);
        ret = 1;
      }

      //////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
      try {
        if (!(await VerifySimple(input_path + 'waiver_withApprovalField_certified_approved.pdf', input_path + 'pdftron.cer'))) {
          ret = 1;
        }
      } catch (err) {
        console.log(err);
        ret = 1;
      }

      //////////////////// TEST 6: Custom signing API.
      // The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
      // which allows users to customize the process of signing documents. Among other things, this
      // includes the capability to allow for easy integration of PDF-specific signing-related operations
      // with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
      // to system keystores, etc.
      try {
        await CustomSigningAPI(input_path + 'waiver.pdf',
          'PDFTronCertificationSig',
          input_path + 'pdftron.pfx',
          'password',
          input_path + 'pdftron.cer',
          input_path + 'signature.jpg',
          PDFNet.DigestAlgorithm.Type.e_SHA256,
          true,
          output_path + 'waiver_custom_signed.pdf');
      } catch (err) {
        console.log(err);
        ret = 1;
      }

      //////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
      // try {
      //   // Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
      //   // For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
      //   // Note that this url may not work in the future. A reliable solution requires using your own TSA.
      //   const tsa_url = 'YOUR_URL_OF_TSA';
      //   if (tsa_url == 'YOUR_URL_OF_TSA')
      //   {
      //     throw new Error('The URL of your timestamp authority was not specified.');
      //   }
      //
      //   // Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
      //   // For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
      //   // Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
      //   const trusted_cert_path = 'YOUR_CERTIFICATE';
      //   if (trusted_cert_path == 'YOUR_CERTIFICATE')
      //   {
      //     throw new Error('The path to your timestamp authority trusted root certificate was not specified.');
      //   }
      //
      //   if (!(await TimestampAndEnableLTV(input_path + 'waiver.pdf',
      //   tsa_url,
      //   trusted_cert_path,
      //   input_path + 'signature.jpg',
      //   output_path + 'waiver_DocTimeStamp_LTV.pdf'))) {
      //     ret = 1;
      //   }
      // } catch (err) {
      //   console.log(err);
      //   ret = 1;
      // }

      //////////////////// End of tests. ////////////////////
      if (!ret) {
        console.log('Tests successful.\n==========');
      }
      else {
        console.log('Tests FAILED!!!\n==========');
      }
    };

    PDFNet.runWithCleanup(main, PDFTronLicense.Key).catch(function (error) {
      console.log('Error: ' + JSON.stringify(error));
    }).then(function () { return PDFNet.shutdown(); });
  };
  exports.runDigitalSignatureTest();
})(exports);
// eslint-disable-next-line spaced-comment
//# sourceURL=DigitalSignatureTest.js
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
#!/usr/bin/python

#-----------------------------------------------------------------------------------------------------------------------
# Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
# Consult LICENSE.txt regarding license information.
#-----------------------------------------------------------------------------------------------------------------------

##----------------------------------------------------------------------------------------------------------------------
## This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
##
## The following steps reflect typical intended usage of the digital signatures API:
##
##	0.	Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
##	
##	1.	EITHER: 
##		(a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
##		-OR-
##		(b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
##		document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
##	
##	2.	Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
##		If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
##		NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
##		
##	[3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
##		or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
##		certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
##		is signed.]
##		
##	4. 	Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
##		a.	Taking a PKCS #12 keyfile path and its password
##		b.	Taking a buffer containing a PKCS #12 private keyfile and its password
##		c.	Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
##			in the following fashion: 
##			i)		Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
##					validate/check a digital signature.
##			ii)		Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
##					pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
##			iii)	Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
##		NOTE: It is only possible to sign/certify one signature per call to the Save function.
##	
##	5.	Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
##		IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
##		so as to not invalidate the other signature(s). 
##
## Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
## of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
## add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
##
## Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
## StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
##----------------------------------------------------------------------------------------------------------------------


import site
site.addsitedir("../../../PDFNetC/Lib")
import sys
from PDFNetPython import *

sys.path.append("../../LicenseKey/PYTHON")
from LicenseKey import *

def VerifySimple(in_docpath, in_public_key_file_path):
	doc = PDFDoc(in_docpath)
	print("==========")
	opts = VerificationOptions(VerificationOptions.e_compatibility_and_archiving)

	# Add trust root to store of trusted certificates contained in VerificationOptions.
	opts.AddTrustedCertificate(in_public_key_file_path, VerificationOptions.e_default_trust | VerificationOptions.e_certification_trust)

	result = doc.VerifySignedDigitalSignatures(opts)
		
	if result is PDFDoc.e_unsigned:
		print("Document has no signed signature fields.")
		return False
		# e_failure == bad doc status, digest status, or permissions status
		# (i.e. does not include trust issues, because those are flaky due to being network/config-related)
	elif result is PDFDoc.e_failure:
		print("Hard failure in verification on at least one signature.")
		return False
	elif result is PDFDoc.e_untrusted:
		print("Could not verify trust for at least one signature.")
		return False
	elif result is PDFDoc.e_unsupported:
		# If necessary, call GetUnsupportedFeatures on VerificationResult to check which
		# unsupported features were encountered (requires verification using 'detailed' APIs)
		print("At least one signature contains unsupported features.")
		return False
		# unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
	elif result is PDFDoc.e_verified:
		print("All signed signatures in document verified.")
		return True
	else:
		print("unrecognized document verification status")
		assert False, "unrecognized document status"


def VerifyAllAndPrint(in_docpath, in_public_key_file_path):
	doc = PDFDoc(in_docpath)
	print("==========")
	opts = VerificationOptions(VerificationOptions.e_compatibility_and_archiving)
	
	# Trust the public certificate we use for signing.
	trusted_cert_buf = []
	trusted_cert_file = MappedFile(in_public_key_file_path)
	file_sz = trusted_cert_file.FileSize()
	file_reader = FilterReader(trusted_cert_file)
	trusted_cert_buf = file_reader.Read(file_sz)
	opts.AddTrustedCertificate(trusted_cert_buf, len(trusted_cert_buf), VerificationOptions.e_default_trust | VerificationOptions.e_certification_trust)

	# Iterate over the signatures and verify all of them.
	digsig_fitr = doc.GetDigitalSignatureFieldIterator()
	verification_status = True
	while (digsig_fitr.HasNext()):
		curr = digsig_fitr.Current()
		result = curr.Verify(opts)
		if result.GetVerificationStatus():
			print("Signature verified, objnum: %lu" % curr.GetSDFObj().GetObjNum())
		else:
			print("Signature verification failed, objnum: %lu" % curr.GetSDFObj().GetObjNum())
			verification_status = False

		digest_algorithm = result.GetDigestAlgorithm()
		if digest_algorithm is DigestAlgorithm.e_SHA1:
			print("Digest algorithm: SHA-1")
		elif digest_algorithm is DigestAlgorithm.e_SHA256:
			print("Digest algorithm: SHA-256")
		elif digest_algorithm is DigestAlgorithm.e_SHA384:
			print("Digest algorithm: SHA-384")
		elif digest_algorithm is DigestAlgorithm.e_SHA512:
			print("Digest algorithm: SHA-512")
		elif digest_algorithm is DigestAlgorithm.e_RIPEMD160:
			print("Digest algorithm: RIPEMD-160")
		elif digest_algorithm is DigestAlgorithm.e_unknown_digest_algorithm:
			print("Digest algorithm: unknown")
		else:
			assert False, "unrecognized document status"

		print("Detailed verification result: \n\t%s\n\t%s\n\t%s\n\t%s" % ( 
			result.GetDocumentStatusAsString(),
			result.GetDigestStatusAsString(),
			result.GetTrustStatusAsString(),
			result.GetPermissionsStatusAsString()))
			
		changes = result.GetDisallowedChanges()
		for it2 in changes:
			print("\tDisallowed change: %s, objnum: %lu" % (it2.GetTypeAsString(), it2.GetObjNum()))
		
		# Get and print all the detailed trust-related results, if they are available.
		if result.HasTrustVerificationResult():
			trust_verification_result = result.GetTrustVerificationResult()
			print("Trust verified." if trust_verification_result.WasSuccessful() else "Trust not verifiable.")
			print(trust_verification_result.GetResultString())
			
			tmp_time_t = trust_verification_result.GetTimeOfTrustVerification()
			
			trust_verification_time_enum = trust_verification_result.GetTimeOfTrustVerificationEnum()
			
			if trust_verification_time_enum is VerificationOptions.e_current:
				print("Trust verification attempted with respect to current time (as epoch time): " + str(tmp_time_t))
			elif trust_verification_time_enum is VerificationOptions.e_signing:
				print("Trust verification attempted with respect to signing time (as epoch time): " + str(tmp_time_t))
			elif trust_verification_time_enum is VerificationOptions.e_timestamp:
				print("Trust verification attempted with respect to secure embedded timestamp (as epoch time): " + str(tmp_time_t))
			else:
				assert False, "unrecognized time enum value"

			if not trust_verification_result.GetCertPath():
				print("Could not print certificate path.")
			else:
				print("Certificate path:")
				cert_path = trust_verification_result.GetCertPath()
				for j in range(len(cert_path)):
					print("\tCertificate:")
					full_cert = cert_path[j]
					print("\t\tIssuer names:")
					issuer_dn  = full_cert.GetIssuerField().GetAllAttributesAndValues()
					for i in range(len(issuer_dn)):  
						print("\t\t\t" + issuer_dn[i].GetStringValue())

					print("\t\tSubject names:")
					subject_dn = full_cert.GetSubjectField().GetAllAttributesAndValues()
					for s in subject_dn:
						print("\t\t\t" + s.GetStringValue())

					print("\t\tExtensions:")
					for x in full_cert.GetExtensions():
						print("\t\t\t" + x.ToString())
					
		else:
			print("No detailed trust verification result available.")
		
			unsupported_features = result.GetUnsupportedFeatures()
			if not unsupported_features:
				print("Unsupported features:")
				for unsupported_feature in unsupported_features:
					print("\t" + unsupported_feature)
		print("==========")
		
		digsig_fitr.Next()
	return verification_status

def CertifyPDF(in_docpath,
	in_cert_field_name,
	in_private_key_file_path,
	in_keyfile_password,
	in_appearance_image_path,
	in_outpath):
	
	print('================================================================================')
	print('Certifying PDF document')

	# Open an existing PDF
	doc = PDFDoc(in_docpath)

	if doc.HasSignatures():
		print('PDFDoc has signatures')
	else:
		print('PDFDoc has no signatures')

	page1 = doc.GetPage(1)

	# Create a text field that we can lock using the field permissions feature.
	annot1 = TextWidget.Create(doc, Rect(143, 440, 350, 460), "asdf_test_field")
	page1.AnnotPushBack(annot1)

	# Create a new signature form field in the PDFDoc. The name argument is optional;
	# leaving it empty causes it to be auto-generated. However, you may need the name for later.
	# Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
	# Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. 
	certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name)
	widgetAnnot = SignatureWidget.Create(doc, Rect(143, 287, 219, 306), certification_sig_field)
	page1.AnnotPushBack(widgetAnnot)

	# (OPTIONAL) Add an appearance to the signature field.
	img = Image.Create(doc.GetSDFDoc(), in_appearance_image_path)
	widgetAnnot.CreateSignatureAppearance(img)

	# Add permissions. Lock the random text field.
	print('Adding document permissions.')
	certification_sig_field.SetDocumentPermissions(DigitalSignatureField.e_annotating_formfilling_signing_allowed)
	
	# Prepare to lock the text field that we created earlier.
	print('Adding field permissions.')
	certification_sig_field.SetFieldPermissions(DigitalSignatureField.e_include, ['asdf_test_field'])

	certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password)

	# (OPTIONAL) Add more information to the signature dictionary.
	certification_sig_field.SetLocation('Vancouver, BC')
	certification_sig_field.SetReason('Document certification.')
	certification_sig_field.SetContactInfo('www.pdftron.com')

	# Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
	doc.Save(in_outpath, 0)

	print('================================================================================')

def SignPDF(in_docpath,	
	in_approval_field_name,	
	in_private_key_file_path, 
	in_keyfile_password, 
	in_appearance_img_path, 
	in_outpath):
	
	print('================================================================================')
	print('Signing PDF document')

	# Open an existing PDF
	doc = PDFDoc(in_docpath)

	# Retrieve the unsigned approval signature field.
	found_approval_field = doc.GetField(in_approval_field_name)
	found_approval_signature_digsig_field = DigitalSignatureField(found_approval_field)
	
	# (OPTIONAL) Add an appearance to the signature field.
	img = Image.Create(doc.GetSDFDoc(), in_appearance_img_path)
	found_approval_signature_widget = SignatureWidget(found_approval_field.GetSDFObj())
	found_approval_signature_widget.CreateSignatureAppearance(img)

	# Prepare the signature and signature handler for signing.
	found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password)

	# The actual approval signing will be done during the following incremental save operation.
	doc.Save(in_outpath, SDFDoc.e_incremental)

	print('================================================================================')

def ClearSignature(in_docpath,
	in_digsig_field_name,
	in_outpath):

	print('================================================================================')
	print('Clearing certification signature')

	doc = PDFDoc(in_docpath)

	digsig = DigitalSignatureField(doc.GetField(in_digsig_field_name))
	
	print('Clearing signature: ' + in_digsig_field_name)
	digsig.ClearSignature()

	if not digsig.HasCryptographicSignature():
		print('Cryptographic signature cleared properly.')

	# Save incrementally so as to not invalidate other signatures from previous saves.
	doc.Save(in_outpath, SDFDoc.e_incremental)

	print('================================================================================')

def PrintSignaturesInfo(in_docpath):
	print('================================================================================')
	print('Reading and printing digital signature information')

	doc = PDFDoc(in_docpath)
	if not doc.HasSignatures():
		print('Doc has no signatures.')
		print('================================================================================')
		return
	else:
		print('Doc has signatures.')

	fitr = doc.GetFieldIterator()
	while fitr.HasNext():
		current = fitr.Current()
		if (current.IsLockedByDigitalSignature()):
			print("==========\nField locked by a digital signature")
		else:
			print("==========\nField not locked by a digital signature")

		print('Field name: ' + current.GetName())
		print('==========')
		
		fitr.Next()

	print("====================\nNow iterating over digital signatures only.\n====================")

	digsig_fitr = doc.GetDigitalSignatureFieldIterator()
	while digsig_fitr.HasNext():
		current = digsig_fitr.Current()
		print('==========')
		print('Field name of digital signature: ' + Field(current.GetSDFObj()).GetName())

		digsigfield = current
		if not digsigfield.HasCryptographicSignature():
			print("Either digital signature field lacks a digital signature dictionary, " +
				"or digital signature dictionary lacks a cryptographic Contents entry. " +
				"Digital signature field is not presently considered signed.\n" +
				"==========")
			digsig_fitr.Next()
			continue

		cert_count = digsigfield.GetCertCount()
		print('Cert count: ' + str(cert_count))
		for i in range(cert_count):
			cert = digsigfield.GetCert(i)
			print('Cert #' + i + ' size: ' + cert.length)

		subfilter = digsigfield.GetSubFilter()

		print('Subfilter type: ' + str(subfilter))

		if subfilter is not DigitalSignatureField.e_ETSI_RFC3161:
			print('Signature\'s signer: ' + digsigfield.GetSignatureName())

			signing_time = digsigfield.GetSigningTime()
			if signing_time.IsValid():
				print('Signing time is valid.')

			print('Location: ' + digsigfield.GetLocation())
			print('Reason: ' + digsigfield.GetReason())
			print('Contact info: ' + digsigfield.GetContactInfo())
		else:
			print('SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)')

		if digsigfield.HasVisibleAppearance():
			print('Visible')
		else:
			print('Not visible')

		digsig_doc_perms = digsigfield.GetDocumentPermissions()
		locked_fields = digsigfield.GetLockedFields()
		for it in locked_fields:
			print('This digital signature locks a field named: ' + it)

		if digsig_doc_perms is DigitalSignatureField.e_no_changes_allowed:
			print('No changes to the document can be made without invalidating this digital signature.')
		elif digsig_doc_perms is DigitalSignatureField.e_formfilling_signing_allowed:
			print('Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.')
		elif digsig_doc_perms is DigitalSignatureField.e_annotating_formfilling_signing_allowed:
			print('Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.')
		elif digsig_doc_perms is DigitalSignatureField.e_unrestricted:
			print('Document not restricted by this digital signature.')
		else:
			print('Unrecognized digital signature document permission level.')
			assert(False)
		print('==========')
		digsig_fitr.Next()

	print('================================================================================')

def CustomSigningAPI(doc_path,
		cert_field_name,
		private_key_file_path,
		keyfile_password,
		public_key_file_path,
		appearance_image_path,
		digest_algorithm_type,
		PAdES_signing_mode,
		output_path):
	print('================================================================================')
	print('Custom signing PDF document')

	doc = PDFDoc(doc_path)

	page1 = doc.GetPage(1)

	digsig_field = doc.CreateDigitalSignatureField(cert_field_name)
	widgetAnnot = SignatureWidget.Create(doc, Rect(143, 287, 219, 306), digsig_field)
	page1.AnnotPushBack(widgetAnnot)

	# (OPTIONAL) Add an appearance to the signature field.
	img = Image.Create(doc.GetSDFDoc(), appearance_image_path)
	widgetAnnot.CreateSignatureAppearance(img)

	# Create a digital signature dictionary inside the digital signature field, in preparation for signing.
	digsig_field.CreateSigDictForCustomSigning('Adobe.PPKLite',
		DigitalSignatureField.e_ETSI_CAdES_detached if PAdES_signing_mode else DigitalSignatureField.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.
	current_date = Date()
	current_date.SetCurrentTime()
	digsig_field.SetSigDictTimeOfSigning(current_date)

	doc.Save(output_path, SDFDoc.e_incremental)

	# Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
	pdf_digest = digsig_field.CalculateDigest(digest_algorithm_type)

	signer_cert = X509Certificate(public_key_file_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.
	pades_versioned_ess_signing_cert_attribute = DigitalSignatureField.GenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type)

	# 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.
	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).
	signedAttrs_digest = DigestAlgorithm.CalculateDigest(digest_algorithm_type, signedAttrs)

	############################ custom digest signing starts ############################
	# At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
	# which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
	signature_value = DigestAlgorithm.SignDigest(
		signedAttrs_digest,
		digest_algorithm_type,
		private_key_file_path,
		keyfile_password)
	############################ custom digest signing ends ##############################

	# Then, load all your chain certificates into a container of X509Certificate.
	chain_certs = []

	# Then, create ObjectIdentifiers for the algorithms you have used.
	# Here we use digest_algorithm_type (SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
	digest_algorithm_oid = ObjectIdentifier(ObjectIdentifier.e_SHA256)
	signature_algorithm_oid = ObjectIdentifier(ObjectIdentifier.e_RSA_encryption_PKCS1)

	# Then, put the CMS signature components together.
	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, output_path)

	print('================================================================================')

def TimestampAndEnableLTV(in_docpath, 
	in_tsa_url,
	in_trusted_cert_path, 
	in_appearance_img_path,
	in_outpath):
	doc = PDFDoc(in_docpath)
	doctimestamp_signature_field = doc.CreateDigitalSignatureField()
	tst_config = TimestampingConfiguration(in_tsa_url)
	opts = VerificationOptions(VerificationOptions.e_compatibility_and_archiving)
#	It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
#	the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
#	response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
#	function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
#	in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part.

	opts.AddTrustedCertificate(in_trusted_cert_path)
#   	By default, we only check online for revocation of certificates using the newer and lighter 
#	OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
#	it may be necessary to enable online CRL revocation checking in order to verify some timestamps
#	(i.e. those that do not have an OCSP responder URL for all non-trusted certificates).

	opts.EnableOnlineCRLRevocationChecking(True)

	widgetAnnot = SignatureWidget.Create(doc, Rect(0.0, 100.0, 200.0, 150.0), doctimestamp_signature_field)
	doc.GetPage(1).AnnotPushBack(widgetAnnot)

	# (OPTIONAL) Add an appearance to the signature field.
	img = Image.Create(doc.GetSDFDoc(), in_appearance_img_path)
	widgetAnnot.CreateSignatureAppearance(img)

	print('Testing timestamping configuration.')
	config_result = tst_config.TestConfiguration(opts)
	if config_result.GetStatus():
		print('Success: timestamping configuration usable. Attempting to timestamp.')
	else:
		# Print details of timestamping failure.
		print(config_result.GetString())
		if config_result.HasResponseVerificationResult():
			tst_result = config_result.GetResponseVerificationResult()
			print('CMS digest status: '+ tst_result.GetCMSDigestStatusAsString())
			print('Message digest status: ' + tst_result.GetMessageImprintDigestStatusAsString())
			print('Trust status: ' + tst_result.GetTrustStatusAsString())
		return False
	

	doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts)

	# Save/signing throws if timestamping fails.
	doc.Save(in_outpath, SDFDoc.e_incremental)

	print('Timestamping successful. Adding LTV information for DocTimeStamp signature.')

	# Add LTV information for timestamp signature to document.
	timestamp_verification_result = doctimestamp_signature_field.Verify(opts)
	if not doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result):
		print('Could not enable LTV for DocTimeStamp.')
		return False
	doc.Save(in_outpath, SDFDoc.e_incremental)
	print('Added LTV information for DocTimeStamp signature successfully.')

	return True

def main():
	# Initialize PDFNet
	PDFNet.Initialize(LicenseKey)
	
	result = True
	input_path = '../../TestFiles/'
	output_path = '../../TestFiles/Output/'
	
	#################### TEST 0:
	# Create an approval signature field that we can sign after certifying.
	# (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
	# Open an existing PDF
	try:
		doc = PDFDoc(input_path + 'waiver.pdf')
		
		widgetAnnotApproval = SignatureWidget.Create(doc, Rect(300, 287, 376, 306), 'PDFTronApprovalSig')
		page1 = doc.GetPage(1)
		page1.AnnotPushBack(widgetAnnotApproval)
		doc.Save(output_path + 'waiver_withApprovalField_output.pdf', SDFDoc.e_remove_unused)
	except Exception as e:
		print(e.args)
		result = False
	#################### TEST 1: certify a PDF.
	try:
		CertifyPDF(input_path + 'waiver_withApprovalField.pdf',
			'PDFTronCertificationSig',
			input_path + 'pdftron.pfx',
			'password',
			input_path + 'pdftron.bmp',
			output_path + 'waiver_withApprovalField_certified_output.pdf')
		PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_output.pdf')
	except Exception as e:
		print(e.args)
		result = False
	#################### TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
	try:
		SignPDF(input_path + 'waiver_withApprovalField_certified.pdf',
			'PDFTronApprovalSig',
			input_path + 'pdftron.pfx',
			'password',
			input_path + 'signature.jpg',
			output_path + 'waiver_withApprovalField_certified_approved_output.pdf')
		PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_output.pdf')
	except Exception as e:
		print(e.args)
		result = False
	#################### TEST 3: Clear a certification from a document that is certified and has an approval signature.
	try:
		ClearSignature(input_path + 'waiver_withApprovalField_certified_approved.pdf',
			'PDFTronCertificationSig',
			output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf')
		PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf')
	except Exception as e:
		print(e.args)
		result = False

	#################### TEST 4: Verify a document's digital signatures.
	try:
		if not VerifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer"):
			result = False
	except Exception as e:
		print(e.args)
		result = False

	#################### TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
	try:
		if not VerifySimple(input_path + 'waiver_withApprovalField_certified_approved.pdf', input_path + 'pdftron.cer'):
			result = False
	except Exception as e:
		print(e.args)
		result = False

	#################### TEST 6: Custom signing API.
	# The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
	# which allows users to customize the process of signing documents. Among other things, this
	# includes the capability to allow for easy integration of PDF-specific signing-related operations
	# with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
	# to system keystores, etc.
	try:
		CustomSigningAPI(input_path + "waiver.pdf",
			"PDFTronApprovalSig",
			input_path + "pdftron.pfx",
			"password",
			input_path + "pdftron.cer",
			input_path + "signature.jpg",
			DigestAlgorithm.e_SHA256,
			True,
			output_path + "waiver_custom_signed.pdf")
	except Exception as e:
		print(e.args)
		result = False

	#################### TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
	# try:
	# 	# Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
	# 	# For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
	# 	# Note that this url may not work in the future. A reliable solution requires using your own TSA.
	# 	tsa_url = 'YOUR_URL_OF_TSA'
	# 	if tsa_url == 'YOUR_URL_OF_TSA':
	# 		raise Exception('Error: The URL of your timestamp authority was not specified.')
	#
	# 	# Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
	# 	# For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
	# 	# Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
	# 	trusted_cert_path = 'YOUR_CERTIFICATE'
	# 	if trusted_cert_path == 'YOUR_CERTIFICATE':
	# 		raise Exception('Error: The path to your timestamp authority trusted root certificate was not specified.')
	#
	# 	if not TimestampAndEnableLTV(input_path + 'waiver.pdf',
	# 		tsa_url,
	# 		trusted_cert_path,
	# 		input_path + 'signature.jpg',
	# 		output_path+ 'waiver_DocTimeStamp_LTV.pdf'):
	# 		result = False
	# except Exception as e:
	# 	print(e.args)
	# 	result = False
	
	#################### End of tests. #####################

	if not result:
		print("Tests FAILED!!!\n==========")
		PDFNet.Terminate()
		return
	PDFNet.Terminate()
	print("Tests successful.\n==========")

if __name__ == '__main__':
	main()
# end if __name__ == '__main__'
```

{% endcode %}
{% endtab %}

{% tab title="Ruby" %}
{% code lineNumbers="true" %}

```ruby
#!/usr/bin/ruby

#-----------------------------------------------------------------------------------------------------------------------
# Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
# Consult LICENSE.txt regarding license information.
#-----------------------------------------------------------------------------------------------------------------------

##----------------------------------------------------------------------------------------------------------------------
## This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
##
## The following steps reflect typical intended usage of the digital signatures API:
##
##	0.	Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
##	
##	1.	EITHER: 
##		(a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
##		-OR-
##		(b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
##		document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
##	
##	2.	Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
##		If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
##		NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
##		
##	[3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
##		or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
##		certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
##		is signed.]
##		
##	4. 	Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
##		a.	Taking a PKCS #12 keyfile path and its password
##		b.	Taking a buffer containing a PKCS #12 private keyfile and its password
##		c.	Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
##			in the following fashion: 
##			i)		Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
##					validate/check a digital signature.
##			ii)		Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
##					pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
##			iii)	Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
##		NOTE: It is only possible to sign/certify one signature per call to the Save function.
##	
##	5.	Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
##		IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
##		so as to not invalidate the other signature(s). 
##
## Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
## of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
## add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
##
## Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
## StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
##----------------------------------------------------------------------------------------------------------------------

require '../../../PDFNetC/Lib/PDFNetRuby'
require '../../LicenseKey/RUBY/LicenseKey'

include PDFNetRuby

$stdout.sync = true
def VerifySimple(in_docpath, in_public_key_file_path)
	doc = PDFDoc.new(in_docpath)
	puts("==========")
	opts = VerificationOptions.new(VerificationOptions::E_compatibility_and_archiving)

	# Add trust root to store of trusted certificates contained in VerificationOptions.
	opts.AddTrustedCertificate(in_public_key_file_path, VerificationOptions::E_default_trust | VerificationOptions::E_certification_trust)

	result = doc.VerifySignedDigitalSignatures(opts)
	case result	
	when PDFDoc::E_unsigned
		puts("Document has no signed signature fields.")
		return false
		# e_failure == bad doc status, digest status, or permissions status
		# (i.e. does not include trust issues, because those are flaky due to being network/config-related)
	when PDFDoc::E_failure
		puts("Hard failure in verification on at least one signature.")
		return false
	when PDFDoc::E_untrusted
		puts("Could not verify trust for at least one signature.")
		return false
	when PDFDoc::E_unsupported
		# If necessary, call GetUnsupportedFeatures on VerificationResult to check which
		# unsupported features were encountered (requires verification using 'detailed' APIs)
		puts("At least one signature contains unsupported features.")
		return false
		# unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
	when PDFDoc::E_verified
		puts("All signed signatures in document verified.")
		return true
	else
		puts("unrecognized document verification status")
		assert(false)
	end
end # VerifySimple()
	
def VerifyAllAndPrint(in_docpath, in_public_key_file_path)
	doc = PDFDoc.new(in_docpath)
	puts("==========")
	opts = VerificationOptions.new(VerificationOptions::E_compatibility_and_archiving)
	
	# Trust the public certificate we use for signing.
	trusted_cert_buf = []
	trusted_cert_file = MappedFile.new(in_public_key_file_path)
	file_sz = trusted_cert_file.FileSize()
	file_reader = FilterReader.new(trusted_cert_file)
	trusted_cert_buf = file_reader.Read(file_sz)
	opts.AddTrustedCertificate(trusted_cert_buf, trusted_cert_buf.length, VerificationOptions::E_default_trust | VerificationOptions::E_certification_trust)

	# Iterate over the signatures and verify all of them.
	digsig_fitr = doc.GetDigitalSignatureFieldIterator()
	verification_status = true
	while digsig_fitr.HasNext() do
		curr = digsig_fitr.Current()
		result = curr.Verify(opts)
		if result.GetVerificationStatus()
			puts("Signature verified, objnum: " + curr.GetSDFObj().GetObjNum().to_s)
		else
			puts("Signature verification failed, objnum: " + curr.GetSDFObj().GetObjNum().to_s)
			verification_status = false
		end

		case result.GetDigestAlgorithm()
		when DigestAlgorithm::E_SHA1
			puts("Digest algorithm: SHA-1")
		when DigestAlgorithm::E_SHA256
			puts("Digest algorithm: SHA-256")
		when DigestAlgorithm::E_SHA384
			puts("Digest algorithm: SHA-384")
		when DigestAlgorithm::E_SHA512
			puts("Digest algorithm: SHA-512")
		when DigestAlgorithm::E_RIPEMD160
			puts("Digest algorithm: RIPEMD-160")
		when DigestAlgorithm::E_unknown_digest_algorithm
			puts("Digest algorithm: unknown")
		else
			puts("unrecognized digest algorithm")
			assert(false)
		end
	
		puts("Detailed verification result: \n\t" +
			result.GetDocumentStatusAsString() + "\n\t" +
			result.GetDigestStatusAsString() + "\n\t" +
			result.GetTrustStatusAsString() + "\n\t" +
			result.GetPermissionsStatusAsString() )
			
		changes = result.GetDisallowedChanges()
		for it2 in changes
			puts("\tDisallowed change: " + it2.GetTypeAsString() + ", objnum: " + it2.GetObjNum().to_s)
		end
		
		# Get and print all the detailed trust-related results, if they are available.
		if result.HasTrustVerificationResult()
			trust_verification_result = result.GetTrustVerificationResult()
			if trust_verification_result.WasSuccessful()
				puts("Trust verified.")
			else
				puts("Trust not verifiable.")
			end
			puts(trust_verification_result.GetResultString())
			
			tmp_time_t = trust_verification_result.GetTimeOfTrustVerification()
			
			case trust_verification_result.GetTimeOfTrustVerificationEnum()
			when VerificationOptions::E_current
				puts("Trust verification attempted with respect to current time (as epoch time): " + tmp_time_t.to_s)
			when VerificationOptions::E_signing
				puts("Trust verification attempted with respect to signing time (as epoch time): " + tmp_time_t.to_s)
			when VerificationOptions::E_timestamp
				puts("Trust verification attempted with respect to secure embedded timestamp (as epoch time): " + tmp_time_t.to_s)
			else
				puts("unrecognized time enum value")
				assert(false)
			end
			if trust_verification_result.GetCertPath().length() == 0
				puts("Could not print certificate path.")
			else
				puts("Certificate path:")
				cert_path = trust_verification_result.GetCertPath()
				for j in 0..cert_path.length()-1
					full_cert = cert_path[j]
					puts("\tCertificate:")
					puts("\t\tIssuer names:")
					issuer_dn  = full_cert.GetIssuerField().GetAllAttributesAndValues()
					for i in 0..issuer_dn.length()-1  
						puts("\t\t\t" + issuer_dn[i].GetStringValue())
					end

					puts("\t\tSubject names:")
					subject_dn = full_cert.GetSubjectField().GetAllAttributesAndValues()
					for i in 0..subject_dn.length()-1
						puts("\t\t\t" + subject_dn[i].GetStringValue())
					end
					puts("\t\tExtensions:")
					ex = full_cert.GetExtensions()
					for i in 0..ex.length()-1
						puts("\t\t\t" + ex[i].ToString())
					end
				end
			end
		else
			puts("No detailed trust verification result available.")
		end
		
		unsupported_features = result.GetUnsupportedFeatures()
		if unsupported_features.length()>0
			puts("Unsupported features:")
			for i in 0..unsupported_features.length()-1
				puts("\t" + unsupported_features[i])		
			end
		end
	puts("==========")
		
		digsig_fitr.Next()
	end

	return verification_status
end # VerifyAllAndPrint

def CertifyPDF(in_docpath,
	in_cert_field_name,
	in_private_key_file_path,
	in_keyfile_password,
	in_appearance_image_path,
	in_outpath)
	
	puts('================================================================================');
	puts('Certifying PDF document');

	# Open an existing PDF
	doc = PDFDoc.new(in_docpath);

	if (doc.HasSignatures())
		puts('PDFDoc has signatures');
	else
		puts('PDFDoc has no signatures');
	end

	page1 = doc.GetPage(1);

	# Create a text field that we can lock using the field permissions feature.
	annot1 = TextWidget.Create(doc, Rect.new(143, 440, 350, 460), "asdf_test_field");
	page1.AnnotPushBack(annot1);

	# Create a new signature form field in the PDFDoc. The name argument is optional;
	# leaving it empty causes it to be auto-generated. However, you may need the name for later.
	# Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
	# Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. 
	certification_sig_field = doc.CreateDigitalSignatureField(in_cert_field_name);
	widgetAnnot = SignatureWidget.Create(doc, Rect.new(143, 287, 219, 306), certification_sig_field);
	page1.AnnotPushBack(widgetAnnot);

	# (OPTIONAL) Add an appearance to the signature field.
	img = Image.Create(doc.GetSDFDoc, in_appearance_image_path);
	widgetAnnot.CreateSignatureAppearance(img);

	# Add permissions. Lock the random text field.
	puts('Adding document permissions.');
	certification_sig_field.SetDocumentPermissions(DigitalSignatureField::E_annotating_formfilling_signing_allowed);
	
	# Prepare to lock the text field that we created earlier.
	puts('Adding field permissions.');
	certification_sig_field.SetFieldPermissions(DigitalSignatureField::E_include, ['asdf_test_field']);

	certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password);

	# (OPTIONAL) Add more information to the signature dictionary.
	certification_sig_field.SetLocation('Vancouver, BC');
	certification_sig_field.SetReason('Document certification.');
	certification_sig_field.SetContactInfo('www.pdftron.com');

	# Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
	doc.Save(in_outpath, 0);

	puts('================================================================================');
end # def CertifyPDF

def SignPDF(in_docpath,	
	in_approval_field_name,	
	in_private_key_file_path, 
	in_keyfile_password, 
	in_appearance_img_path, 
	in_outpath)
	
	puts('================================================================================');
	puts('Signing PDF document');

	# Open an existing PDF
	doc = PDFDoc.new(in_docpath);

	# Retrieve the unsigned approval signature field.
	found_approval_field = doc.GetField(in_approval_field_name);
	found_approval_signature_digsig_field = DigitalSignatureField.new(found_approval_field);
	
	# (OPTIONAL) Add an appearance to the signature field.
	img = Image.Create(doc.GetSDFDoc, in_appearance_img_path);
	found_approval_signature_widget = SignatureWidget.new(found_approval_field.GetSDFObj());
	found_approval_signature_widget.CreateSignatureAppearance(img);

	# Prepare the signature and signature handler for signing.
	found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password);

	# The actual approval signing will be done during the following incremental save operation.
	doc.Save(in_outpath, SDFDoc::E_incremental);

	puts('================================================================================');
	
end # def SignPDF

def ClearSignature(in_docpath,
	in_digsig_field_name,
	in_outpath)

	puts('================================================================================');
	puts('Clearing certification signature');

	doc = PDFDoc.new(in_docpath);

	digsig = DigitalSignatureField.new(doc.GetField(in_digsig_field_name));
	
	puts('Clearing signature: ' + in_digsig_field_name);
	digsig.ClearSignature();

	if (!digsig.HasCryptographicSignature())
		puts('Cryptographic signature cleared properly.');
	end

	# Save incrementally so as to not invalidate other signatures from previous saves.
	doc.Save(in_outpath, SDFDoc::E_incremental);

	puts('================================================================================');

end # def ClearSignature

def PrintSignaturesInfo(in_docpath)
	puts('================================================================================');
	puts('Reading and printing digital signature information');

	doc = PDFDoc.new(in_docpath);
	if (!doc.HasSignatures())
		puts('Doc has no signatures.');
		puts('================================================================================');
		return;
	else
		puts('Doc has signatures.');
	end

	fitr = doc.GetFieldIterator()
	while fitr.HasNext() do
		current = fitr.Current();
		if (current.IsLockedByDigitalSignature())
			puts("==========\nField locked by a digital signature");
		else
			puts("==========\nField not locked by a digital signature");
		end

		puts('Field name: ' + current.GetName());
		puts('==========');
		
		fitr.Next()
	end

	puts("====================\nNow iterating over digital signatures only.\n====================");

	digsig_fitr = doc.GetDigitalSignatureFieldIterator();
	while digsig_fitr.HasNext() do
		current = digsig_fitr.Current();
		puts('==========');
		puts('Field name of digital signature: ' + Field.new(current.GetSDFObj()).GetName());

		digsigfield = current;
		if (!digsigfield.HasCryptographicSignature())
			puts("Either digital signature field lacks a digital signature dictionary, " +
				"or digital signature dictionary lacks a cryptographic Contents entry. " +
				"Digital signature field is not presently considered signed.\n" +
				"==========");
			digsig_fitr.Next()
			next;
		end

		cert_count = digsigfield.GetCertCount();
		puts('Cert count: ' + cert_count.to_s);
		for i in 0...cert_count
			cert = digsigfield.GetCert(i);
			puts('Cert #' + i + ' size: ' + cert.length);
		end

		subfilter = digsigfield.GetSubFilter();

		puts('Subfilter type: ' + subfilter.to_s);

		if (subfilter != DigitalSignatureField::E_ETSI_RFC3161)
			puts('Signature\'s signer: ' + digsigfield.GetSignatureName());

			signing_time = digsigfield.GetSigningTime();
			if (signing_time.IsValid())
				puts('Signing time is valid.');
			end

			puts('Location: ' + digsigfield.GetLocation());
			puts('Reason: ' + digsigfield.GetReason());
			puts('Contact info: ' + digsigfield.GetContactInfo());
		else
			puts('SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)');
		end

		if (digsigfield.HasVisibleAppearance())
			puts('Visible');
		else
			puts('Not visible');
		end

		digsig_doc_perms = digsigfield.GetDocumentPermissions();
		locked_fields = digsigfield.GetLockedFields();
		for it in locked_fields
			puts('This digital signature locks a field named: ' + it);
		end

		case digsig_doc_perms
		when DigitalSignatureField::E_no_changes_allowed
			puts('No changes to the document can be made without invalidating this digital signature.');
		when DigitalSignatureField::E_formfilling_signing_allowed
			puts('Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.');
		when DigitalSignatureField::E_annotating_formfilling_signing_allowed
			puts('Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.');
		when DigitalSignatureField::E_unrestricted
			puts('Document not restricted by this digital signature.');
		else
			puts('Unrecognized digital signature document permission level.');
			assert(false);
		end
		puts('==========');
		digsig_fitr.Next()
	end

	puts('================================================================================');
end # def PrintSignaturesInfo

def CustomSigningAPI(doc_path,
		cert_field_name,
		private_key_file_path,
		keyfile_password,
		public_key_file_path,
		appearance_image_path,
		digest_algorithm_type,
		pades_signing_mode,
		output_path)
	puts('================================================================================');
	puts('Custom signing PDF document');

	doc = PDFDoc.new(doc_path);

	page1 = doc.GetPage(1);

	digsig_field = doc.CreateDigitalSignatureField(cert_field_name);
	widgetAnnot = SignatureWidget.Create(doc, Rect.new(143, 287, 219, 306), digsig_field);
	page1.AnnotPushBack(widgetAnnot);

	# (OPTIONAL) Add an appearance to the signature field.
	img = Image.Create(doc.GetSDFDoc(), appearance_image_path);
	widgetAnnot.CreateSignatureAppearance(img);

	# Create a digital signature dictionary inside the digital signature field, in preparation for signing.
	digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
		pades_signing_mode ? DigitalSignatureField::E_ETSI_CAdES_detached : DigitalSignatureField::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.
	current_date = Date.new();
	current_date.SetCurrentTime();
	digsig_field.SetSigDictTimeOfSigning(current_date);

	doc.Save(output_path, SDFDoc::E_incremental);

	# Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
	pdf_digest = digsig_field.CalculateDigest(digest_algorithm_type);

	signer_cert = X509Certificate.new(public_key_file_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.
	pades_versioned_ess_signing_cert_attribute = DigitalSignatureField.GenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type);

	# 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.
	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).
	signedAttrs_digest = DigestAlgorithm.CalculateDigest(digest_algorithm_type, signedAttrs);

	############################ custom digest signing starts ############################
	# At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
	# which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
	signature_value = DigestAlgorithm.SignDigest(
		signedAttrs_digest,
		digest_algorithm_type,
		private_key_file_path,
		keyfile_password);
	############################ custom digest signing ends ##############################

	# Then, load all your chain certificates into a container of X509Certificate.
	chain_certs = VectorX509Certificate.new();

	# Then, create ObjectIdentifiers for the algorithms you have used.
	# Here we use digest_algorithm_type (SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
	digest_algorithm_oid = ObjectIdentifier.new(ObjectIdentifier::E_SHA256);
	signature_algorithm_oid = ObjectIdentifier.new(ObjectIdentifier::E_RSA_encryption_PKCS1);

	# Then, put the CMS signature components together.
	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, output_path);

	puts('================================================================================');
end # def CustomSigningAPI

def TimestampAndEnableLTV(in_docpath,
	in_tsa_url,
	in_trusted_cert_path,
	in_appearance_img_path,
	in_outpath)
	doc = PDFDoc.new(in_docpath);
	doctimestamp_signature_field = doc.CreateDigitalSignatureField();
	tst_config = TimestampingConfiguration.new(in_tsa_url);
	opts = VerificationOptions.new(VerificationOptions::E_compatibility_and_archiving);
#	It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
#	the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
#	response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
#	function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
#	in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part.

	opts.AddTrustedCertificate(in_trusted_cert_path);
#   	By default, we only check online for revocation of certificates using the newer and lighter 
#	OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
#	it may be necessary to enable online CRL revocation checking in order to verify some timestamps
#	(i.e. those that do not have an OCSP responder URL for all non-trusted certificates).

	opts.EnableOnlineCRLRevocationChecking(true);

	widgetAnnot = SignatureWidget.Create(doc, Rect.new(0.0, 100.0, 200.0, 150.0), doctimestamp_signature_field);
	doc.GetPage(1).AnnotPushBack(widgetAnnot);

	# (OPTIONAL) Add an appearance to the signature field.
	img = Image.Create(doc.GetSDFDoc(), in_appearance_img_path);
	widgetAnnot.CreateSignatureAppearance(img);

	puts('Testing timestamping configuration.');
	config_result = tst_config.TestConfiguration(opts);
	if (config_result.GetStatus())
		puts('Success: timestamping configuration usable. Attempting to timestamp.');
	else
		# Print details of timestamping failure.
		puts(config_result.GetString());
		if config_result.HasResponseVerificationResult()
			tst_result = config_result.GetResponseVerificationResult();
			puts('CMS digest status: '+ tst_result.GetCMSDigestStatusAsString());
			puts('Message digest status: ' + tst_result.GetMessageImprintDigestStatusAsString());
			puts('Trust status: ' + tst_result.GetTrustStatusAsString());
		end
		return false;
	end

	doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts);

	# Save/signing throws if timestamping fails.
	doc.Save(in_outpath, SDFDoc::E_incremental);

	puts('Timestamping successful. Adding LTV information for DocTimeStamp signature.');

	# Add LTV information for timestamp signature to document.
	timestamp_verification_result = doctimestamp_signature_field.Verify(opts);
	if !doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result)
		puts('Could not enable LTV for DocTimeStamp.');
		return false;
	end
	doc.Save(in_outpath, SDFDoc::E_incremental);
	puts('Added LTV information for DocTimeStamp signature successfully.');

	return true;
end

def main()
    # Initialize PDFNet
    PDFNet.Initialize(PDFTronLicense.Key)
	
    result = true
	input_path = '../../TestFiles/';
	output_path = '../../TestFiles/Output/';
	
	#################### TEST 0:
	# Create an approval signature field that we can sign after certifying.
	# (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
	# Open an existing PDF
	begin
		doc = PDFDoc.new(input_path + 'waiver.pdf');
		
		widgetAnnotApproval = SignatureWidget.Create(doc, Rect.new(300, 287, 376, 306), 'PDFTronApprovalSig');
		page1 = doc.GetPage(1);
		page1.AnnotPushBack(widgetAnnotApproval);
		doc.Save(output_path + 'waiver_withApprovalField_output.pdf', SDFDoc::E_remove_unused);
	rescue Exception => e
        puts(e.message)
        puts(e.backtrace.inspect)
		result = false
    end
	
	#################### TEST 1: certify a PDF.
	begin
		CertifyPDF(input_path + 'waiver_withApprovalField.pdf',
			'PDFTronCertificationSig',
			input_path + 'pdftron.pfx',
			'password',
			input_path + 'pdftron.bmp',
			output_path + 'waiver_withApprovalField_certified_output.pdf');
		PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_output.pdf');
	rescue Exception => e
        puts(e.message)
        puts(e.backtrace.inspect)
		result = false
    end
	#################### TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
	begin
		SignPDF(input_path + 'waiver_withApprovalField_certified.pdf',
			'PDFTronApprovalSig',
			input_path + 'pdftron.pfx',
			'password',
			input_path + 'signature.jpg',
			output_path + 'waiver_withApprovalField_certified_approved_output.pdf');
		PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_output.pdf');
	rescue Exception => e
        puts(e.message)
        puts(e.backtrace.inspect)
		result = false
    end

	#################### TEST 3: Clear a certification from a document that is certified and has an approval signature.
	begin
		ClearSignature(input_path + 'waiver_withApprovalField_certified_approved.pdf',
			'PDFTronCertificationSig',
			output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
		PrintSignaturesInfo(output_path + 'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
	rescue Exception => e
        puts(e.message)
        puts(e.backtrace.inspect)
		result = false
    end

	#################### TEST 4: Verify a document's digital signatures.
	begin
		if !VerifyAllAndPrint(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer")
			return false;
		end
	rescue Exception => e
        puts(e.message);
        puts(e.backtrace.inspect);
	end

	#################### TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
	begin
		if !VerifySimple(input_path + 'waiver_withApprovalField_certified_approved.pdf', input_path + 'pdftron.cer')
			result = false;
		end
	rescue Exception => e
        puts(e.message);
        puts(e.backtrace.inspect);
	end
	
	#################### TEST 6: Custom signing API.
	# The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
	# which allows users to customize the process of signing documents. Among other things, this
	# includes the capability to allow for easy integration of PDF-specific signing-related operations
	# with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
	# to system keystores, etc.
	begin
		CustomSigningAPI(input_path + "waiver.pdf",
			"PDFTronApprovalSig",
			input_path + "pdftron.pfx",
			"password",
			input_path + "pdftron.cer",
			input_path + "signature.jpg",
			DigestAlgorithm::E_SHA256,
			true,
			output_path + "waiver_custom_signed.pdf")
	rescue Exception => e
		puts(e.message);
		puts(e.backtrace.inspect);
	end

	#################### TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
	# begin
	# 	# Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
	# 	# For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
	# 	# Note that this url may not work in the future. A reliable solution requires using your own TSA.
	# 	tsa_url = 'YOUR_URL_OF_TSA';
	# 	if tsa_url == 'YOUR_URL_OF_TSA'
	# 		raise 'Error: The URL of your timestamp authority was not specified.';
	# 	end
	#
	# 	# Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
	# 	# For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
	# 	# Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
	# 	trusted_cert_path = 'YOUR_CERTIFICATE';
	# 	if trusted_cert_path == 'YOUR_CERTIFICATE'
	# 		raise 'Error: The path to your timestamp authority trusted root certificate was not specified.';
	# 	end
	#
	# 	if !TimestampAndEnableLTV(input_path + 'waiver.pdf',
	# 		tsa_url,
	# 		trusted_cert_path,
	# 		input_path + 'signature.jpg',
	# 		output_path+ 'waiver_DocTimeStamp_LTV.pdf')
	# 		result = false;
	# 	end
	# rescue Exception => e
	# 	puts(e.message);
	# 	puts(e.backtrace.inspect);
	# 	result = false;
	# end

	#################### End of tests. ####################
	PDFNet.Terminate
	if (!result)
        	puts("Tests FAILED!!!\n==========")
        	return
	end # if (!result)
	
	puts("Tests successful.\n==========")

end # def main()

main()
```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
<?php
//----------------------------------------------------------------------------------------------------------------------
// Copyright (c) 2001-2023 by Apryse Software Inc. All Rights Reserved.
// Consult LICENSE.txt regarding license information.
//----------------------------------------------------------------------------------------------------------------------
if(file_exists("../../../PDFNetC/Lib/PDFNetPHP.php"))

//----------------------------------------------------------------------------------------------------------------------
// This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
//
// The following steps reflect typical intended usage of the digital signatures API:
//
//	0.	Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
//	
//	1.	EITHER: 
//		(a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
//		-OR-
//		(b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
//		document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
//	
//	2.	Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
//		If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
//		NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
//		
//	[3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
//		or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
//		certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
//		is signed.]
//		
//	4. 	Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
//		a.	Taking a PKCS #12 keyfile path and its password
//		b.	Taking a buffer containing a PKCS #12 private keyfile and its password
//		c.	Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
//			in the following fashion: 
//			i)		Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
//					validate/check a digital signature.
//			ii)		Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
//					pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
//			iii)	Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
//		NOTE: It is only possible to sign/certify one signature per call to the Save function.
//	
//	5.	Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
//		IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
//		so as to not invalidate the other signature(s). 
//
// Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
// of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
// add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
//
// Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
// StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
//----------------------------------------------------------------------------------------------------------------------

include('../../../PDFNetC/Lib/PDFNetPHP.php');
include("../../LicenseKey/PHP/LicenseKey.php");

function VerifySimple($in_docpath, $in_public_key_file_path)
{
	$doc = new PDFDoc($in_docpath);
	echo(nl2br("==========".PHP_EOL));
	$opts = new VerificationOptions(VerificationOptions::e_compatibility_and_archiving);

	// Add trust root to store of trusted certificates contained in VerificationOptions.
	$opts->AddTrustedCertificate($in_public_key_file_path, VerificationOptions::e_default_trust | VerificationOptions::e_certification_trust);

	$result = $doc->VerifySignedDigitalSignatures($opts);
	switch ($result)
	{
	case PDFDoc::e_unsigned:
		echo(nl2br("Document has no signed signature fields.".PHP_EOL));
		return False;
		/* e_failure == bad doc status, digest status, or permissions status
		(i.e. does not include trust issues, because those are flaky due to being network/config-related) */
	case PDFDoc::e_failure:
		echo(nl2br("Hard failure in verification on at least one signature.".PHP_EOL));
		return False;
	case PDFDoc::e_untrusted:
		echo(nl2br("Could not verify trust for at least one signature.".PHP_EOL));
		return False;
	case PDFDoc::e_unsupported:
		/* If necessary, call GetUnsupportedFeatures on VerificationResult to check which
		unsupported features were encountered (requires verification using 'detailed' APIs) */
		echo(nl2br("At least one signature contains unsupported features.".PHP_EOL));
		return False;
		// unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
	case PDFDoc::e_verified:
		echo(nl2br("All signed signatures in document verified.".PHP_EOL));
		return True;
	default:
		echo(nl2br("unrecognized document verification status".PHP_EOL));
		assert(False);
	}
}

function VerifyAllAndPrint($in_docpath, $in_public_key_file_path)
{
	$doc = new PDFDoc($in_docpath);
	echo(nl2br("==========".PHP_EOL));
	$opts = new VerificationOptions(VerificationOptions::e_compatibility_and_archiving);
	
	// Trust the public certificate we use for signing.
	$trusted_cert_file = new MappedFile($in_public_key_file_path);
	$file_sz = $trusted_cert_file->FileSize();
	$file_reader = new FilterReader($trusted_cert_file);
	$trusted_cert_buf = $file_reader->Read($file_sz);
	$opts->AddTrustedCertificate($trusted_cert_buf, strlen($trusted_cert_buf), VerificationOptions::e_default_trust | VerificationOptions::e_certification_trust);

	// Iterate over the signatures and verify all of them.
	$digsig_fitr = $doc->GetDigitalSignatureFieldIterator();
	$verification_status = True;
	while ($digsig_fitr->HasNext())
	{
		$curr = $digsig_fitr->Current();
		$result = $curr->Verify($opts);
		if ($result->GetVerificationStatus())
		{
			echo(nl2br("Signature verified, objnum: ".strval($curr->GetSDFObj()->GetObjNum()).PHP_EOL));
		}
		else
		{
			echo(nl2br("Signature verification failed, objnum: ".strval($curr->GetSDFObj()->GetObjNum()).PHP_EOL));
			$verification_status = False;
		}
		
		switch($result->GetDigestAlgorithm())
		{
			case DigestAlgorithm::e_SHA1:
				echo(nl2br("Digest algorithm: SHA-1".PHP_EOL));
				break;
			case DigestAlgorithm::e_SHA256:
				echo(nl2br("Digest algorithm: SHA-256".PHP_EOL));
				break;
			case DigestAlgorithm::e_SHA384:
				echo(nl2br("Digest algorithm: SHA-384".PHP_EOL));
				break;
			case DigestAlgorithm::e_SHA512:
				echo(nl2br("Digest algorithm: SHA-512".PHP_EOL));
				break;
			case DigestAlgorithm::e_RIPEMD160:
				echo(nl2br("Digest algorithm: RIPEMD-160".PHP_EOL));
				break;
			case DigestAlgorithm::e_unknown_digest_algorithm:
				echo(nl2br("Digest algorithm: unknown".PHP_EOL));
				break;
			default:
				echo(nl2br("unrecognized digest algorithm".PHP_EOL));
				assert(False);
		}
		echo(nl2br("Detailed verification result: \n\t".$result->GetDocumentStatusAsString()."\n\t"
		.$result->GetDigestStatusAsString()."\n\t"
		.$result->GetTrustStatusAsString()."\n\t"
		.$result->GetPermissionsStatusAsString().PHP_EOL));


		$changes = $result->GetDisallowedChanges();
		for ($i = 0; $i < $changes->size(); $i++)
		{
			$change = $changes->get($i);
			echo(nl2br("\tDisallowed change: ".strval($change->GetTypeAsString()).", objnum: ".strval($change->GetObjNum()).PHP_EOL));
		}
		
		// Get and print all the detailed trust-related results, if they are available.
		if ($result->HasTrustVerificationResult())
		{
			$trust_verification_result = $result->GetTrustVerificationResult();
			if ($trust_verification_result->WasSuccessful())
			{
				echo(nl2br("Trust verified.".PHP_EOL));
			}
			else
			{
				echo(nl2br("Trust not verifiable.".PHP_EOL));
			}
			echo(nl2br($trust_verification_result->GetResultString().PHP_EOL));
			
			$tmp_time_t = $trust_verification_result->GetTimeOfTrustVerification();
			
			switch ($trust_verification_result->GetTimeOfTrustVerificationEnum())
			{
				case VerificationOptions::e_current:
					echo(nl2br("Trust verification attempted with respect to current time (as epoch time): ".$tmp_time_t.PHP_EOL));
					break;
				case VerificationOptions::e_signing:
					echo(nl2br("Trust verification attempted with respect to signing time (as epoch time): ".$tmp_time_t.PHP_EOL));
					break;
				case VerificationOptions::e_timestamp:
					echo(nl2br("Trust verification attempted with respect to secure embedded timestamp (as epoch time): ".$tmp_time_t.PHP_EOL));
					break;
				default:
					echo(nl2br('unrecognized time enum value'.PHP_EOL));
					assert(False);
			}

			if ($trust_verification_result->GetCertPath()->Size() == 0)
			{
				echo(nl2br("Could not print certificate path.\n"));
			}
			else
			{
				echo(nl2br("Certificate path:\n"));
				$cert_path = $trust_verification_result->GetCertPath();
				for ($j = 0; $j < $cert_path->Size(); $j++)
				{
					echo(nl2br("\tCertificate:\n"));
					$full_cert = $cert_path->Get($j);
					echo(nl2br("\t\tIssuer names:\n"));
										
					$issuer_dn = $full_cert->GetIssuerField()->GetAllAttributesAndValues();
					for ($i = 0; $i < $issuer_dn->Size(); $i++)
					{
						echo(nl2br("\t\t\t". $issuer_dn->Get($i)->GetStringValue()."\n"));
					}
					echo(nl2br("\t\tSubject names:\n"));
					$subject_dn = $full_cert->GetSubjectField()->GetAllAttributesAndValues();
					for ($i = 0; $i < $subject_dn->Size(); $i++)
					{
						echo(nl2br("\t\t\t".$subject_dn->Get($i)->GetStringValue()."\n"));
					}
					echo(nl2br("\t\tExtensions:\n"));
					$ex = $full_cert->GetExtensions();
					for ($i = 0; $i < $ex->Size(); $i++)
					{	
						echo(nl2br("\t\t\t".$ex->Get($i)->ToString()."\n"));
					}
				}
			}
		}	
		else
		{
			echo(nl2br("No detailed trust verification result available."));
		}

		$unsupported_features = $result->GetUnsupportedFeatures();
		if (count($unsupported_features) > 0)
		{
			echo(nl2br("Unsupported features:\n"));
			for ($i = 0; $i < count($unsupported_features); $i++)
			{
				echo(nl2br("\t".$unsupported_features[$i]."\n"));
			}
		}		
		echo(nl2br("==========".PHP_EOL));
		
		$digsig_fitr->Next();
	}

	return $verification_status;
}

function CertifyPDF($in_docpath,
	$in_cert_field_name,
	$in_private_key_file_path,
	$in_keyfile_password,
	$in_appearance_image_path,
	$in_outpath)
{
	
	echo(nl2br('================================================================================'.PHP_EOL));
	echo(nl2br('Certifying PDF document'.PHP_EOL));

	// Open an existing PDF
	$doc = new PDFDoc($in_docpath);

	if ($doc->HasSignatures())
	{
		echo(nl2br('PDFDoc has signatures'.PHP_EOL));
	}
	else
	{
		echo(nl2br('PDFDoc has no signatures'.PHP_EOL));
	}

	$page1 = $doc->GetPage(1);

	// Create a text field that we can lock using the field permissions feature.
	$annot1 = TextWidget::Create($doc, new Rect(143.0, 440.0, 350.0, 460.0), "asdf_test_field");
	$page1->AnnotPushBack($annot1);

	// Create a new signature form field in the PDFDoc. The name argument is optional;
	// leaving it empty causes it to be auto-generated. However, you may need the name for later.
	// Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
	// Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. 
	$certification_sig_field = $doc->CreateDigitalSignatureField($in_cert_field_name);
	$widgetAnnot = SignatureWidget::Create($doc, new Rect(143.0, 287.0, 219.0, 306.0), $certification_sig_field);
	$page1->AnnotPushBack($widgetAnnot);

	// (OPTIONAL) Add an appearance to the signature field.
	$img = Image::Create($doc->GetSDFDoc(), $in_appearance_image_path);
	$widgetAnnot->CreateSignatureAppearance($img);

	// Prepare the document locking permission level. It will be applied upon document certification.
	echo(nl2br('Adding document permissions.'.PHP_EOL));
	$certification_sig_field->SetDocumentPermissions(DigitalSignatureField::e_annotating_formfilling_signing_allowed);
	
	// Prepare to lock the text field that we created earlier.
	echo(nl2br('Adding field permissions.'.PHP_EOL));
	$certification_sig_field->SetFieldPermissions(DigitalSignatureField::e_include, array('asdf_test_field'));

	$certification_sig_field->CertifyOnNextSave($in_private_key_file_path, $in_keyfile_password);

	// (OPTIONAL) Add more information to the signature dictionary.
	$certification_sig_field->SetLocation('Vancouver, BC');
	$certification_sig_field->SetReason('Document certification.');
	$certification_sig_field->SetContactInfo('www.pdftron.com');

	// Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
	$doc->Save($in_outpath, 0);

	echo(nl2br('================================================================================'.PHP_EOL));
}

function SignPDF($in_docpath,	
	$in_approval_field_name,	
	$in_private_key_file_path, 
	$in_keyfile_password, 
	$in_appearance_img_path, 
	$in_outpath)
{
	echo(nl2br('================================================================================'.PHP_EOL));
	echo(nl2br('Signing PDF document'.PHP_EOL));

	// Open an existing PDF
	$doc = new PDFDoc($in_docpath);

	// Retrieve the unsigned approval signature field.
	$found_approval_field = $doc->GetField($in_approval_field_name);
	$found_approval_signature_digsig_field = new DigitalSignatureField($found_approval_field);
	
	// (OPTIONAL) Add an appearance to the signature field.
	$img = Image::Create($doc->GetSDFDoc(), $in_appearance_img_path);
	$found_approval_signature_widget = new SignatureWidget($found_approval_field->GetSDFObj());
	$found_approval_signature_widget->CreateSignatureAppearance($img);

	// Prepare the signature and signature handler for signing.
	$found_approval_signature_digsig_field->SignOnNextSave($in_private_key_file_path, $in_keyfile_password);

	// The actual approval signing will be done during the following incremental save operation.
	$doc->Save($in_outpath, SDFDoc::e_incremental);

	echo(nl2br('================================================================================'.PHP_EOL));
}

function ClearSignature($in_docpath,
	$in_digsig_field_name,
	$in_outpath)
{
	echo(nl2br('================================================================================'.PHP_EOL));
	echo(nl2br('Clearing certification signature'.PHP_EOL));

	$doc = new PDFDoc($in_docpath);

	$digsig = new DigitalSignatureField($doc->GetField($in_digsig_field_name));
	
	echo(nl2br('Clearing signature: '.$in_digsig_field_name.PHP_EOL));
	$digsig->ClearSignature();

	if (!$digsig->HasCryptographicSignature())
	{
		echo(nl2br('Cryptographic signature cleared properly.'.PHP_EOL));
	}

	// Save incrementally so as to not invalidate other signatures from previous saves.
	$doc->Save($in_outpath, SDFDoc::e_incremental);

	echo(nl2br('================================================================================'.PHP_EOL));
}

function PrintSignaturesInfo($in_docpath)
{
	echo(nl2br('================================================================================'.PHP_EOL));
	echo(nl2br('Reading and printing digital signature information'.PHP_EOL));

	$doc = new PDFDoc($in_docpath);
	if (!$doc->HasSignatures())
	{
		echo(nl2br('Doc has no signatures.'.PHP_EOL));
		echo(nl2br('================================================================================'.PHP_EOL));
		return;
	}
	else
	{
		echo(nl2br('Doc has signatures.'.PHP_EOL));
	}

	$fitr = $doc->GetFieldIterator();
	while ($fitr->HasNext())
	{
		$current = $fitr->Current();
		if ($current->IsLockedByDigitalSignature())
		{
			echo(nl2br("==========\nField locked by a digital signature".PHP_EOL));
		}
		else
		{
			echo(nl2br("==========\nField not locked by a digital signature".PHP_EOL));
		}

		echo(nl2br('Field name: '.$current->GetName().PHP_EOL));
		echo(nl2br('=========='.PHP_EOL));
		
		$fitr->Next();
	}

	echo(nl2br("====================\nNow iterating over digital signatures only.\n====================".PHP_EOL));

	$digsig_fitr = $doc->GetDigitalSignatureFieldIterator();
	while ($digsig_fitr->HasNext())
	{
		$current = $digsig_fitr->Current();
		echo(nl2br('=========='.PHP_EOL));
		$fld = new Field($current->GetSDFObj());
		$fname = $fld->GetName();
		echo(nl2br('Field name of digital signature: '.$fname.PHP_EOL));

		$digsigfield = $current;
		if (!$digsigfield->HasCryptographicSignature())
		{
			echo(nl2br("Either digital signature field lacks a digital signature dictionary, ".
				"or digital signature dictionary lacks a cryptographic Contents entry. ".
				"Digital signature field is not presently considered signed.\n".
				"==========".PHP_EOL));
			$digsig_fitr->Next();
			continue;
		}

		$cert_count = $digsigfield->GetCertCount();
		echo(nl2br('Cert count: '.strval($cert_count).PHP_EOL));
		for ($i = 0; $i<$cert_count; ++$i) 
		{
			$cert = $digsigfield->GetCert(i);
			echo(nl2br('Cert #'.i.' size: '.$cert.length.PHP_EOL));
		}

		$subfilter = $digsigfield->GetSubFilter();

		echo(nl2br('Subfilter type: '.strval($subfilter).PHP_EOL));

		if ($subfilter !== DigitalSignatureField::e_ETSI_RFC3161)
		{
			echo(nl2br('Signature\'s signer: '.$digsigfield->GetSignatureName().PHP_EOL));

			$signing_time = $digsigfield->GetSigningTime();
			if ($signing_time->IsValid())
			{
				echo(nl2br('Signing time is valid.'.PHP_EOL));
			}

			echo(nl2br('Location: '.$digsigfield->GetLocation().PHP_EOL));
			echo(nl2br('Reason: '.$digsigfield->GetReason().PHP_EOL));
			echo(nl2br('Contact info: '.$digsigfield->GetContactInfo().PHP_EOL));
		}
		else
		{
			echo(nl2br('SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)'.PHP_EOL));
		}

		if ($digsigfield->HasVisibleAppearance())
		{
			echo(nl2br('Visible'.PHP_EOL));
		}
		else
		{
			echo(nl2br('Not visible'.PHP_EOL));
		}

		$digsig_doc_perms = $digsigfield->GetDocumentPermissions();
		$locked_fields = $digsigfield->GetLockedFields();
		foreach ($locked_fields as $locked_field)
		{
			echo(nl2br('This digital signature locks a field named: '.$locked_field.PHP_EOL));
		}

		switch ($digsig_doc_perms)
		{
			case DigitalSignatureField::e_no_changes_allowed:
				echo(nl2br('No changes to the document can be made without invalidating this digital signature.'.PHP_EOL));
				break;
			case DigitalSignatureField::e_formfilling_signing_allowed:
				echo(nl2br('Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.'.PHP_EOL));
				break;
			case DigitalSignatureField::e_annotating_formfilling_signing_allowed:
				echo(nl2br('Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.'.PHP_EOL));
				break;
			case DigitalSignatureField::e_unrestricted:
				echo(nl2br('Document not restricted by this digital signature.'.PHP_EOL));
				break;
			default:
				echo(nl2br('Unrecognized digital signature document permission level.'.PHP_EOL));
				assert(false);
		}
		
		echo(nl2br('=========='.PHP_EOL));
		$digsig_fitr->Next();
	}

	echo(nl2br('================================================================================'.PHP_EOL));
}

function CustomSigningAPI($doc_path,
	$cert_field_name,
	$private_key_file_path,
	$keyfile_password,
	$public_key_file_path,
	$appearance_image_path,
	$digest_algorithm_type,
	$PAdES_signing_mode,
	$output_path)
{
	echo(nl2br('================================================================================'.PHP_EOL));
	echo(nl2br('Custom signing PDF document'.PHP_EOL));
			
	$doc = new PDFDoc($doc_path);
	$page1 = $doc->GetPage(1);

	$digsig_field = $doc->CreateDigitalSignatureField($cert_field_name);
	$widgetAnnot = SignatureWidget::Create($doc, new Rect(143.0, 287.0, 219.0, 306.0), $digsig_field);
	$page1->AnnotPushBack($widgetAnnot);

	// (OPTIONAL) Add an appearance to the signature field.
	$img = Image::Create($doc->GetSDFDoc(), $appearance_image_path);
	$widgetAnnot->CreateSignatureAppearance($img);

	// Create a digital signature dictionary inside the digital signature field, in preparation for signing.
	$digsig_field->CreateSigDictForCustomSigning("Adobe.PPKLite",
		$PAdES_signing_mode ? DigitalSignatureField::e_ETSI_CAdES_detached : DigitalSignatureField::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.
	$current_date = new Date();
	$current_date->SetCurrentTime();
	$digsig_field->SetSigDictTimeOfSigning($current_date);

	$doc->Save($output_path, SDFDoc::e_incremental);

	// Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
	$pdf_digest = $digsig_field->CalculateDigest($digest_algorithm_type);

	$signer_cert = new X509Certificate($public_key_file_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.
	$pades_versioned_ess_signing_cert_attribute = DigitalSignatureField::GenerateESSSigningCertPAdESAttribute($signer_cert, $digest_algorithm_type);

	// 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.
	$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).
	$signedAttrs_digest = DigestAlgorithm::CalculateDigest($digest_algorithm_type, $signedAttrs);

	//////////////////////////// custom digest signing starts ////////////////////////////
	// At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
	// which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
	$signature_value = DigestAlgorithm::SignDigest(
		$signedAttrs_digest,
		$digest_algorithm_type,
		$private_key_file_path,
		$keyfile_password);
	//////////////////////////// custom digest signing ends //////////////////////////////

	// Then, load all your chain certificates into a container of X509Certificate.
	$chain_certs = new VectorX509Certificate();

	// Then, create ObjectIdentifiers for the algorithms you have used.
	// Here we use digest_algorithm_type (SHA256) for hashing, and RSAES-PKCS1-v1_5 (specified in the private key) for signing.
	$digest_algorithm_oid = new ObjectIdentifier(ObjectIdentifier::e_SHA256);
	$signature_algorithm_oid = new ObjectIdentifier(ObjectIdentifier::e_RSA_encryption_PKCS1);

	// Then, put the CMS signature components together.
	$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, $output_path);
			
	echo(nl2br('================================================================================'.PHP_EOL));
}

function TimestampAndEnableLTV($in_docpath,
	$in_tsa_url,
	$in_trusted_cert_path, 
	$in_appearance_img_path,
	$in_outpath)
{
	$doc = new PDFDoc($in_docpath);
	$doctimestamp_signature_field = $doc->CreateDigitalSignatureField();
	$tst_config = new TimestampingConfiguration($in_tsa_url);
	$opts = new VerificationOptions(VerificationOptions::e_compatibility_and_archiving);
	/* It is necessary to add to the VerificationOptions a trusted root certificate corresponding to 
	the chain used by the timestamp authority to sign the timestamp token, in order for the timestamp
	response to be verifiable during DocTimeStamp signing. It is also necessary in the context of this 
	function to do this for the later LTV section, because one needs to be able to verify the DocTimeStamp 
	in order to enable LTV for it, and we re-use the VerificationOptions opts object in that part. */
	$opts->AddTrustedCertificate($in_trusted_cert_path);
	/* By default, we only check online for revocation of certificates using the newer and lighter 
	OCSP protocol as opposed to CRL, due to lower resource usage and greater reliability. However, 
	it may be necessary to enable online CRL revocation checking in order to verify some timestamps
	(i.e. those that do not have an OCSP responder URL for all non-trusted certificates). */
	$opts->EnableOnlineCRLRevocationChecking(true);

	$widgetAnnot = SignatureWidget::Create($doc, new Rect(0.0, 100.0, 200.0, 150.0), $doctimestamp_signature_field);
	$doc->GetPage(1)->AnnotPushBack($widgetAnnot);

	// (OPTIONAL) Add an appearance to the signature field.
	$img = Image::Create($doc->GetSDFDoc(), $in_appearance_img_path);
	$widgetAnnot->CreateSignatureAppearance($img);

	echo(nl2br('Testing timestamping configuration.'.PHP_EOL));
	$config_result = $tst_config->TestConfiguration($opts);
	if ($config_result->GetStatus())
	{
		echo(nl2br('Success: timestamping configuration usable. Attempting to timestamp.'.PHP_EOL));
	}
	else
	{
		// Print details of timestamping failure.
		echo(nl2br($config_result->GetString().PHP_EOL));
		if ($config_result->HasResponseVerificationResult())
		{
			$tst_result = $config_result->GetResponseVerificationResult();
			echo(nl2br('CMS digest status: '.$tst_result->GetCMSDigestStatusAsString().PHP_EOL));
			echo(nl2br('Message digest status: '.$tst_result->GetMessageImprintDigestStatusAsString().PHP_EOL));
			echo(nl2br('Trust status: '.$tst_result->GetTrustStatusAsString().PHP_EOL));
		}
		return false;
	}

	$doctimestamp_signature_field->TimestampOnNextSave($tst_config, $opts);

	// Save/signing throws if timestamping fails.
	$doc->Save($in_outpath, SDFDoc::e_incremental);

	echo(nl2br('Timestamping successful. Adding LTV information for DocTimeStamp signature.'.PHP_EOL));

	// Add LTV information for timestamp signature to document.
	$timestamp_verification_result = $doctimestamp_signature_field->Verify($opts);
	if (!$doctimestamp_signature_field->EnableLTVOfflineVerification($timestamp_verification_result))
	{
		echo(nl2br('Could not enable LTV for DocTimeStamp.'.PHP_EOL));
		return false;
	}
	$doc->Save($in_outpath, SDFDoc::e_incremental);
	echo(nl2br('Added LTV information for DocTimeStamp signature successfully.'.PHP_EOL));

	return true;
}

function main()
{
	global $LicenseKey;
	// Initialize PDFNet
	PDFNet::Initialize($LicenseKey);
	
	$result = true;
	$input_path = '../../TestFiles/';
	$output_path = '../../TestFiles/Output/';
	
	//////////////////// TEST 0:
	// Create an approval signature field that we can sign after certifying.
	// (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
	// Open an existing PDF
	try
	{
		$doc = new PDFDoc($input_path.'waiver.pdf');
		$widgetAnnotApproval = SignatureWidget::Create($doc, new Rect(300.0, 287.0, 376.0, 306.0), 'PDFTronApprovalSig');
		$page1 = $doc->GetPage(1);
		$page1->AnnotPushBack($widgetAnnotApproval);
		$doc->Save($output_path.'waiver_withApprovalField_output.pdf', SDFDoc::e_remove_unused);
	}
	catch (Exception $e)
	{
        echo(nl2br($e->getMessage().PHP_EOL));
        echo(nl2br($e->getTraceAsString().PHP_EOL));
        $result = false;
    }
	//////////////////// TEST 1: certify a PDF.
	try
	{
		CertifyPDF($input_path.'waiver_withApprovalField.pdf',
			'PDFTronCertificationSig',
			$input_path.'pdftron.pfx',
			'password',
			$input_path.'pdftron.bmp',
			$output_path.'waiver_withApprovalField_certified_output.pdf');
		PrintSignaturesInfo($output_path.'waiver_withApprovalField_certified_output.pdf');
	}
	catch (Exception $e)
	{
        echo(nl2br($e->getMessage().PHP_EOL));
        echo(nl2br($e->getTraceAsString().PHP_EOL));
        $result = false;
    }
	//////////////////// TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
	try
	{
		SignPDF($input_path.'waiver_withApprovalField_certified.pdf',
			'PDFTronApprovalSig',
			$input_path.'pdftron.pfx',
			'password',
			$input_path.'signature.jpg',
			$output_path.'waiver_withApprovalField_certified_approved_output.pdf');
		PrintSignaturesInfo($output_path.'waiver_withApprovalField_certified_approved_output.pdf');
	}
	catch (Exception $e)
	{
        echo(nl2br($e->getMessage().PHP_EOL));
        echo(nl2br($e->getTraceAsString().PHP_EOL));
        $result = false;
    }
	//////////////////// TEST 3: Clear a certification from a document that is certified and has an approval signature.
	try
	{
		ClearSignature($input_path.'waiver_withApprovalField_certified_approved.pdf',
			'PDFTronCertificationSig',
			$output_path.'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
		PrintSignaturesInfo($output_path.'waiver_withApprovalField_certified_approved_certcleared_output.pdf');
	}
	catch (Exception $e)
	{
        echo(nl2br($e->getMessage().PHP_EOL));
        echo(nl2br($e->getTraceAsString().PHP_EOL));
        $result = false;
    }
	//////////////////// TEST 4: Verify a document's digital signatures.
	try
	{
		if (!VerifyAllAndPrint($input_path.'waiver_withApprovalField_certified_approved.pdf', $input_path.'pdftron.cer'))
		{
			$result = false;
		}
	}
	catch (Exception $e)
	{
        echo(nl2br($e->getMessage().PHP_EOL));
        echo(nl2br($e->getTraceAsString().PHP_EOL));
        $result = false;
    }
	//////////////////// TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
	try
	{
		if (!VerifySimple($input_path.'waiver_withApprovalField_certified_approved.pdf', $input_path.'pdftron.cer'))
		{
			$result = false;
		}
	}
	catch (Exception $e)
	{
        echo(nl2br($e->getMessage().PHP_EOL));
        echo(nl2br($e->getTraceAsString().PHP_EOL));
        $result = false;
    }
	
	//////////////////// TEST 6: Custom signing API.
	// The Apryse custom signing API is a set of APIs related to cryptographic digital signatures
	// which allows users to customize the process of signing documents. Among other things, this
	// includes the capability to allow for easy integration of PDF-specific signing-related operations
	// with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
	// to system keystores, etc.
	try
	{
		CustomSigningAPI($input_path.'waiver.pdf',
			'PDFTronApprovalSig',
			$input_path.'pdftron.pfx',
			'password',
			$input_path.'pdftron.cer',
			$input_path.'signature.jpg',
			DigestAlgorithm::e_SHA256,
			true,
			$output_path.'waiver_custom_signed.pdf');
	}
	catch (Exception $e)
	{
		echo(nl2br($e->getMessage().PHP_EOL));
		echo(nl2br($e->getTraceAsString().PHP_EOL));
		$result = false;
	}

	//////////////////// TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
	// try
	// {
	// 	// Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
	// 	// For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
	// 	// Note that this url may not work in the future. A reliable solution requires using your own TSA.
	// 	$tsa_url = 'YOUR_URL_OF_TSA';
	// 	if ($tsa_url == 'YOUR_URL_OF_TSA')
	// 	{
	// 		throw new Exception('Error: The URL of your timestamp authority was not specified.');
	// 	}
	//
	// 	// Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
	// 	// For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
	// 	// Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
	// 	$trusted_cert_path = 'YOUR_CERTIFICATE';
	// 	if ($trusted_cert_path == 'YOUR_CERTIFICATE')
	// 	{
	// 		throw new Exception('Error: The path to your timestamp authority trusted root certificate was not specified.');
	// 	}
	//
	// 	if(!TimestampAndEnableLTV($input_path.'waiver.pdf',
	// 				$tsa_url,
	// 				$trusted_cert_path,
	// 				$input_path.'signature.jpg',
	// 				$output_path.'waiver_DocTimeStamp_LTV.pdf'))
	// 	{
	// 		$result = false;
	// 	}
	// }
	// catch (Exception $e)
	// {
	// 	echo(nl2br($e->getMessage().PHP_EOL));
	// 	echo(nl2br($e->getTraceAsString().PHP_EOL));
	// 	$result = false;
	// }

	//////////////////// End of tests. ////////////////////
	PDFNet::Terminate();
	if (!$result)
	{
		echo(nl2br("Tests FAILED!!!\n==========".PHP_EOL));
		return;
	}
	
	echo(nl2br("Tests successful.\n==========".PHP_EOL));
}

main();

?>
```

{% endcode %}
{% endtab %}

{% tab title="VB" %}
{% code lineNumbers="true" %}

```vb
'
' Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
'

''----------------------------------------------------------------------------------------------------------------------
'' This sample demonstrates the basic usage of the high-level digital signatures API in PDFNet.
''
'' The following steps reflect typical intended usage of the digital signatures API:
''
''	0.	Start with a PDF with or without form fields in it that one would like to lock (or, one can add a field, see (1)).
''	
''	1.	EITHER: 
''		(a) Call doc.CreateDigitalSignatureField, optionally providing a name. You receive a DigitalSignatureField.
''		-OR-
''		(b) If you didn't just create the digital signature field that you want to sign/certify, find the existing one within the 
''		document by using PDFDoc.DigitalSignatureFieldIterator or by using PDFDoc.GetField to get it by its fully qualified name.
''	
''	2.	Create a signature widget annotation, and pass the DigitalSignatureField that you just created or found. 
''		If you want it to be visible, provide a Rect argument with a non-zero width or height, and don't set the
''		NoView and Hidden flags. [Optionally, add an appearance to the annotation when you wish to sign/certify.]
''		
''	[3. (OPTIONAL) Add digital signature restrictions to the document using the field modification permissions (SetFieldPermissions) 
''		or document modification permissions functions (SetDocumentPermissions) of DigitalSignatureField. These features disallow 
''		certain types of changes to be made to the document without invalidating the cryptographic digital signature once it
''		is signed.]
''		
''	4. 	Call either CertifyOnNextSave or SignOnNextSave. There are three overloads for each one (six total):
''		a.	Taking a PKCS #12 keyfile path and its password
''		b.	Taking a buffer containing a PKCS #12 private keyfile and its password
''		c.	Taking a unique identifier of a signature handler registered with the PDFDoc. This overload is to be used
''			in the following fashion: 
''			i)		Extend and implement a new SignatureHandler. The SignatureHandler will be used to add or 
''					validate/check a digital signature.
''			ii)		Create an instance of the implemented SignatureHandler and register it with PDFDoc with 
''					pdfdoc.AddSignatureHandler(). The method returns a SignatureHandlerId.
''			iii)	Call SignOnNextSaveWithCustomHandler/CertifyOnNextSaveWithCustomHandler with the SignatureHandlerId.
''		NOTE: It is only possible to sign/certify one signature per call to the Save function.
''	
''	5.	Call pdfdoc.Save(). This will also create the digital signature dictionary and write a cryptographic signature to it.
''		IMPORTANT: If there are already signed/certified digital signature(s) in the document, you must save incrementally
''		so as to not invalidate the other signature(s). 
''
'' Additional processing can be done before document is signed. For example, UseSignatureHandler() returns an instance
'' of SDF dictionary which represents the signature dictionary (or the /V entry of the form field). This can be used to
'' add additional information to the signature dictionary (e.g. Name, Reason, Location, etc.).
''
'' Although the steps above describes extending the SignatureHandler class, this sample demonstrates the use of
'' StdSignatureHandler (a built-in SignatureHandler in PDFNet) to sign a PDF file.
''----------------------------------------------------------------------------------------------------------------------

'' In order to use .NET Framework's Cryptography library, define "USE_DOTNET_CRYPTO" and then add System.Security to
'' references list.

Imports System
Imports System.Collections.Generic
Imports System.IO
#If USE_DOTNET_CRYPTO Then
Imports System.Security.Cryptography
Imports System.Security.Cryptography.Pkcs
Imports System.Security.Cryptography.X509Certificates
#End If ' USE_DOTNET_CRYPTO

Imports pdftron
Imports pdftron.Crypto
Imports pdftron.PDF
Imports pdftron.PDF.Annots
Imports pdftron.SDF

'''''''''''''''''''' Here follows an example of how to implement a custom signature handler. ''''''''''
#If USE_DOTNET_CRYPTO Then
Class DotNetCryptoSignatureHandler
	Inherits SignatureHandler
	Private m_data As List(Of Byte)
	Private m_signingCert As String
	Private m_certPassword As String

	Public Sub New(ByVal signingCert As String, ByVal password As String)
		m_signingCert = signingCert
		m_certPassword = password
		m_data = New List(Of Byte)()
	End Sub

	Public Overrides Sub AppendData(ByVal data As Byte())
		m_data.AddRange(data)
	End Sub

	Public Overrides Function Reset() As Boolean
		m_data.Clear()
		Return (True)
	End Function

	Public Overrides Function CreateSignature() As Byte()
		Try
			Dim ci As New ContentInfo(m_data.ToArray())
			Dim sc As New SignedCms(ci, True)
			Dim cert As New X509Certificate2(m_signingCert, m_certPassword)
			Dim cs As New CmsSigner()
			cs.Certificate = cert
			cs.DigestAlgorithm = New Oid("2.16.840.1.101.3.4.2.1") ' SHA-256
			sc.ComputeSignature(cs)
			Dim sig As Byte() = sc.Encode()
			Return (sig)
		Catch e As Exception
			Console.[Error].WriteLine(e)
		End Try
		Return (Nothing)
	End Function

	Public Overrides Function GetName() As String
		Return ("Adobe.PPKLite")
	End Function

	Protected Overrides Sub Finalize()
		Try
			Console.Out.WriteLine("DotNetCryptoSignatureHandler Destructor.")
		Finally
			MyBase.Finalize()
		End Try
	End Sub
End Class
#End If ' USE_DOTNET_CRYPTO
'''''''''' End of the DotNetCryptoSignatureHandler custom handler code. ''''''''''''''''''''

Module Module1
	Dim input_path As String = "../../../../TestFiles/"
	Dim output_path As String = "../../../../TestFiles/Output/"

	Public Function VerifySimple(ByVal in_docpath As String, ByVal in_public_key_file_path As String) As Boolean
		Using doc As PDFDoc = New PDFDoc(in_docpath)
			Console.WriteLine("==========")
			Dim opts As VerificationOptions = New VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving)

			'Add trust root to store of trusted certificates contained in VerificationOptions.
			opts.AddTrustedCertificate(in_public_key_file_path, System.Convert.ToInt32(VerificationOptions.CertificateTrustFlag.e_default_trust) Or System.Convert.ToInt32(VerificationOptions.CertificateTrustFlag.e_certification_trust))

			Dim result As PDFDoc.SignaturesVerificationStatus = doc.VerifySignedDigitalSignatures(opts)
			Select Case result

				Case PDFDoc.SignaturesVerificationStatus.e_unsigned
					Console.WriteLine("Document has no signed signature fields.")
					Return False
				' e_failure == bad doc status, digest status, or permissions status
				' (i.e. does not include trust issues, because those are flaky due to being network/config-related) 
				Case PDFDoc.SignaturesVerificationStatus.e_failure
					Console.WriteLine("Hard failure in verification on at least one signature.")
					Return False
				Case PDFDoc.SignaturesVerificationStatus.e_untrusted
					Console.WriteLine("Could not verify trust for at least one signature.")
					Return False
				Case PDFDoc.SignaturesVerificationStatus.e_unsupported
					'If necessary, call GetUnsupportedFeatures on VerificationResult to check which
					' unsupported features were encountered (requires verification using 'detailed' APIs)
					Console.WriteLine("At least one signature contains unsupported features.")
					Return False
				'unsigned sigs skipped; parts of document may be unsigned (check GetByteRanges on signed sigs to find out)
				Case PDFDoc.SignaturesVerificationStatus.e_verified
					Console.WriteLine("All signed signatures in document verified.")
					Return True
				Case Else
					Throw New Exception("unrecognized document verification status")
			End Select
		End Using
	End Function

	Public Function VerifyAllAndPrint(ByVal in_docpath As String, ByVal in_public_key_file_path As String) As Boolean
		Using doc As PDFDoc = New PDFDoc(in_docpath)
			Console.WriteLine("==========")
			Dim opts As VerificationOptions = New VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving)

			' Trust the public certificate we use for signing.
			Dim trusted_cert_buf As Byte() = File.ReadAllBytes(in_public_key_file_path)
			opts.AddTrustedCertificate(trusted_cert_buf, System.Convert.ToInt32(VerificationOptions.CertificateTrustFlag.e_default_trust) Or System.Convert.ToInt32(VerificationOptions.CertificateTrustFlag.e_certification_trust))

			' Iterate over the signatures and verify all of them.
			Dim digsig_fitr As DigitalSignatureFieldIterator = doc.GetDigitalSignatureFieldIterator()
			Dim verification_status As Boolean = True
			While digsig_fitr.HasNext()
				Dim curr As DigitalSignatureField = digsig_fitr.Current()
				Dim result As VerificationResult = curr.Verify(opts)

				If result.GetVerificationStatus() Then
					Console.Write("Signature verified, ")
				Else
					Console.Write("Signature verification failed, ")
					verification_status = False
				End If

				Console.WriteLine("objnum: {0}", curr.GetSDFObj().GetObjNum())

				Select Case result.GetDigestAlgorithm()
					Case DigestAlgorithm.Type.e_sha1:
						Console.WriteLine("Digest algorithm: SHA-1")
					Case DigestAlgorithm.Type.e_sha256:
						Console.WriteLine("Digest algorithm: SHA-256")
					Case DigestAlgorithm.Type.e_sha384:
						Console.WriteLine("Digest algorithm: SHA-384")
					Case DigestAlgorithm.Type.e_sha512:
						Console.WriteLine("Digest algorithm: SHA-512")
					Case DigestAlgorithm.Type.e_ripemd160:
						Console.WriteLine("Digest algorithm: RIPEMD-160")
					Case DigestAlgorithm.Type.e_unknown_digest_algorithm:
						Console.WriteLine("Digest algorithm: unknown")
					Case Else
						Throw New Exception("unrecognized digest algorithm")
				End Select

				Console.WriteLine("Detailed verification result: " & vbLf & vbTab & "{0}" & vbLf & vbTab & "{1}" & vbLf & vbTab & "{2}" & vbLf & vbTab & "{3}", result.GetDocumentStatusAsString(), result.GetDigestStatusAsString(), result.GetTrustStatusAsString(), result.GetPermissionsStatusAsString())

				Dim changes As DisallowedChange() = result.GetDisallowedChanges()

				For Each it2 As DisallowedChange In changes
					Console.WriteLine(vbTab & "Disallowed change: {0}, objnum: {1}", it2.GetTypeAsString(), it2.GetObjNum())
				Next

				' Get and print all the detailed trust-related results, if they are available.
				If result.HasTrustVerificationResult() Then
					Dim trust_verification_result As TrustVerificationResult = result.GetTrustVerificationResult()
					Console.WriteLine(If(trust_verification_result.WasSuccessful(), "Trust verified.", "Trust not verifiable."))
					Console.WriteLine(trust_verification_result.GetResultString())
					Dim time_of_verification As ULong = trust_verification_result.GetTimeOfTrustVerification()

					Select Case trust_verification_result.GetTimeOfTrustVerificationEnum()
						Case VerificationOptions.TimeMode.e_current
							Console.WriteLine("Trust verification attempted with respect to current time (as epoch time): {0}", time_of_verification)
						Case VerificationOptions.TimeMode.e_signing
							Console.WriteLine("Trust verification attempted with respect to signing time (as epoch time): {0}", time_of_verification)
						Case VerificationOptions.TimeMode.e_timestamp
							Console.WriteLine("Trust verification attempted with respect to secure embedded timestamp (as epoch time): {0}", time_of_verification)
						Case Else
							Throw New Exception("unrecognized time enum value")
					End Select

					If trust_verification_result.GetCertPath().Length = 0 Then
						Console.WriteLine("Could not print certificate path.")
					Else
						Console.WriteLine("Certificate path:")
						Dim cert_path As X509Certificate() = trust_verification_result.GetCertPath()

						For j As Integer = 0 To cert_path.Length - 1
							Console.WriteLine(vbTab & "Certificate:")
							Dim full_cert As X509Certificate = cert_path(j)
							Console.WriteLine(vbTab & vbTab & "Issuer names:")
							Dim issuer_dn As X501AttributeTypeAndValue() = full_cert.GetIssuerField().GetAllAttributesAndValues()

							For i As Integer = 0 To issuer_dn.Length - 1
								Console.WriteLine(vbTab & vbTab & vbTab & issuer_dn(i).GetStringValue())
							Next

							Console.WriteLine(vbTab & vbTab & "Subject names:")
							Dim subject_dn As X501AttributeTypeAndValue() = full_cert.GetSubjectField().GetAllAttributesAndValues()

							For i As Integer = 0 To subject_dn.Length - 1
								Console.WriteLine(vbTab & vbTab & vbTab & subject_dn(i).GetStringValue())
							Next

							Console.WriteLine(vbTab & vbTab & "Extensions:")

							For i As Integer = 0 To full_cert.GetExtensions().Length - 1
								Console.WriteLine(vbTab & vbTab & vbTab & full_cert.GetExtensions()(i).ToString())
							Next
						Next
					End If
				Else
					Console.WriteLine("No detailed trust verification result available.")
				End If

				Dim unsupported_features As String() = result.GetUnsupportedFeatures()

				If unsupported_features.Length > 0 Then
					Console.WriteLine("Unsupported features:")

					For i As Integer = 0 To unsupported_features.Length - 1
						Console.WriteLine(vbTab & unsupported_features(i))
					Next
				End If

				Console.WriteLine("==========")
				digsig_fitr.[Next]()
			End While

			Return verification_status
		End Using
	End Function



	Sub CertifyPDF(ByVal in_docpath As String, ByVal in_cert_field_name As String, ByVal in_private_key_file_path As String, ByVal in_keyfile_password As String, ByVal in_appearance_image_path As String, ByVal in_outpath As String)
		Console.Out.WriteLine("================================================================================")
		Console.Out.WriteLine("Certifying PDF document")

		' Open an existing PDF
		Using doc As PDFDoc = New PDFDoc(in_docpath)
			Console.Out.WriteLine("PDFDoc has " & (If(doc.HasSignatures(), "signatures", "no signatures")))

			Dim page1 As Page = doc.GetPage(1)

			' Create a text field that we can lock using the field permissions feature.
			Dim annot1 As TextWidget = TextWidget.Create(doc, New Rect(143, 440, 350, 460), "asdf_test_field")
			page1.AnnotPushBack(annot1)

			' Create a new signature form field in the PDFDoc. The name argument is optional;
			' leaving it empty causes it to be auto-generated. However, you may need the name for later.
			' Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a
			' Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible.
			Dim certification_sig_field As DigitalSignatureField = doc.CreateDigitalSignatureField(in_cert_field_name)
			Dim widgetAnnot As SignatureWidget = SignatureWidget.Create(doc, New Rect(143, 287, 219, 306), certification_sig_field)
			page1.AnnotPushBack(widgetAnnot)

			' (OPTIONAL) Add an appearance to the signature field.
			Dim img As Image = Image.Create(doc, in_appearance_image_path)
			widgetAnnot.CreateSignatureAppearance(img)

			' Add permissions. Lock the random text field.
			Console.Out.WriteLine("Adding document permissions.")
			certification_sig_field.SetDocumentPermissions(DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed)

			' Prepare to lock the text field that we created earlier.
			Console.Out.WriteLine("Adding field permissions.")
			Dim fields_to_lock As String() = New String(0) {}
			fields_to_lock(0) = "asdf_test_field"
			certification_sig_field.SetFieldPermissions(DigitalSignatureField.FieldPermissions.e_include, fields_to_lock)

#If USE_DOTNET_CRYPTO Then
			Dim sigHandler As DotNetCryptoSignatureHandler = New DotNetCryptoSignatureHandler(in_private_key_file_path, in_keyfile_password)
			Dim sigHandlerId As SignatureHandlerId = doc.AddSignatureHandler(sigHandler)
			certification_sig_field.CertifyOnNextSaveWithCustomHandler(sigHandlerId)
			' Add to the digital signature dictionary a SubFilter name that uniquely identifies the signature format 
			' for verification tools. As an example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, 
			' so we embed one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this 
			' when using the StdSignatureHandler.
			Dim f_obj As Obj = certification_sig_field.GetSDFObj()
			f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached")
#Else
			certification_sig_field.CertifyOnNextSave(in_private_key_file_path, in_keyfile_password)
#End If ' USE_DOTNET_CRYPTO

			' (OPTIONAL) Add more information to the signature dictionary.
			certification_sig_field.SetLocation("Vancouver, BC")
			certification_sig_field.SetReason("Document certification.")
			certification_sig_field.SetContactInfo("www.pdftron.com")

			' Save the PDFDoc. Once the method below is called, PDFNet will also sign the document using the information provided.
			doc.Save(in_outpath, 0)
		End Using

		Console.Out.WriteLine("================================================================================")
	End Sub

	Sub SignPDF(ByVal in_docpath As String, ByVal in_approval_field_name As String, ByVal in_private_key_file_path As String, ByVal in_keyfile_password As String, ByVal in_appearance_img_path As String, ByVal in_outpath As String)
		Console.Out.WriteLine("================================================================================")
		Console.Out.WriteLine("Signing PDF document")

		' Open an existing PDF
		Using doc As PDFDoc = New PDFDoc(in_docpath)
			' Retrieve the unsigned approval signature field.
			Dim found_approval_field As Field = doc.GetField(in_approval_field_name)
			Dim found_approval_signature_digsig_field As DigitalSignatureField = New DigitalSignatureField(found_approval_field)

			' (OPTIONAL) Add an appearance to the signature field.
			Dim img As Image = Image.Create(doc, in_appearance_img_path)
			Dim found_approval_signature_widget As SignatureWidget = New SignatureWidget(found_approval_field.GetSDFObj())
			found_approval_signature_widget.CreateSignatureAppearance(img)

			' Prepare the signature and signature handler for signing.
#If USE_DOTNET_CRYPTO Then
			Dim sigHandler As DotNetCryptoSignatureHandler = New DotNetCryptoSignatureHandler(in_private_key_file_path, in_keyfile_password)
			Dim sigHandlerId As SignatureHandlerId = doc.AddSignatureHandler(sigHandler)
			found_approval_signature_digsig_field.SignOnNextSaveWithCustomHandler(sigHandlerId)
#Else
			found_approval_signature_digsig_field.SignOnNextSave(in_private_key_file_path, in_keyfile_password)
			' Add a SubFilter name that uniquely identifies the signature format for verification tools. As an 
			' example, the custom handler defined in this file uses the CMS/PKCS #7 detached format, so we embed 
			' one of the standard predefined SubFilter values: "adbe.pkcs7.detached". It is not necessary to do this 
			' when using the StdSignatureHandler.
			Dim f_obj As Obj = found_approval_signature_digsig_field.GetSDFObj()
			f_obj.FindObj("V").PutName("SubFilter", "adbe.pkcs7.detached")
#End If ' USE_DOTNET_CRYPTO

			' The actual approval signing will be done during the following incremental save operation.
			doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental)
		End Using

		Console.Out.WriteLine("================================================================================")
	End Sub

	Sub ClearSignature(ByVal in_docpath As String, ByVal in_digsig_field_name As String, ByVal in_outpath As String)
		Console.Out.WriteLine("================================================================================")
		Console.Out.WriteLine("Clearing certification signature")

		Using doc As PDFDoc = New PDFDoc(in_docpath)
			Dim digsig As DigitalSignatureField = New DigitalSignatureField(doc.GetField(in_digsig_field_name))

			Console.Out.WriteLine("Clearing signature: " & in_digsig_field_name)
			digsig.ClearSignature()

			If Not digsig.HasCryptographicSignature() Then
				Console.Out.WriteLine("Cryptographic signature cleared properly.")
			End If

			' Save incrementally so as to not invalidate other signatures from previous saves.
			doc.Save(in_outpath, SDFDoc.SaveOptions.e_incremental)
		End Using

		Console.Out.WriteLine("================================================================================")
	End Sub

	Sub PrintSignaturesInfo(ByVal in_docpath As String)
		Console.Out.WriteLine("================================================================================")
		Console.Out.WriteLine("Reading and printing digital signature information")

		Using doc As PDFDoc = New PDFDoc(in_docpath)

			If Not doc.HasSignatures() Then
				Console.Out.WriteLine("Doc has no signatures.")
				Console.Out.WriteLine("================================================================================")
				Return
			Else
				Console.Out.WriteLine("Doc has signatures.")
			End If

			Dim fitr As FieldIterator = doc.GetFieldIterator()

			While fitr.HasNext()
				If fitr.Current().IsLockedByDigitalSignature() Then
					Console.Out.WriteLine("==========" & vbLf & "Field locked by a digital signature")
				Else
					Console.Out.WriteLine("==========" & vbLf & "Field not locked by a digital signature")
				End If

				Console.Out.WriteLine("Field name: " & fitr.Current().GetName())
				Console.Out.WriteLine("==========")
				fitr.Next()
			End While

			Console.Out.WriteLine("====================" & vbLf & "Now iterating over digital signatures only." & vbLf & "====================")

			Dim digsig_fitr As DigitalSignatureFieldIterator = doc.GetDigitalSignatureFieldIterator()
			While digsig_fitr.HasNext()
				Console.Out.WriteLine("==========")
				Console.Out.WriteLine("Field name of digital signature: " & New Field(digsig_fitr.Current().GetSDFObj()).GetName())
				Dim digsigfield As DigitalSignatureField = digsig_fitr.Current()

				If Not digsigfield.HasCryptographicSignature() Then
					Console.Out.WriteLine("Either digital signature field lacks a digital signature dictionary, " &
					"or digital signature dictionary lacks a cryptographic Contents entry. " &
					"Digital signature field is not presently considered signed." &
					vbLf & "==========")
					digsig_fitr.Next()
					Continue While
				End If

				Dim cert_count As Integer = digsigfield.GetCertCount()
				Console.Out.WriteLine("Cert count: " & cert_count)
				For i As Integer = 0 To cert_count - 1
					Dim cert As Byte() = digsigfield.GetCert(i)
					Console.Out.WriteLine("Cert #" & i & " size: " & cert.Length)
				Next

				Dim subfilter As DigitalSignatureField.SubFilterType = digsigfield.GetSubFilter()
				Console.Out.WriteLine("Subfilter type: " & CInt(subfilter))

				If subfilter <> DigitalSignatureField.SubFilterType.e_ETSI_RFC3161 Then
					Console.Out.WriteLine("Signature's signer: " & digsigfield.GetSignatureName())
					Dim signing_time As pdftron.PDF.Date = digsigfield.GetSigningTime()
					If signing_time.IsValid() Then
						Console.Out.WriteLine("Signing time is valid.")
					End If
					Console.Out.WriteLine("Location: " & digsigfield.GetLocation())
					Console.Out.WriteLine("Reason: " & digsigfield.GetReason())
					Console.Out.WriteLine("Contact info: " & digsigfield.GetContactInfo())
				Else
					Console.Out.WriteLine("SubFilter == e_ETSI_RFC3161 (DocTimeStamp; no signing info)" & vbLf)
				End If

				Console.Out.WriteLine((If((digsigfield.HasVisibleAppearance()), "Visible", "Not visible")))

				Dim digsig_doc_perms As DigitalSignatureField.DocumentPermissions = digsigfield.GetDocumentPermissions()
				Dim locked_fields As String() = digsigfield.GetLockedFields()
				For Each field_name As String In locked_fields
					Console.Out.WriteLine("This digital signature locks a field named: " & field_name)
				Next

				Select Case digsig_doc_perms
					Case DigitalSignatureField.DocumentPermissions.e_no_changes_allowed
						Console.Out.WriteLine("No changes to the document can be made without invalidating this digital signature.")
					Case DigitalSignatureField.DocumentPermissions.e_formfilling_signing_allowed
						Console.Out.WriteLine("Page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
					Case DigitalSignatureField.DocumentPermissions.e_annotating_formfilling_signing_allowed
						Console.Out.WriteLine("Annotating, page template instantiation, form filling, and signing digital signatures are allowed without invalidating this digital signature.")
					Case DigitalSignatureField.DocumentPermissions.e_unrestricted
						Console.Out.WriteLine("Document not restricted by this digital signature.")
					Case Else
						Throw New Exception("Unrecognized digital signature document permission level.")
				End Select

				Console.Out.WriteLine("==========")
				digsig_fitr.Next()
			End While
		End Using

		Console.Out.WriteLine("================================================================================")
	End Sub

	Sub CustomSigningAPI(ByVal doc_path As String, ByVal cert_field_name As String, ByVal private_key_file_path As String, ByVal keyfile_password As String, ByVal public_key_file_path As String, ByVal appearance_image_path As String, ByVal digest_algorithm_type As DigestAlgorithm.Type, ByVal PAdES_signing_mode As Boolean, ByVal output_path As String)
		Console.Out.WriteLine("================================================================================")
		Console.Out.WriteLine("Custom signing PDF document")

		Using doc As PDFDoc = New PDFDoc(doc_path)
			Dim page1 As Page = doc.GetPage(1)

			Dim digsig_field As DigitalSignatureField = doc.CreateDigitalSignatureField(cert_field_name)
			Dim widgetAnnot As SignatureWidget = SignatureWidget.Create(doc, New Rect(143, 287, 219, 306), digsig_field)
			page1.AnnotPushBack(widgetAnnot)

			' (OPTIONAL) Add an appearance to the signature field.
			Dim img As Image = Image.Create(doc, appearance_image_path)
			widgetAnnot.CreateSignatureAppearance(img)

			' Create a digital signature dictionary inside the digital signature field, in preparation for signing.
			digsig_field.CreateSigDictForCustomSigning("Adobe.PPKLite",
				If(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.
			Dim current_date As pdftron.PDF.Date = New pdftron.PDF.Date()
			current_date.SetCurrentTime()
			digsig_field.SetSigDictTimeOfSigning(current_date)

			doc.Save(output_path, SDFDoc.SaveOptions.e_incremental)

			' Digest the relevant bytes of the document in accordance with ByteRanges surrounding the signature.
			Dim pdf_digest As Byte() = digsig_field.CalculateDigest(digest_algorithm_type)

			Dim signer_cert As X509Certificate = New X509Certificate(public_key_file_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.
			Dim pades_versioned_ess_signing_cert_attribute As Byte() = DigitalSignatureField.GenerateESSSigningCertPAdESAttribute(signer_cert, digest_algorithm_type)

			' 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.
			Dim signedAttrs As Byte() = DigitalSignatureField.GenerateCMSSignedAttributes(pdf_digest, pades_versioned_ess_signing_cert_attribute)

			' Calculate the digest of the signedAttrs (i.e. Not the PDF digest, this time).
			Dim signedAttrs_digest As Byte() = DigestAlgorithm.CalculateDigest(digest_algorithm_type, signedAttrs)

			'''''''''''''''''''''''''''' custom digest signing starts '''''''''''''''''''''''''''''
			' At this point, you can sign the digest (for example, with HSM). We use our own SignDigest function instead here as an example,
			' which you can also use for your purposes if necessary as an alternative to the handler/callback APIs (i.e. Certify/SignOnNextSave).
			Dim signature_value As Byte() = DigestAlgorithm.SignDigest(signedAttrs_digest, digest_algorithm_type, private_key_file_path, keyfile_password)
			'''''''''''''''''''''''''''' custom digest signing ends '''''''''''''''''''''''''''''''

			' Then, load all your chain certificates into a container of X509Certificate.
			Dim chain_certs() As X509Certificate = {}

			' Then, create ObjectIdentifiers for the algorithms you have used.
			' Here we use digest_algorithm_type (usually SHA256) for hashing, And RSAES-PKCS1-v1_5 (specified in the private key) for signing.
			Dim digest_algorithm_oid As ObjectIdentifier = New ObjectIdentifier(digest_algorithm_type)
			Dim signature_algorithm_oid As ObjectIdentifier = New ObjectIdentifier(ObjectIdentifier.Predefined.e_RSA_encryption_PKCS1)

			' Then, put the CMS signature components together.
			Dim cms_signature As Byte() = 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, output_path)
		End Using

		Console.Out.WriteLine("================================================================================")
	End Sub

	Public Function TimestampAndEnableLTV(ByVal doc_path As String, ByVal tsa_url As String, ByVal trusted_cert_path As String, ByVal appearance_img_path As String, ByVal output_path As String) As Boolean
		Using doc As PDFDoc = New PDFDoc(doc_path)
			Dim doctimestamp_signature_field As DigitalSignatureField = doc.CreateDigitalSignatureField()
			Dim tst_config As TimestampingConfiguration = New TimestampingConfiguration(tsa_url)
			Dim opts As VerificationOptions = New VerificationOptions(VerificationOptions.SignatureVerificationSecurityLevel.e_compatibility_and_archiving)
			opts.AddTrustedCertificate(trusted_cert_path)
			opts.EnableOnlineCRLRevocationChecking(True)
			Dim widgetAnnot As SignatureWidget = SignatureWidget.Create(doc, New Rect(0, 100, 200, 150), doctimestamp_signature_field)
			doc.GetPage(1).AnnotPushBack(widgetAnnot)
			Dim widgetObj As Obj = widgetAnnot.GetSDFObj()
			Dim img As Image = Image.Create(doc, appearance_img_path)
			widgetAnnot.CreateSignatureAppearance(img)
			Console.WriteLine("Testing timestamping configuration.")
			Dim config_result As TimestampingResult = tst_config.TestConfiguration(opts)

			If config_result.GetStatus() Then
				Console.WriteLine("Success: timestamping configuration usable. Attempting to timestamp.")
			Else
				Console.WriteLine(config_result.GetString())

				If config_result.HasResponseVerificationResult() Then
					Dim tst_result As EmbeddedTimestampVerificationResult = config_result.GetResponseVerificationResult()
					Console.WriteLine("CMS digest status: {0}" & vbLf, tst_result.GetCMSDigestStatusAsString())
					Console.WriteLine("Message digest status: {0}" & vbLf, tst_result.GetMessageImprintDigestStatusAsString())
					Console.WriteLine("Trust status: {0}" & vbLf, tst_result.GetTrustStatusAsString())
				End If

				Return False
			End If

			doctimestamp_signature_field.TimestampOnNextSave(tst_config, opts)
			doc.Save(output_path, SDFDoc.SaveOptions.e_incremental)
			Console.WriteLine("Timestamping successful. Adding LTV information for DocTimeStamp signature.")
			Dim timestamp_verification_result As VerificationResult = doctimestamp_signature_field.Verify(opts)

			If Not doctimestamp_signature_field.EnableLTVOfflineVerification(timestamp_verification_result) Then
				Console.WriteLine("Could not enable LTV for DocTimeStamp.")
				Return False
			End If

			doc.Save(output_path, SDFDoc.SaveOptions.e_incremental)
			Console.WriteLine("Added LTV information for DocTimeStamp signature successfully.")
			Return True
		End Using
	End Function

	Dim pdfNetLoader As PDFNetLoader
	Sub New()
		pdfNetLoader = pdftron.PDFNetLoader.Instance()
	End Sub

	Sub Main()
		' Initialize PDFNet
		PDFNet.Initialize(PDFTronLicense.Key)

		Dim result As Boolean = True

		'''''''''''''''''''' TEST 0: 
		' Create an approval signature field that we can sign after certifying.
		' (Must be done before calling CertifyOnNextSave/SignOnNextSave/WithCustomHandler.)
		Try
			Using doc As PDFDoc = New PDFDoc(input_path & "waiver.pdf")
				Dim approval_signature_field As DigitalSignatureField = doc.CreateDigitalSignatureField("PDFTronApprovalSig")
				Dim widgetAnnotApproval As SignatureWidget = SignatureWidget.Create(doc, New Rect(300, 287, 376, 306), approval_signature_field)
				Dim page1 As Page = doc.GetPage(1)
				page1.AnnotPushBack(widgetAnnotApproval)
				doc.Save(output_path & "waiver_withApprovalField_output.pdf", SDFDoc.SaveOptions.e_remove_unused)
			End Using
		Catch e As Exception
			Console.[Error].WriteLine(e)
			result = False
		End Try

		'''''''''''''''''''' TEST 1: certify a PDF.
		Try
			CertifyPDF(input_path & "waiver_withApprovalField.pdf", "PDFTronCertificationSig", input_path & "pdftron.pfx", "password", input_path & "pdftron.bmp", output_path & "waiver_withApprovalField_certified_output.pdf")
			PrintSignaturesInfo(output_path & "waiver_withApprovalField_certified_output.pdf")
		Catch e As Exception
			Console.[Error].WriteLine(e)
			result = False
		End Try

		'''''''''''''''''''' TEST 2: approval-sign an existing, unsigned signature field in a PDF that already has a certified signature field.
		Try
			SignPDF(input_path & "waiver_withApprovalField_certified.pdf", "PDFTronApprovalSig", input_path & "pdftron.pfx", "password", input_path & "signature.jpg", output_path & "waiver_withApprovalField_certified_approved_output.pdf")
			PrintSignaturesInfo(output_path & "waiver_withApprovalField_certified_approved_output.pdf")
		Catch e As Exception
			Console.[Error].WriteLine(e)
			result = False
		End Try

		'''''''''''''''''''' TEST 3: Clear a certification from a document that is certified and has an approval signature.
		Try
			ClearSignature(input_path & "waiver_withApprovalField_certified_approved.pdf", "PDFTronCertificationSig", output_path & "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
			PrintSignaturesInfo(output_path & "waiver_withApprovalField_certified_approved_certcleared_output.pdf")
		Catch e As Exception
			Console.[Error].WriteLine(e)
			result = False
		End Try

		'''''''''''''''''''' TEST 4: Verify a document's digital signatures.
		Try
					If Not VerifyAllAndPrint(input_path & "waiver_withApprovalField_certified_approved.pdf", input_path & "pdftron.cer") Then
						result = False
					End If
		Catch e As Exception
			Console.[Error].WriteLine(e)
			result = False
		End Try

		'''''''''''''''''''' TEST 5: Verify a document's digital signatures in a simple fashion using the document API.
		Try
			If Not VerifySimple(input_path + "waiver_withApprovalField_certified_approved.pdf", input_path + "pdftron.cer") Then
				result = False
			End If
		Catch e As Exception
			Console.[Error].WriteLine(e)
			result = False
		End Try

		'''''''''''''''''''' TEST 6 Custom signing API.
		' The Apryse custom signing API Is a set of APIs related to cryptographic digital signatures
		' which allows users to customize the process of signing documents. Among other things, this
		' includes the capability to allow for easy integration of PDF-specific signing-related operations
		' with access to Hardware Security Module (HSM) tokens/devices, access to cloud keystores, access
		' to system keystores, etc.
		Try
			CustomSigningAPI(input_path & "waiver.pdf",
				"PDFTronApprovalSig",
				input_path & "pdftron.pfx",
				"password",
				input_path & "pdftron.cer",
				input_path & "signature.jpg",
				DigestAlgorithm.Type.e_sha256,
				True,
				output_path & "waiver_custom_signed.pdf")
		Catch e As Exception
			Console.[Error].WriteLine(e)
			result = False
		End Try

		'''''''''''''''''''' TEST 7: Timestamp a document, then add Long Term Validation (LTV) information for the DocTimeStamp.
		' Try
		' 	' Replace YOUR_URL_OF_TSA with the timestamp authority (TSA) URL to use during timestamping.
		' 	' For example, as of July 2024, http://timestamp.globalsign.com/tsa/r6advanced1 was usable.
		' 	' Note that this url may not work in the future. A reliable solution requires using your own TSA.
		' 	Dim tsa_url As String = "YOUR_URL_OF_TSA"
		' 	If String.Compare(tsa_url, "YOUR_URL_OF_TSA") = 0 Then
		' 		Throw New Exception("Error: The URL of your timestamp authority was not specified.")
		' 	End If
		'
		' 	' Replace YOUR_CERTIFICATE with the trusted root certificate corresponding to the chain used by the timestamp authority.
		' 	' For example, as of July 2024, https://secure.globalsign.com/cacert/gstsacasha384g4.crt was usable.
		' 	' Note that this certificate may not work in the future. A reliable solution requires using your own TSA certificate.
		' 	Dim trusted_cert_path As String = "YOUR_CERTIFICATE"
		' 	If String.Compare(trusted_cert_path, "YOUR_CERTIFICATE") = 0 Then
		' 		Throw New Exception("Error: The path to your timestamp authority trusted root certificate was not specified.")
		' 	End If
		'
		' 	If Not TimestampAndEnableLTV(input_path + "waiver.pdf",
		' 			tsa_url,
		' 			trusted_cert_path,
		' 			input_path + "signature.jpg",
		' 			output_path + "waiver_DocTimeStamp_LTV.pdf") Then
		' 		result = False
		' 	End If
		'
		' Catch e As Exception
		' 	Console.[Error].WriteLine(e)
		' 	result = False
		' End Try

		'''''''''''''''''''' End of tests. ''''''''''''''''''''
		PDFNet.Terminate()
		If result Then
			Console.Out.WriteLine("Tests successful." & vbLf & "==========")
		Else
			Console.Out.WriteLine("Tests FAILED!!!" & vbLf & "==========")
		End If
	End Sub
End Module
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/core/get-started/samples/digitalsignaturestest.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
