Table of Contents

Signing PDFs (VB.Net / netframework)

Note

This demo is available in your FlexCel installation at <FlexCel Install Folder>\samples\vb\VS2022\netframework\25.Printing and Exporting\35.Signing Pdfs and also at https:​//​github.​com/​tmssoftware/​TMS-​FlexCel.​NET-​demos/​tree/​master/​vb/​VS2022/​netframework/​Modules/​25.​Printing and Exporting/35.Signing Pdfs

Overview

In this example we will show how to add a visible or invisible signature to a generated PDF file.

Concepts

  • In order to sign a PDF file you will need a certificate issued by a valid Certificate Authority, or one issued by yourself. In this example we will use a self signed certificate. This certificate will not validate by default when you open it in Acrobat, you need to add it to your trusted list.

  • The default algorithm for CmsSigner .NET class is SHA-1, which is known to have vulnerabilities and shouldn't be used anymore. So in this example we use SHA512 instead by changing the DigestAlgorithm.

  • In order to sign a file, FlexCel will write a requirement for Acrobat 8 or newer in the generated files. This is because only Acrobat 8 or newer support SHA512. Older versions of acrobat will still display the pages but will not validate the signature.

  • We provide a default signing implementation using standard .NET crypto classes**.** You can still create your own signature engine by using a third party cryptography library or by calling CryptoApi in windows via p/invoke. This is explained in the section Signing PDF Files in the PDF exporting guide.

Files

mainForm.Designer.vb

Namespace SigningPdfs
	Partial Public Class mainForm
		''' <summary>
		''' Required designer variable.
		''' </summary>
		Private components As System.ComponentModel.IContainer = Nothing

		''' <summary>
		''' Clean up any resources being used.
		''' </summary>
		''' <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		Protected Overrides Sub Dispose(ByVal disposing As Boolean)
			If disposing AndAlso (components IsNot Nothing) Then
				components.Dispose()
			End If
			MyBase.Dispose(disposing)
		End Sub

		#Region "Windows Form Designer generated code"

		''' <summary>
		''' Required method for Designer support - do not modify
		''' the contents of this method with the code editor.
		''' </summary>
		Private Sub InitializeComponent()
			Me.btnCreateAndSign = New System.Windows.Forms.Button()
			Me.cbVisibleSignature = New System.Windows.Forms.CheckBox()
			Me.OpenExcelDialog = New System.Windows.Forms.OpenFileDialog()
			Me.savePdfDialog = New System.Windows.Forms.SaveFileDialog()
			Me.SignaturePicture = New System.Windows.Forms.PictureBox()
			Me.OpenImageDialog = New System.Windows.Forms.OpenFileDialog()
			CType(Me.SignaturePicture, System.ComponentModel.ISupportInitialize).BeginInit()
			Me.SuspendLayout()
			' 
			' btnCreateAndSign
			' 
			Me.btnCreateAndSign.Image = My.Resources.acroread
			Me.btnCreateAndSign.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft
			Me.btnCreateAndSign.Location = New System.Drawing.Point(24, 25)
			Me.btnCreateAndSign.Name = "btnCreateAndSign"
			Me.btnCreateAndSign.Size = New System.Drawing.Size(155, 30)
			Me.btnCreateAndSign.TabIndex = 0
			Me.btnCreateAndSign.Text = "Create and Sign Pdf"
			Me.btnCreateAndSign.UseVisualStyleBackColor = True
'			Me.btnCreateAndSign.Click += New System.EventHandler(Me.btnCreateAndSign_Click)
			' 
			' cbVisibleSignature
			' 
			Me.cbVisibleSignature.AutoSize = True
			Me.cbVisibleSignature.Location = New System.Drawing.Point(24, 78)
			Me.cbVisibleSignature.Name = "cbVisibleSignature"
			Me.cbVisibleSignature.Size = New System.Drawing.Size(167, 17)
			Me.cbVisibleSignature.TabIndex = 1
			Me.cbVisibleSignature.Text = "Visible Signature (in last page)"
			Me.cbVisibleSignature.UseVisualStyleBackColor = True
'			Me.cbVisibleSignature.CheckedChanged += New System.EventHandler(Me.cbVisibleSignature_CheckedChanged)
			' 
			' OpenExcelDialog
			' 
			Me.OpenExcelDialog.DefaultExt = "xls"
			Me.OpenExcelDialog.Filter = "Excel Files|*.xls;*.xlsx;*.xlsm|Excel 97/2003|*.xls|Excel 2007|*.xlsx;*.xlsm|All files|*.*"
			Me.OpenExcelDialog.Title = "Select Excel file to convert..."
			' 
			' savePdfDialog
			' 
			Me.savePdfDialog.DefaultExt = "pdf"
			Me.savePdfDialog.Filter = "Pdf Files|*.pdf"
			Me.savePdfDialog.Title = "Select where to save the file..."
			' 
			' SignaturePicture
			' 
			Me.SignaturePicture.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
			Me.SignaturePicture.Image = My.Resources.sign
			Me.SignaturePicture.Location = New System.Drawing.Point(24, 110)
			Me.SignaturePicture.Name = "SignaturePicture"
			Me.SignaturePicture.Size = New System.Drawing.Size(155, 100)
			Me.SignaturePicture.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom
			Me.SignaturePicture.TabIndex = 2
			Me.SignaturePicture.TabStop = False
'			Me.SignaturePicture.Click += New System.EventHandler(Me.SignaturePicture_Click)
			' 
			' OpenImageDialog
			' 
			Me.OpenImageDialog.Filter = "Supported Images|*.png;*.bmp*.jpg|All files|*.*"
			' 
			' mainForm
			' 
			Me.AutoScaleDimensions = New System.Drawing.SizeF(6F, 13F)
			Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
			Me.ClientSize = New System.Drawing.Size(205, 100)
			Me.Controls.Add(Me.SignaturePicture)
			Me.Controls.Add(Me.cbVisibleSignature)
			Me.Controls.Add(Me.btnCreateAndSign)
			Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D
			Me.Name = "mainForm"
			Me.Text = "Signing PDFs"
			CType(Me.SignaturePicture, System.ComponentModel.ISupportInitialize).EndInit()
			Me.ResumeLayout(False)
			Me.PerformLayout()

		End Sub

		#End Region

		Private WithEvents btnCreateAndSign As System.Windows.Forms.Button
		Private WithEvents cbVisibleSignature As System.Windows.Forms.CheckBox
		Private OpenExcelDialog As System.Windows.Forms.OpenFileDialog
		Private savePdfDialog As System.Windows.Forms.SaveFileDialog
		Private WithEvents SignaturePicture As System.Windows.Forms.PictureBox
		Private OpenImageDialog As System.Windows.Forms.OpenFileDialog
	End Class
End Namespace


mainForm.vb

Imports System.ComponentModel
Imports System.Text
Imports FlexCel.Render
Imports FlexCel.XlsAdapter
Imports FlexCel.Pdf
Imports System.IO
Imports System.Security.Cryptography.X509Certificates
Imports System.Security.Cryptography.Pkcs
Imports System.Drawing.Imaging
Imports System.Reflection

Namespace SigningPdfs
	Partial Public Class mainForm
		Inherits Form

		Public Sub New()
			Application.EnableVisualStyles()
			InitializeComponent()
		End Sub

		Private Sub cbVisibleSignature_CheckedChanged(ByVal sender As Object, ByVal e As EventArgs) Handles cbVisibleSignature.CheckedChanged
			SignaturePicture.Visible = cbVisibleSignature.Checked
			Dim delta As Integer = SignaturePicture.Height + 30
			If cbVisibleSignature.Checked Then
				Me.Height += delta
			Else
				Me.Height -= delta
			End If
		End Sub

		Private Sub SignaturePicture_Click(ByVal sender As Object, ByVal e As EventArgs) Handles SignaturePicture.Click
			If OpenImageDialog.ShowDialog() <> System.Windows.Forms.DialogResult.OK Then
				Return
			End If
			SignaturePicture.Load(OpenImageDialog.FileName)
		End Sub

		Private Sub btnCreateAndSign_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnCreateAndSign.Click
			'Load the Excel file.
			If OpenExcelDialog.ShowDialog() <> System.Windows.Forms.DialogResult.OK Then
				Return
			End If
			Dim xls As New XlsFile()
			xls.Open(OpenExcelDialog.FileName)

			Dim DataPath As String = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) & "\..\..\"

			'Export it to pdf.
			Using pdf As New FlexCelPdfExport(xls, True)
				pdf.FontEmbed = TFontEmbed.Embed

				'Load the certificate and create a signer.
                'In this example we just have the password in clear. It should be kept in a SecureString.
                'Also make sure to set the flag X509KeyStorageFlags.EphemeralKeySet to avoid files created
                'on disk: https://snede.net/the-most-dangerous-constructor-in-net/
                'As X509KeyStorageFlags.EphemeralKeySet only exists in .NET 4.8 or newer, for older versions we will 
                'define it as (X509KeyStorageFlags)32. For .NET 4.8 or newer and  NET Core, you can use X509KeyStorageFlags.EphemeralKeySet
               Dim Cert As New X509Certificate2(DataPath & "flexcel.pfx", "password", X509KeyStorageFlags.EphemeralKeySet) 

				'Note that to use the CmsSigner class you need to add a reference to System.Security dll. 
				'It is *not* enough to add it to the using clauses, you need to add a reference to the dll.
				Dim Signer As New CmsSigner(Cert)

				'By default CmsSigner uses SHA1, but SHA1 has known vulnerabilities and it is deprecated. 
				'So we will use SHA512 instead.
				'"2.16.840.1.101.3.4.2.3" is the Oid for SHA512.
				Signer.DigestAlgorithm = New System.Security.Cryptography.Oid("2.16.840.1.101.3.4.2.3")

				Dim sig As TPdfSignature
				If cbVisibleSignature.Checked Then
					Using fs As New MemoryStream()
						SignaturePicture.Image.Save(fs, ImageFormat.Png)
						Dim ImgData() As Byte = fs.ToArray()

						'The -1 as "page" parameter means the last page.
						sig = New TPdfVisibleSignature(New TBuiltInSignerFactory(Signer), "Signature", "I have read the document and certify it is valid.", "Springfield", "adrian@tmssoftware.com", -1, New RectangleF(50, 50, 140, 70), ImgData)
					End Using
				Else
					sig = New TPdfSignature(New TBuiltInSignerFactory(Signer), "Signature", "I have read the document and certify it is valid.", "Springfield", "adrian@tmssoftware.com")
				End If

				'You must sign the document *BEFORE* starting to write it.
				pdf.Sign(sig)

				If savePdfDialog.ShowDialog() <> System.Windows.Forms.DialogResult.OK Then
					Return
				End If
				Using PdfStream As New FileStream(savePdfDialog.FileName, FileMode.Create)
					pdf.BeginExport(PdfStream)
					pdf.ExportAllVisibleSheets(False, "Signed Pdf")
					pdf.EndExport()
				End Using

			End Using

			If MessageBox.Show("Do you want to open the generated file?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) <> System.Windows.Forms.DialogResult.Yes Then
				Return
			End If
			Process.Start(savePdfDialog.FileName)

		End Sub
	End Class
End Namespace

Program.vb

Namespace SigningPdfs
	Friend NotInheritable Class Program

		Private Sub New()
		End Sub

		''' <summary>
		''' The main entry point for the application.
		''' </summary>
		<STAThread> _
		Shared Sub Main()
			Application.EnableVisualStyles()
			Application.SetCompatibleTextRenderingDefault(False)
			Application.Run(New mainForm())
		End Sub
	End Class
End Namespace