PDF Signature

This demo illustrates how to add a digital signature to a PDF document using the PDF Document API. Specify signing information on the Options pane and click Sign and Download. The resulting document is signed with a sample signature.

Options
Reason
Location
Contact Info
Hash algorithm
TSA server
@model AspNetCoreDemos.OfficeFileAPI.PdfPreviewModel

<img id="previewImage" src="@Url.Action(Model.PreviewDocumentAction, Model.ControllerName)" class="preview-image-bordered @(Model.IsViewWithoutSidePanel ? "preview-image-without-side-panel" : "preview-image-with-side-panel")" />

<script type="text/javascript">
    PdfPreview = {
        basePath: '@Url.Action(Model.PreviewDocumentAction, Model.ControllerName)',
        Update: function (param) {
            var iframeElement = document.getElementById("previewImage");
            if (!iframeElement)
                return;
            var additionalParams = "&" + new Date().valueOf();
            if (param)
                additionalParams = param;
            iframeElement.src = this.basePath + "?" + additionalParams;
        }
    };
</script>
@model AspNetCoreDemos.OfficeFileAPI.PdfSignatureModel
@using DevExtreme.AspNet.Mvc

@using (Html.BeginForm("PdfSignature", "DocumentProtection")) {
    <div class="demo-view-container demo-preview-border">
        @(Html.DevExtreme().ScrollView()
                                       .ID("scrollview")
                                       .ScrollByContent(true)
                                       .ScrollByThumb(true)
                                       .ShowScrollbar(ShowScrollbarMode.OnHover)
                                       .Direction(ScrollDirection.Both)
                                       .Height("580px")
                                       .Content(@<text>
                                <div id="scrollview-content">
                                    @await Html.PartialAsync("PdfFileView", Model.PreviewModel)
                                </div>
                                    </text>)
        )
    </div>
    <div class="options">
        <div class="caption">Options</div>
        <div class="option">
            <div class="label">Reason</div>
            @(Html.DevExtreme().TextBoxFor(m => m.Reason))
        </div>
        <div class="option">
            <div class="label">Location</div>
            @(Html.DevExtreme().TextBoxFor(m => m.Location))
        </div>
        <div class="option">
            <div class="label">Contact Info</div>
            @(Html.DevExtreme().TextBoxFor(m => m.ContactInfo))
        </div>
        <div class="option">
            <div class="label">Hash algorithm</div>
            @(Html.DevExtreme().SelectBoxFor(m => m.HashAlgorithm)
                                                               .DataSource(Html.GetEnumSelectList<DevExpress.Office.DigitalSignatures.HashAlgorithmType>()
                                                               .Select(i => new { Value = int.Parse(i.Value), Text = i.Text }))
                                                               .ValueExpr("Value")
                                                               .DisplayExpr("Text"))
        </div>
        <div class="option">
            <div class="label">TSA server</div>
            @(Html.DevExtreme().TextBoxFor(m => m.TSAServer)
                                                               .Mode(TextBoxMode.Url)
                                                               .ID("tsaServerTextBox")
                                                               .ValidationStatus(ViewBag.ErrorMessage != null ? ValidationStatus.Invalid : ValidationStatus.Valid))
        </div>
        <div class="option-buttons">
            @(Html.DevExtreme().Button()
                                                               .Text("Sign and Download")
                                                               .Type(ButtonType.Default)
                                                               .StylingMode(ButtonStylingMode.Contained)
                                                               .UseSubmitBehavior(true)
                                                               .OnClick("SignClick")
            )
        </div>
        <div id="error" class="error-message">
            <span>@ViewBag.ErrorMessage</span>
        </div>
    </div>
    <script type="text/javascript">
        function SignClick() {
            $("#tsaServerTextBox").dxTextBox("instance").option("validationStatus", 'valid');
            document.getElementById("error").hidden = true;
        }
    </script>
}

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;

namespace AspNetCoreDemos.OfficeFileAPI {
    public partial class DocumentProtectionController : OfficeDemoController {
        public DocumentProtectionController(ILogger<DocumentProtectionController> logger, IWebHostEnvironment hostingEnvironment)
            : base(logger, hostingEnvironment) {
        }
        protected const string TsaServerUriInvalidExceptionString = "ERROR: TSA server URI is invalid or server doesn't support SHA-256 hashing algorithm";
    }
}
using System;
using System.IO;
using DevExpress.Office.DigitalSignatures;
using DevExpress.Office.Tsp;
using DevExpress.Pdf;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace AspNetCoreDemos.OfficeFileAPI {
    public partial class DocumentProtectionController {
        const string signSessionKey = "SignKey";
        const string signDefaultFile = "/Documents/Pdf/SignatureDemo.pdf";

        [HttpGet]
        public IActionResult PdfSignature() {
            PdfSignatureModel model = new PdfSignatureModel();
            return View(model);
        }

        public IActionResult PdfSignaturePartial() {
            return GetPartialView(signSessionKey, signDefaultFile);
        }

        [HttpPost]
        public IActionResult PdfSignature(PdfSignatureModel model) {
            try {
                return CreateFileStreamResult(Sign(model), "application/pdf", "pdf", "Result");
            } catch(Exception ex) {
                Logger.LogError(ex, TsaServerUriInvalidExceptionString);
                ViewBag.ErrorMessage = TsaServerUriInvalidExceptionString;
                return View("PdfSignature", model);
            }
        }

        Stream Sign(PdfSignatureModel model) {
            string pdfFolderPath = HostingEnvironment.ContentRootPath + "/Documents/Pdf/";
            using (PdfDocumentSigner documetSigner = new PdfDocumentSigner(pdfFolderPath + "SignatureDemo.pdf")) {
                byte[] imageData;
                using (FileStream fileStream = new FileStream(pdfFolderPath + "Faximile.emf", FileMode.Open, FileAccess.Read)) {
                    int length = (int)fileStream.Length;
                    imageData = new byte[length];
                    fileStream.Read(imageData, 0, length);
                }
                var tsaUri = model.TSAServer;
                TsaClient tsa;
                if (tsaUri != null)
                    tsa = new TsaClient(tsaUri, HashAlgorithmType.SHA256);
                else
                    tsa = null;
                var signer = new Pkcs7Signer(pdfFolderPath + "SignDemo.pfx", "dxdemo", model.HashAlgorithm, tsa);
                var fieldInfo = new PdfSignatureFieldInfo(1);
                fieldInfo.SignatureBounds = new PdfRectangle(394, 254, 482, 286);
                PdfSignatureBuilder signature = new PdfSignatureBuilder(signer, fieldInfo);
                signature.SetImageData(imageData);
                signature.Location = model.Location;
                signature.ContactInfo = model.ContactInfo;
                signature.Reason = model.Reason;
                MemoryStream stream = new MemoryStream();
                documetSigner.SaveDocument(stream, signature);
                return stream;
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.IO;
using DevExpress.Pdf;

namespace AspNetCoreDemos.OfficeFileAPI {
    public class PdfModelBase : IDisposable {
        readonly PdfDocumentProcessor processor;
        MemoryStream stream;
        List<PdfPageModel> items = new List<PdfPageModel>();

        public MemoryStream Stream { get { return stream; } }
        protected PdfDocumentProcessor Processor { get { return processor; } }
        protected PdfDocument Document { get { return processor.Document; } }
        public List<PdfPageModel> Items { get { return items; } }

        public string DocumentUrl { get; set; }
        public int PageIndex { get; set; }
        internal virtual string SessionKey { get; }
        public PdfPreviewModel PreviewModel { get; internal set; }

        public PdfModelBase() {
            processor = new PdfDocumentProcessor();
            PreviewModel = new PdfPreviewModel();
        }

        protected void CreateEmptyDocument() {
            stream = new MemoryStream();
            Processor.CreateEmptyDocument(stream);
            Document.Creator = "PDF Document Processor Demo";
            Document.Producer = "Developer Express Inc., " + AssemblyInfo.Version;
            Document.Author = "DevExpress Inc.";
        }

        public virtual void LoadDocument(byte[] data) {
            using(MemoryStream stream = new MemoryStream(data))
                LoadDocument(stream);
        }

        protected void LoadDocument(Stream stream) {
            processor.LoadDocument(stream, true);
            for(int pageNumber = 1; pageNumber <= processor.Document.Pages.Count; pageNumber++)
                Items.Add(new PdfPageModel(processor, pageNumber));
        }

        public void Dispose() {
            if(processor != null)
                processor.Dispose();
            GC.SuppressFinalize(this);
        }
    }
}
using DevExpress.Pdf;
using System;
using DevExpress.Office.DigitalSignatures;

namespace AspNetCoreDemos.OfficeFileAPI {
    public class PdfSignatureModel : PdfPreviewModelContainer {
        public PdfSignatureModel() : base("PdfSignaturePartial", "DocumentProtection") {
        }

        public string Reason { get; set; }
        public string Location { get; set; }
        public string ContactInfo { get; set; }
        public HashAlgorithmType HashAlgorithm { get; set; } = HashAlgorithmType.SHA256;
        public Uri TSAServer { get; set; } = new Uri("https://freetsa.org/tsr");
    }
}