Example T871584
Visible to All Users

PDF Document API - Use a Custom Signer Class to Apply Signatures to a PDF Document

The PDF Document API allows you to replace a built-in PKCS#7 signature builder (the Pkcs7Signer class) with a custom signer.

The code sample project shows how to create a Pkcs7SignerBase descendant to implement a custom signer based on the Bouncy Castle C# API and calculate a document hash using a custom digest calculator.

To sign a PDF file using an external web service, retrieve a certificate/certificate chain from the service, and calculate the document hash. Then, sign the calculated document hash with a private key obtained from the external service in the SignDigest method of the BouncyCastleSigner class.

[!IMPORTANT]
The Universal Subscription or Office File API Subscription is required to use this example in production code. Please refer to the following page for pricing information: DevExpress Subscriptions

Files to Review

Documentation

More Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

CustomSigner/Program.cs(vb)
C#
using DevExpress.Pdf; using System; using System.Diagnostics; using DevExpress.Office.Tsp; using DevExpress.Office.DigitalSignatures; namespace CustomSigner { static class Program { static void Main(string[] args) { using (var signer = new PdfDocumentSigner(@"Document.pdf")) { //Create a timestamp: ITsaClient tsaClient = new TsaClient(new Uri(@"https://freetsa.org/tsr"), HashAlgorithmType.SHA256); //Specify the signature's field name and location: var description = new PdfSignatureFieldInfo(1); description.Name = "SignatureField"; description.SignatureBounds = new PdfRectangle(10, 10, 50, 150); //Create a custom signer object: var bouncyCastleSigner = new BouncyCastleSigner("certificate.pfx", "123", tsaClient); //Apply a signature to a new form field: var signatureBuilder = new PdfSignatureBuilder(bouncyCastleSigner, description); //Specify the image and signer information: signatureBuilder.SetImageData(System.IO.File.ReadAllBytes("signature.jpg")); signatureBuilder.Location = "LOCATION"; //Sign and save the document: signer.SaveDocument("SignedDocument.pdf", signatureBuilder); Process.Start(new ProcessStartInfo("SignedDocument.pdf") { UseShellExecute = true}) ; } return; } } }
CustomSigner/BouncyCastleSigner.cs(vb)
C#
using DevExpress.Pdf; using DevExpress.Office.DigitalSignatures; using DevExpress.Office.Tsp; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.Security; using System.Collections.Generic; using System.IO; using System.Linq; namespace CustomSigner { // Declare a custom class to calculate a digest value: public class BouncyCastleDigestCalculator : IDigestCalculator { readonly IDigest digest; public string AlgorithmOid => DigestUtilities.GetObjectIdentifier(digest.AlgorithmName).Id; public BouncyCastleDigestCalculator() { digest = new Sha512Digest(); } public byte[] ComputeDigest(Stream stream) { digest.Reset(); byte[] buffer = new byte[1024 * 1024]; int readByteCount; do { readByteCount = stream.Read(buffer, 0, buffer.Length); if (readByteCount != 0) digest.BlockUpdate(buffer, 0, readByteCount); } while (readByteCount != 0); byte[] result = new byte[GetDigestSize()]; digest.DoFinal(result, 0); return result; } public int GetDigestSize() { return digest.GetDigestSize(); } } public class BouncyCastleSigner : Pkcs7SignerBase { //Specify the signing algoritm's OID: const string PKCS1RsaEncryption = "1.2.840.113549.1.1.1"; readonly byte[][] certificates; readonly IAsymmetricBlockCipher rsaEngine; //Specify a custom digest calculator: protected override IDigestCalculator DigestCalculator { get { return new BouncyCastleDigestCalculator(); } } protected override string SigningAlgorithmOID => PKCS1RsaEncryption; public BouncyCastleSigner(string file, string password, ITsaClient tsaClient) : base(tsaClient, null, null, PdfSignatureProfile.Pdf) { //Read PKCS#12 file: var pkcs = new Pkcs12StoreBuilder().Build(); pkcs.Load(File.Open(file, FileMode.Open), password.ToCharArray()); //Get the certificate's alias: var alias = pkcs.Aliases.OfType<string>().First(a => pkcs.IsKeyEntry(a)); //Get the certificate's chain: certificates = pkcs.GetCertificateChain(alias).Select(c => c.Certificate.GetEncoded()).ToArray(); //Initialize the encryption engine: rsaEngine = new Pkcs1Encoding(new RsaBlindedEngine()); rsaEngine.Init(true, pkcs.GetKey(alias).Key); } protected override IEnumerable<byte[]> GetCertificates() { return certificates; } protected override byte[] SignDigest(byte[] digest) { //Create the digest info object //Encrypted by the signer's private key: var dInfo = new DigestInfo(new AlgorithmIdentifier(new DerObjectIdentifier(DigestCalculator.AlgorithmOid), DerNull.Instance), digest); byte[] digestInfo = dInfo.GetEncoded(); return rsaEngine.ProcessBlock(digestInfo, 0, digestInfo.Length); } } }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.