The SignFile function in Debenu 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");
            }
        }
    }
}

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

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