Example T871585
Visible to All Users

PDF Document API - Use a Custom Timestamp Client to Apply Signatures to a Document

The PDF Document API allows you to use a timestamp for the signature. The following code sample project shows how to create a custom timestamp client based on the Bouncy Castle C# API.

Files to Review

More Examples

Does this example address your development requirements/objectives?

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

Example Code

CustomTsaClient/Program.cs(vb)
C#
using DevExpress.Pdf; using DevExpress.Office.DigitalSignatures; using DevExpress.Office.Tsp; using Org.BouncyCastle.Crypto.Digests; using System; using System.IO; using System.Diagnostics; namespace CustomTsaClient { static class Program { static void Main(string[] args) { using (var signer = new PdfDocumentSigner(@"Document.pdf")) { //Create a custom timestamp client instance: ITsaClient tsaClient = new BouncyCastleTsaClient(new Uri(@"https://freetsa.org/tsr"), new Sha256Digest(), new System.Net.Http.HttpClient()); //Create a PKCS#7 signature: Pkcs7Signer pkcs7Signature = new Pkcs7Signer(@"testcert.pfx", "123", HashAlgorithmType.SHA256, tsaClient); //Apply the signature to the form field: var signatureBuilder = new PdfSignatureBuilder(pkcs7Signature, "Sign"); //Specify image data and signer information: signatureBuilder.SetImageData(File.ReadAllBytes("JaneCooper.jpg")); signatureBuilder.Location = "United Kingdom"; //Sign and save the document: signer.SaveDocument("SignedDocument.pdf", signatureBuilder); } Process.Start(new ProcessStartInfo("SignedDocument.pdf") { UseShellExecute = true }); return; } } }
CustomTsaClient/BouncyCastleTsaClient.cs(vb)
C#
using DevExpress.Office.Tsp; using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Tsp; using System; using System.IO; using System.Net.Http; using System.Security.Cryptography; public class BouncyCastleTsaClient : ITsaClient { readonly Uri tsaServerURI; readonly IDigest hashCalculator; readonly HttpClient httpClient; public BouncyCastleTsaClient(Uri tsaServerURI, IDigest hashCalculator, HttpClient httpClient) { this.tsaServerURI = tsaServerURI; this.hashCalculator = hashCalculator; this.httpClient = httpClient; } byte[] CalculateDigest(Stream stream) { byte[] buffer = new byte[81920]; hashCalculator.Reset(); int bytesRead; while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) hashCalculator.BlockUpdate(buffer, 0, bytesRead); byte[] result = new byte[hashCalculator.GetDigestSize()]; hashCalculator.DoFinal(result, 0); return result; } public byte[] GenerateTimeStamp(Stream stream) { //Generate a timestamp request: TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator(); tsqGenerator.SetCertReq(true); BigInteger nonce; using (RandomNumberGenerator generator = RandomNumberGenerator.Create()) { byte[] nonceValue = new byte[10]; generator.GetBytes(nonceValue); nonce = new BigInteger(nonceValue); } string algorithmOid = DigestUtilities.GetObjectIdentifier(hashCalculator.AlgorithmName).Id; TimeStampRequest request = tsqGenerator.Generate(algorithmOid, CalculateDigest(stream), nonce); byte[] requestBytes = request.GetEncoded(); //Send the request to a server: HttpClient httpClient = new HttpClient(); using var content = new ByteArrayContent(requestBytes); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/timestamp-query"); //Get a responce from the server: using HttpResponseMessage responseMessage = httpClient.PostAsync(tsaServerURI, content).Result; if (!responseMessage.IsSuccessStatusCode) { throw new Exception($"TimeStamp request to the \"{tsaServerURI}\" failed with status code {responseMessage.StatusCode}."); } byte[] responseBytes = responseMessage.Content.ReadAsByteArrayAsync().Result; //Read the response: TimeStampResponse response = new TimeStampResponse(responseBytes); response.Validate(request); PkiFailureInfo failure = response.GetFailInfo(); //Throw an exception if the responce returned an error: if (failure != null) throw new Exception($"TimeStamp request to the \"{tsaServerURI}\" failed."); TimeStampToken token = response.TimeStampToken; //Throw an exception if the responce doesn't contain the timestamp: if (token == null) throw new Exception($"TimeStamp request to the \"{tsaServerURI}\" failed."); return token.GetEncoded(); } public byte[] GenerateTimeStamp(byte[] digest, string digestAlgorithmOID) { //Generate a timestamp request: TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator(); tsqGenerator.SetCertReq(true); BigInteger nonce; using (RandomNumberGenerator generator = RandomNumberGenerator.Create()) { byte[] nonceValue = new byte[10]; generator.GetBytes(nonceValue); nonce = new BigInteger(nonceValue); } string algorithmOid = DigestUtilities.GetObjectIdentifier(hashCalculator.AlgorithmName).Id; TimeStampRequest request = tsqGenerator.Generate(algorithmOid, digest, nonce); byte[] requestBytes = request.GetEncoded(); //Send the request to a server: HttpClient httpClient = new HttpClient(); using var content = new ByteArrayContent(requestBytes); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/timestamp-query"); //Get a responce from the server: using HttpResponseMessage responseMessage = httpClient.PostAsync(tsaServerURI, content).Result; if (!responseMessage.IsSuccessStatusCode) { throw new Exception($"TimeStamp request to the \"{tsaServerURI}\" failed with status code {responseMessage.StatusCode}."); } byte[] responseBytes = responseMessage.Content.ReadAsByteArrayAsync().Result; //Read the response: TimeStampResponse response = new TimeStampResponse(responseBytes); response.Validate(request); PkiFailureInfo failure = response.GetFailInfo(); //Throw an exception if the responce returned an error: if (failure != null) throw new Exception($"TimeStamp request to the \"{tsaServerURI}\" failed."); TimeStampToken token = response.TimeStampToken; //Throw an exception if the responce doesn't contain the timestamp: if (token == null) throw new Exception($"TimeStamp request to the \"{tsaServerURI}\" failed."); return token.GetEncoded(); } }

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.