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 |