In addition to encrypting and digitally signing a PDF with an electronic certificate, storing the raw data of the signature, including point positions, velocity, and acceleration, can help strengthen the evidence. By generating and storing a hash of the raw signature data in the encrypted and digitally signed document and in a database, signed documents can be compared and validated.
Since version 32.0.2 of the Document Viewer, the SignatureData object contains the raw signature data. The SignaturePoint class stores the location of each captured signature point and a time stamp.
Learn More
During the e-signing process, the document is encrypted and digitally signed and is therefore tamper-proof. Additional features, such as storing the raw signature data, including point positions, velocity, and acceleration, can enhance the evidence.
Signature Acquisition
This example shows how to extract the signature lines from the returned signature data and store a hash of it as an attachment to the created PDF document.
The first step is to use the Document Viewer to capture a signature, which is then used to sign the document. To do this, click the Sign New Document button.
Generating the Hash
The ProcessSignature method creates the hash from the signature data. This data is then stored in a local json database file.
[HttpPost] | |
public IActionResult ProcessSignature([FromBody] TXTextControl.Web.MVC.DocumentViewer.Models.SignatureData data) | |
{ | |
if (data == null) | |
{ | |
return BadRequest(); | |
} | |
string signatureDataJson = JsonConvert.SerializeObject(data.SignatureLines); | |
string hash = CreateHash(signatureDataJson); | |
Envelope envelope = new Envelope | |
{ | |
DocumentId = data.UniqueId, | |
SignatureHash = hash, | |
SignatureData = signatureDataJson | |
}; | |
AddEnvelope(envelope); | |
if (SaveSignedPDF(data, envelope)) | |
{ | |
return Ok(true); | |
} | |
else | |
{ | |
return StatusCode(500); | |
} | |
} |
The CreateHash method is the calculation of a SHA256 hash of the signature lines that have previously been serialized as JSON.
private string CreateHash(string json) | |
{ | |
using (SHA256 sha256Hash = SHA256.Create()) | |
{ | |
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(json)); | |
return BitConverter.ToString(bytes).Replace("-", "").ToLower(); | |
} | |
} |
Embedding Attachment
The SaveSignedPDF method creates an Embedded
╰ TXTextControl Namespace
╰ EmbeddedFile Class
The EmbeddedFile class represents a file embedded in another document. object that contains the hash of the signature lines along with the unique document ID and the actual signature data. The actual signature lines are not necessarily needed, but demonstrate the possibility in case the hash needs to be regenerated. Finally, a digital signature is applied and the embedded file is attached to the PDF document.
private bool SaveSignedPDF(TXTextControl.Web.MVC.DocumentViewer.Models.SignatureData data, Envelope envelope) | |
{ | |
try | |
{ | |
using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl()) | |
{ | |
tx.Create(); | |
tx.Load(Convert.FromBase64String(data.SignedDocument.Document), TXTextControl.BinaryStreamType.InternalUnicodeFormat); | |
var embeddedFile = new EmbeddedFile($"tx-hash_{data.UniqueId}.txt", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(envelope)), null) | |
{ | |
Relationship = "Data", | |
MIMEType = "application/json" | |
}; | |
X509Certificate2 cert = new X509Certificate2("App_Data/textcontrolself.pfx", "123"); | |
var saveSettings = new TXTextControl.SaveSettings | |
{ | |
EmbeddedFiles = new EmbeddedFile[] { embeddedFile }, | |
CreatorApplication = "TX Text Control Sample Application", | |
SignatureFields = new DigitalSignature[] | |
{ | |
new TXTextControl.DigitalSignature(cert, null, "txsign") | |
} | |
}; | |
var savePath = Path.Combine("App_Data/signed", $"{data.UniqueId}.pdf"); | |
Directory.CreateDirectory(Path.GetDirectoryName(savePath)); | |
tx.Save(savePath, TXTextControl.StreamType.AdobePDF, saveSettings); | |
} | |
return true; | |
} | |
catch (Exception) | |
{ | |
return false; | |
} | |
} |
After the document is signed, it is listed in the summary table.
Now click on the Download button to download the PDF document that was generated. You can see the attached text file in the Attachments tab when you open it in Acrobat Reader.
If you open the attachment in a text editor, you will be able to see the json structure, including the hash value that was generated.
{ | |
"DocumentId":"5df83af0-f456-4485-a29f-6290c523067b", | |
"SignatureHash":"7592d7e308a1aa3cf2c26f7226c572fc1c744a223a0c6eed0431312d807d23c0", | |
"SignatureData":"[[{\"X\":190.0,\"Y\":29.28125,\"CreationTimeStamp\":1698671635701},...]]" | |
} |
Then click the Choose File button and select the file you downloaded. Confirm by clicking Validate Signature Hash. You should see the following page if the signature hash comparison was successful and the stored hashes match.
Extracting the Hash
The Validate method extracts the hash information from the PDF and compares the stored hash values from the document and the local database file.
public IActionResult Validate([FromForm] IFormFile file) | |
{ | |
if (file == null) | |
{ | |
return NotFound(); | |
} | |
byte[] bPDF = GetBytesFromFormFile(file); | |
var uploadedEnvelope = ExtractEnvelopeFromPDF(bPDF); | |
if (uploadedEnvelope == null) | |
{ | |
return NotFound(); | |
} | |
var envelopes = LoadEnvelopesFromJson(); | |
var envelope = envelopes.FirstOrDefault(e => e.DocumentId == uploadedEnvelope.DocumentId); | |
if (envelope != null && envelope.SignatureHash == uploadedEnvelope.SignatureHash) | |
{ | |
return View(true); | |
} | |
return View(false); | |
} |
The ExtractEnvelopeFromPDF method uses the Server
╰ TXTextControl Namespace
╰ ServerTextControl Class
The ServerTextControl class implements a component that provide high-level text processing features for server-based applications. class to load the PDF document and extract the attachment from the Embedded
╰ TXTextControl Namespace
╰ LoadSaveSettingsBase Class
╰ EmbeddedFiles Property
Specifies an array of EmbeddedFile objects which will be embedded in the saved document. property.
private Envelope ExtractEnvelopeFromPDF(byte[] document) | |
{ | |
using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl()) | |
{ | |
tx.Create(); | |
var loadSettings = new TXTextControl.LoadSettings | |
{ | |
EmbeddedFiles = new EmbeddedFile[] { }, | |
}; | |
tx.Load(document, TXTextControl.BinaryStreamType.AdobePDF, loadSettings); | |
var embeddedFile = loadSettings.EmbeddedFiles | |
.FirstOrDefault(ef => ef.FileName.StartsWith("tx-hash_")); | |
if (embeddedFile != null) | |
{ | |
return JsonConvert.DeserializeObject<Envelope>(Encoding.UTF8.GetString((byte[])embeddedFile.Data)); | |
} | |
} | |
return null; | |
} |
Conclusion
The storage and comparison of signature raw data hash values is a powerful feature that can add an extra layer of security and trust to e-signature workflows.
You will be able to test the sample by downloading it from our GitHub repository.