Foxit Quick PDF Library

Advanced options for signing PDF files

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.

Updated on May 16, 2022

Was this article helpful?
Thanks for your feedback. If you have a comment on how to improve the article, you can write it here: