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