Back to Blog

    Developer Blog

    Advanced options for signing PDF files

    Foxit Quick PDF Library

    The SignFile function in Foxit Quick PDF Library lets you sign PDF files using the PKCS#12 format (containing a certificate and private key). But sometimes more advanced signing options are required. This is why we’ve added two new functions: SetSignProcessPassthrough and GetSignProcessByteRange. These functions let you sign PDF files using signatures that were created externally.

    These advanced options for signing PDF files means you can sign PDF files using a certificate from the Certificate Store on Windows, using a USB token or even a SmartCard. We’ve provided some C# and Visual Basic sample code which demonstrates how to use these new functions.

    C# Sample Code

    using System;
    using System.IO;
    using System.Windows.Forms;
    using System.Security;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Cryptography.Pkcs;
    using DebenuPDFLibraryDLL0916; // Add the DebenuPDFLibraryDLL1013.cs import file to your project
    
    namespace PassthroughSigning
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void AddLog(string logEntry)
            {
                textBox1.Text += logEntry + "\r\n";
            }
    
            private byte[] SignData(byte[] inputData, X509Certificate2 cert)
            {
                // Create an SHA-1 hash of the file data
                SHA1 sha = new SHA1CryptoServiceProvider();
                byte[] sha1Result = sha.ComputeHash(inputData);
    
                // Sign the hash using the certificate
                // This could be changed to use a hardware device (eg. smartcard)
                ContentInfo content = new ContentInfo(sha1Result);
                SignedCms signedCms = new SignedCms(content);
                CmsSigner cmsSigner = new CmsSigner(cert);
                signedCms.ComputeSignature(cmsSigner);
                return signedCms.Encode();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                string workFolder = @"E:\DQPL\Testing\";
                string dllFileName = @"E:\DQPL\DebenuPDFLibraryDLL0916.dll";
    
                // Load a certificate from a .pfx file
                // This could be changed to use a certificate from the store
                X509Certificate2 cert = new X509Certificate2(workFolder + "qpl_test.pfx", "testing");
                AddLog("Loaded certificate: " + cert.SubjectName.Name);
    
                // Estimate how much space we need for the signature by first signing some
                // random data
                byte[] randomData = new byte[1];
                randomData[0] = 123;
                byte[] testSig = SignData(randomData, cert);
    
                // Number of bytes to reserve for the signature placeholder. We need double the
                // bytes because they will will be stored in hex format, and we
                // add 512 bytes extra margin
                int signatureLength = testSig.Length * 2 + 512; 
    
                // The hashRange array will contain the position in the file of the
                // digital signature placeholder
                int[] hashRange = new int[4];
    
                DebenuPDFLibraryDLL0916.PDFLibrary DPL = new DebenuPDFLibraryDLL0916.PDFLibrary(dllFileName);
                if (DPL.LibraryLoaded())
                {
                    if (DPL.UnlockKey("...add_license_key_here...") == 1)
                    {
                        AddLog("Creating PDF");
                        DPL.DrawText(100, 700, "Hello world");
                        DPL.SaveToFile(workFolder + "Passthrough.pdf");
    
                        AddLog("Signing PDF");
                        int signProcessID = DPL.NewSignProcessFromFile(workFolder + "Passthrough.pdf", "");
                        AddLog("signProcessID = " + signProcessID);
                        DPL.SetSignProcessPassthrough(signProcessID, signatureLength);
                        DPL.SetSignProcessField(signProcessID, "PassthroughSignature");
                        DPL.SetSignProcessInfo(signProcessID, "Reason for signing", "Location of signing", "Contact info of signer");
                        DPL.EndSignProcessToFile(signProcessID, workFolder + "Passthrough-Signed.pdf");
                        int result = DPL.GetSignProcessResult(signProcessID);
                        AddLog("Signing result = " + result);
    
                        AddLog("Byte ranges:");
                        for (int x = 0; x < 4; x++)
                        {
                            hashRange[x] = DPL.GetSignProcessByteRange(signProcessID, x + 1);
                            AddLog("[" + x + "] = " + hashRange[x]);
                        }
                    }
                }
    
                // Read the file data into a byte array, missing out the
                // placeholder for the signature data
                byte[] fileData = new byte[hashRange[1] + hashRange[3]];
                using (BinaryReader reader = new BinaryReader(new FileStream(workFolder + "Passthrough-Signed.pdf", FileMode.Open)))
                {
                    reader.BaseStream.Seek(hashRange[0], SeekOrigin.Begin);
                    reader.Read(fileData, 0, hashRange[1]);
                    reader.BaseStream.Seek(hashRange[2], SeekOrigin.Begin);
                    reader.Read(fileData, hashRange[1], hashRange[3]);
                }
    
                // Sign the file data (generates an SHA-1 hash and
                // signs that hash)
                byte[] enc = SignData(fileData, cert);
    
                // Convert the signature to hex format
                string hex = BitConverter.ToString(enc);
                hex = hex.Replace("-", "");
    
                byte[] encHex = new byte[enc.Length * 2];
                for (int x = 0; x < enc.Length; x++)
                {
                    string tempHex = string.Format("{0:x2}", enc[x]);
                    encHex[x * 2] = (byte)tempHex[0];
                    encHex[x * 2 + 1] = (byte)tempHex[1];
                }
    
                if (encHex.Length < signatureLength)
                {
                    // Write the signature into the placeholder
                    using (BinaryWriter writer = new BinaryWriter(new FileStream(workFolder + "Passthrough-Signed.pdf", FileMode.Open)))
                    {
                        writer.BaseStream.Seek(hashRange[1] + 1, SeekOrigin.Begin);
                        writer.BaseStream.Write(encHex, 0, encHex.Length);
                    }
                }
                else
                {
                    AddLog("Error: digital signature is larger than the placeholder size");
                }
            }
        }
    }
    

    Visual Basic Sample Code

    Imports System
    imports System.IO
    Imports System.Windows.Forms
    Imports System.Security
    Imports System.Security.Cryptography
    Imports System.Security.Cryptography.X509Certificates
    Imports System.Security.Cryptography.Pkcs
    Imports DQPLAdvancedSigning.DebenuPDFLibraryDLL1013 ' Add the DebenuPDFLibraryDLL1013.vb import file to your updated project name
    
    Public Class Form1
    
        Private Sub AddLog(logEntry As String)
            TextBox1.Text += logEntry + "\r\n"
        End Sub
    
        Private Function SignData(inputData As Byte(), cert As X509Certificate2) As Byte()
    
            Dim content As ContentInfo
            Dim SignedCms As SignedCms
            Dim CmsSigner As CmsSigner
            Dim sha As SHA1
            Dim sha1Result As Byte()
    
            ' Create an SHA-1 hash of the file data
            sha = New SHA1CryptoServiceProvider()
            sha1Result = sha.ComputeHash(inputData)
    
            ' Sign the hash using the certificate
            ' This could be changed to use a hardware device (eg. smartcard)
            content = New ContentInfo(sha1Result)
            SignedCms = New SignedCms(content)
            CmsSigner = New CmsSigner(cert)
            SignedCms.ComputeSignature(CmsSigner)
    
            Return SignedCms.Encode()
    
        End Function
    
    
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            Dim workFolder As String
            Dim dllFileName As String
            Dim cert As X509Certificate2
            Dim RandomData(1) As Byte
            Dim TestSig As Byte()
            Dim SignatureLength As Integer
            Dim hashRange(4) As Integer
            Dim DPL As DebenuPDFLibraryDLL1013.PDFLibrary
            Dim signProcessID As Integer
            Dim Result As Integer
            Dim X As Integer
            Dim hex, tempHex As String
            Dim filedata() As Byte
            Dim enc As Byte()
            Dim encHex(1) As Byte
            Dim signFile1 As String
            Dim signFile2 As String
    
            workFolder = "C:\DQPL\Testing\WorkFolder\"
            dllFileName = "C:\DQPL\BuildsSrc\1013\DLL\DebenuPDFLibraryDLL1013.dll"
            signFile1 = workFolder + "Passthrough.pdf"
            signFile2 = workFolder + "Passthrough-Signed.pdf"
    
            ' Load a certificate from a .pfx file
            ' This could be changed to use a certificate from the store
    
            cert = New X509Certificate2(workFolder + "qpl_test.pfx", "testing")
            AddLog("Loaded certificate: " + cert.SubjectName.Name)
    
            ' Estimate how much space we need for the signature by first signing some
            ' random data
    
            RandomData(1) = 123
            TestSig = SignData(RandomData, cert)
    
            ' Number of bytes to reserve for the signature placeholder. We need double the
            ' bytes because they will will be stored in hex format, and we
            ' add 512 bytes extra margin
    
            SignatureLength = TestSig.Length * 2 + 512
    
            ' The hashRange array will contain the position in the file of the
            ' digital signature placeholder
    
            DPL = New DebenuPDFLibraryDLL1013.PDFLibrary(dllFileName)
    
            If DPL.LibraryLoaded() = -1 Then
    
                If DPL.UnlockKey("...Insert_License_Key...") = 1 Then
                    AddLog("Creating PDF")
                    DPL.DrawText(100, 700, "Hello world")
                    DPL.SaveToFile(signFile1)
    
                    AddLog("Signing PDF")
                    signProcessID = DPL.NewSignProcessFromFile(signFile1, "")
                    AddLog("signProcessID = " + signProcessID.ToString)
                    DPL.SetSignProcessPassthrough(signProcessID, SignatureLength)
                    DPL.SetSignProcessField(signProcessID, "PassthroughSignature")
                    DPL.SetSignProcessInfo(signProcessID, "Reason for signing", "Location of signing", "Contact info of signer")
                    DPL.EndSignProcessToFile(signProcessID, signFile2)
                    Int(Result = DPL.GetSignProcessResult(signProcessID))
                    AddLog("Signing result = " + Result.ToString)
    
                    AddLog("Byte ranges:")
                    For X = 0 To 3
                        hashRange(X) = DPL.GetSignProcessByteRange(signProcessID, X + 1)
                        AddLog("[" + X.ToString + "] = " + hashRange(X).ToString)
                    Next
                End If
            End If
    
            ' Read the file data into a byte array, missing out the
            ' placeholder for the signature data
    
            filedata = New Byte(hashRange(1) + (hashRange(3) - 1)) {}
    
            If (File.Exists(signFile2)) Then
                Using reader As BinaryReader = New BinaryReader(File.Open(signFile2, FileMode.Open))
                    reader.BaseStream.Seek(hashRange(0), SeekOrigin.Begin)
                    reader.Read(filedata, 0, hashRange(1))
                    reader.BaseStream.Seek(hashRange(2), SeekOrigin.Begin)
                    reader.Read(filedata, hashRange(1), hashRange(3))
                End Using
            End If
    
            ' Sign the file data (generates an SHA-1 hash and
            ' signs that hash)
            enc = SignData(filedata, cert)
    
            ' Convert the signature to hex format
            hex = BitConverter.ToString(enc)
            hex = hex.Replace("-", "")
    
            Array.Resize(encHex, enc.Length * 2)
    
            For X = 0 To enc.Length - 1
                tempHex = String.Format("{0:x2}", enc(X))
                encHex(X * 2) = Asc(tempHex(0))
                encHex(X * 2 + 1) = Asc(tempHex(1))
            Next
    
            If (encHex.Length < SignatureLength) Then
                ' Write the signature into the placeholder
                Using writer As BinaryWriter = New BinaryWriter(File.Open(signFile2, FileMode.Open))
                    writer.BaseStream.Seek(hashRange(1) + 1, SeekOrigin.Begin)
                    writer.BaseStream.Write(encHex, 0, encHex.Length)
                End Using
            Else
                AddLog("Error: digital signature is larger than the placeholder size")
            End If
        End Sub
    End Class
    

    This article refers to a deprecated product. If you are looking for support for Foxit PDF SDK, please click here.