'
' Copyright (c) 2001-2025 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 & "apryse.pfx", "password", input_path & "apryse.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 & "apryse.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 & "apryse.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 + "apryse.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 & "apryse.pfx",
				"password",
				input_path & "apryse.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 June 2025, 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 June 2025, https://secure.globalsign.net/cacert/root-r6.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
