Table of Contents

Signing PDFs (C# / netframework)

Note

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

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using FlexCel.Render;
using FlexCel.XlsAdapter;
using FlexCel.Pdf;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.Drawing.Imaging;
using System.Reflection;
using System.Diagnostics;

namespace SigningPdfs
{
    public partial class mainForm: Form
    {
        public mainForm()
        {
            Application.EnableVisualStyles();
            InitializeComponent();
        }

        private void cbVisibleSignature_CheckedChanged(object sender, EventArgs e)
        {
            SignaturePicture.Visible = cbVisibleSignature.Checked;
            int delta = SignaturePicture.Height + 30;
            if (cbVisibleSignature.Checked) this.Height += delta; else this.Height -= delta;
        }

        private void SignaturePicture_Click(object sender, EventArgs e)
        {
            if (OpenImageDialog.ShowDialog() != DialogResult.OK) return;
            SignaturePicture.Load(OpenImageDialog.FileName);
        }

        private void btnCreateAndSign_Click(object sender, EventArgs e)
        {
            //Load the Excel file.
            if (OpenExcelDialog.ShowDialog() != DialogResult.OK) return;
            XlsFile xls = new XlsFile();
            xls.Open(OpenExcelDialog.FileName);

            string DataPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + @"\..\..\";

            //Export it to pdf.
            using (FlexCelPdfExport pdf = 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
                  X509Certificate2 Cert = 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.
                CmsSigner Signer = 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");

                TPdfSignature sig;
                if (cbVisibleSignature.Checked)
                {
                    using (MemoryStream fs = new MemoryStream())
                    {
                        SignaturePicture.Image.Save(fs, ImageFormat.Png);
                        byte[] ImgData = 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);
                    }
                }
                else
                {
                    sig = new TPdfSignature(new TBuiltInSignerFactory(Signer),
                                "Signature", "I have read the document and certify it is valid.", "Springfield", "adrian@tmssoftware.com");
                }

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

                if (savePdfDialog.ShowDialog() != DialogResult.OK) return;
                using (FileStream PdfStream = new FileStream(savePdfDialog.FileName, FileMode.Create))
                {
                    pdf.BeginExport(PdfStream);
                    pdf.ExportAllVisibleSheets(false, "Signed Pdf");
                    pdf.EndExport();
                }

            }

            if (MessageBox.Show("Do you want to open the generated file?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return;
            Process.Start(savePdfDialog.FileName);

        }
    }
}

mainForm.Designer.cs

namespace SigningPdfs
{
    partial class mainForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #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 void InitializeComponent()
        {
            this.btnCreateAndSign = new System.Windows.Forms.Button();
            this.cbVisibleSignature = new System.Windows.Forms.CheckBox();
            this.OpenExcelDialog = new System.Windows.Forms.OpenFileDialog();
            this.savePdfDialog = new System.Windows.Forms.SaveFileDialog();
            this.SignaturePicture = new System.Windows.Forms.PictureBox();
            this.OpenImageDialog = new System.Windows.Forms.OpenFileDialog();
            ((System.ComponentModel.ISupportInitialize)(this.SignaturePicture)).BeginInit();
            this.SuspendLayout();
            // 
            // btnCreateAndSign
            // 
            this.btnCreateAndSign.Image = global::SigningPdfs.Properties.Resources.acroread;
            this.btnCreateAndSign.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
            this.btnCreateAndSign.Location = new System.Drawing.Point(24, 25);
            this.btnCreateAndSign.Name = "btnCreateAndSign";
            this.btnCreateAndSign.Size = new System.Drawing.Size(155, 30);
            this.btnCreateAndSign.TabIndex = 0;
            this.btnCreateAndSign.Text = "Create and Sign Pdf";
            this.btnCreateAndSign.UseVisualStyleBackColor = true;
            this.btnCreateAndSign.Click += new System.EventHandler(this.btnCreateAndSign_Click);
            // 
            // cbVisibleSignature
            // 
            this.cbVisibleSignature.AutoSize = true;
            this.cbVisibleSignature.Location = new System.Drawing.Point(24, 78);
            this.cbVisibleSignature.Name = "cbVisibleSignature";
            this.cbVisibleSignature.Size = new System.Drawing.Size(167, 17);
            this.cbVisibleSignature.TabIndex = 1;
            this.cbVisibleSignature.Text = "Visible Signature (in last page)";
            this.cbVisibleSignature.UseVisualStyleBackColor = true;
            this.cbVisibleSignature.CheckedChanged += new System.EventHandler(this.cbVisibleSignature_CheckedChanged);
            // 
            // OpenExcelDialog
            // 
            this.OpenExcelDialog.DefaultExt = "xls";
            this.OpenExcelDialog.Filter = "Excel Files|*.xls;*.xlsx;*.xlsm|Excel 97/2003|*.xls|Excel 2007|*.xlsx;*.xlsm|All files|*.*";
            this.OpenExcelDialog.Title = "Select Excel file to convert...";
            // 
            // savePdfDialog
            // 
            this.savePdfDialog.DefaultExt = "pdf";
            this.savePdfDialog.Filter = "Pdf Files|*.pdf";
            this.savePdfDialog.Title = "Select where to save the file...";
            // 
            // SignaturePicture
            // 
            this.SignaturePicture.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.SignaturePicture.Image = global::SigningPdfs.Properties.Resources.sign;
            this.SignaturePicture.Location = new System.Drawing.Point(24, 110);
            this.SignaturePicture.Name = "SignaturePicture";
            this.SignaturePicture.Size = new System.Drawing.Size(155, 100);
            this.SignaturePicture.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
            this.SignaturePicture.TabIndex = 2;
            this.SignaturePicture.TabStop = false;
            this.SignaturePicture.Click += new System.EventHandler(this.SignaturePicture_Click);
            // 
            // OpenImageDialog
            // 
            this.OpenImageDialog.Filter = "Supported Images|*.png;*.bmp*.jpg|All files|*.*";
            // 
            // mainForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(205, 100);
            this.Controls.Add(this.SignaturePicture);
            this.Controls.Add(this.cbVisibleSignature);
            this.Controls.Add(this.btnCreateAndSign);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
            this.Name = "mainForm";
            this.Text = "Signing PDFs";
            ((System.ComponentModel.ISupportInitialize)(this.SignaturePicture)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button btnCreateAndSign;
        private System.Windows.Forms.CheckBox cbVisibleSignature;
        private System.Windows.Forms.OpenFileDialog OpenExcelDialog;
        private System.Windows.Forms.SaveFileDialog savePdfDialog;
        private System.Windows.Forms.PictureBox SignaturePicture;
        private System.Windows.Forms.OpenFileDialog OpenImageDialog;
    }
}


Program.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace SigningPdfs
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new mainForm());
        }
    }
}