15

With WebCrypto API evolving and being supported by Chrome and Firefox, I would like to use it for digitally signing a PDF document. There is not much of literature around, but I found some examples [1] and a library called PKI.js [2]. In the examples, the signing process is described, but in the end, a signature is returned. I would expect my Base64 PDF file returned again in a signed Base64 string, but sadly, this is not what happens. PKI.js too, to my knowledge, does not provide a way to sign my Base64 PDF.

Is there a way to sign a PDF with JavaScript and the WebCrypto API only? The private key can be entered in a <textarea> or, even better, stored in the certificate settings of the browser.

Base64 PDF (from REST API) → Sign with JS & certificate → Signed Base64 PDF (send to REST)

ssc-hrep3
  • 15,024
  • 7
  • 48
  • 87

4 Answers4

8

Disclosure: I work for CISPL.

As of now, WebCrypto API does not provide access to (Windows) or any other Key stores or local crypto USB/Smartcard device.

Also in most of the signing scenarios, for requirement to protect pdf file within the server boundaries, its not recommended to send complete pdf file to browser or to signing API server.

Thus, its good practice, to create hash of PDF for signing, send hash to browser and use javascript through browser extension to access some application running on local system to access local keystore (or USB/Smartcard) and produce the signature and send back (PKCS7 or CMS container in case of PDF signing) to server where the signature may be injected back to PDF from which hash was created for signing and was sent to browser or to signing api server.

For browser based signing scenarios, my company provides one such free Browser extension Signer.Digital and .NET library required on server. Local system (host running behind the chrome browser on windows) may be downloaded from cNET Download site Installing this host and restarting Chrome will automatically add Signer.Digital Chrome Extension and/or Signer.Digital Firefox Extension

The actual working of this extension is illustrated here along with complete code walk through and download link to working sample VS 2015 project source code.

Javascript to call method from extension:

 //Calculate Sign for the Hash by Calling function from Extension SignerDigital

 SignerDigital.signPdfHash(hash, $("#CertThumbPrint").val(), "SHA-256")      //or "SHA256"
  //SignerDigitial.signHashCAdESBr method may be used for producing ICP-Brazil Signature

  .then(
         function (signDataResp) {
           //Send signDataResp to Server
     },
         function (errmsg) {
             //Send errmsg to server or display the result in browser.
           }
  );

If success, returns Base64 encoded pkcs7 signature - use suitable library or one provided by Signer.Digital to inject sign to pdf

If Failed, returns error msg starting with "SDHost Error:"

Digital Signing from Browser

Digital Signing from Browser

  1. Server send Hash of data/document/content to be signed to browser.
  2. Browser uses Signer.Digital Browser Extension Javascript API to invoke action from Signer.Digital Browser Extension Host.
  3. On Windows, Browser Extension Host uses Microsoft Certificate store and underlaying CSP to get hash signed.
  4. On Linux, Browser Extension Host uses PKCS#11 .SO library of Crypto Device to get hash signed.
  5. Raw Signature (Signature of Hash) or Signature Container is returned to browser by Signer.Digital Browser EXtension Host.
  6. In case of Crypto Device viz. USB Token or Smartcard, private key of the user never comes out of the device but hash to be signed is sent to device to get it signed.
  7. The web application (Javascript in Browser) sends Signature back to server and same can be emended in PDF Document, or XML or Json or as required.

JULY 2022: Added method signCAdESBr to sign PDF or content as per ICP-Brazil standard and method signCAdESEg to sign as per Egypt ITIDA CAdES-BES.

Bharat Vasant
  • 850
  • 3
  • 12
  • 46
  • 1
    *"its good practice..."* - well, what good practice is, depends on which application you trust more. Your answer assumes that the server application is trustworthy, that it can be trusted to supply the hash for the pdf the user does want to sign. This assumption might not be true for a user dealing with some server application for the first time who happens to have a signing app he trusts on his computer.. – mkl Apr 23 '19 at 15:13
  • @mki, I am talking about pdf being generated on server. If user has PDF on his own computer, then there are plenty of tools available including most used Acrobat Reader to sign the PDF document... But question is about signing using JavaScript which means document is on Server and Signature is on Browser. – Bharat Vasant Apr 23 '19 at 17:36
  • 1
    *"I am talking about pdf being generated on server."* - even then, if I don't trust that server to send me the correct hash, I (as a user) want to be able to instead apply a signature by downloading the pdf, signing it locally using trusted software, and then uploading the signed pdf again. I have to admit, though, that arbitrary browser extensions don't really count as trusted software here, so here it indeed hardly matters whether the use case transfers only a hash or a whole document to me... – mkl Apr 23 '19 at 20:59
  • Some examples being signing Invoice or PO just prepared by me on my Companies' Web application (say CRM or web based accounting software) and I want to finally put sign. Other example is eReturn I have just previewed on my Companies web application or eReturn service providers' web UI, then I don't prefer to view XML or Json of return before signing.. Just trying to help trusted cases... you want me to replace "good practice" with "common practice" ?! :) – Bharat Vasant Apr 24 '19 at 00:05
  • 1
    No need to change anything. If the user has reason to trust the server in question (e.g. supported by an appropriate CC certification), that solution does have its charm. – mkl Apr 24 '19 at 05:46
  • Hi @BharatVasant can you provide this for .net core also? – صفي Nov 06 '20 at 13:40
  • Yes, we have .Net core library and ready application also. – Bharat Vasant Nov 06 '20 at 13:54
7

It is technically possible to do this, in-fact it is one of the scenarios we had in mind when we made PKIjs (which is why there is this sample) - https://pkijs.org/examples/PDFexample.html

That said to do signing requires working with the PDF structure itself, which either requires a custom parser or modifications to an existing one (pdfjs for example).

Long story short, signing a PDF in browser will take a lot of work, it is something we are working on though.

rmhrisk
  • 1,814
  • 10
  • 16
  • I want to sign plain text, so is that possible with this library? I did not find an example that can read a p12 cert+private key and produce a signature. – Michael Chourdakis Jul 03 '16 at 10:59
  • 1
    You can sign anything with it. This example signs arbitrary files - https://pkijs.org/examples/CMSSigned_complex_example.html this example shows importing PKCS#12s - https://pkijs.org/examples/PKCS12SimpleExample.html see http://unmitigatedrisk.com/?p=543 for some limitations and implementation notes. – rmhrisk Jul 04 '16 at 20:37
2

There is PDFSign.js, a library that can sign a PDF file in the browser. It uses forge though for the signature. If PKI.js supports detached pkcs7 signatures, then it should be easy to replace forge.

0

You can sign any file (including pdf) using openpgp.js

https://openpgpjs.org/openpgpjs/doc/#create-and-verify-detached-signatures

(scroll down to 'create-and-verify-detached-signatures')

Read the file as a Uint8Array and sign it with your private key.

user2677034
  • 624
  • 10
  • 20
  • 7
    Whenever people want to sign and stress the fact that they want to sign pdfs, they often mean that they want to sign using integrated pdf signatures and not using separate, detached signature files. This implies, for interoperable signatures, PKCS#1/PKCS#7 formats based on X.509 certificates, not PGP formats. – mkl Dec 31 '19 at 18:15