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.
@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");
}
}