mozilla / pdf.js

PDF Reader in JavaScript
https://mozilla.github.io/pdf.js/
Apache License 2.0
48.58k stars 9.99k forks source link

Digital signatures in pdf.js #1076

Closed soa-x closed 3 years ago

soa-x commented 12 years ago

Hi! We are interested about when or if you´re going to implement the Signature Data View (Xades, Pades & Cades) in the PDF viewer (PDF.js)

Kind Regards

Alejandro Pinedo, SOA-X

Epsiom commented 4 years ago

@konum The warning is from "http://localhost:4200/assets/vendor/pdf.worker.js", but I already commented out the two lines of this file as told before: if (data.fieldType === 'Sig') { //this.setFlags(AnnotationFlag.HIDDEN); //data.fieldValue = null; }

Still no digital signature in sight...

konum commented 4 years ago

@Epsiom Didn't work for me either. Still looking into it.

konum commented 4 years ago

@Epsiom Managed to get the signature to view. In pdf.worker.js add this on line 18480 case "Sig": return new SquareAnnotation(parameters);

Is the switch just above this line. (0, _util.warn)('Unimplemented widget field type "' + fieldType + '", ' + "falling back to base field type.");

Epsiom commented 4 years ago

@konum Didn't work for me : ERROR Error: Uncaught (in promise): Object: {"message":"SquareAnnotation is not defined","stack":"AnnotationFactory_create@http://localhost:4200/assets/vendor/pdf.worker.js:47999:7\nget annotations@http://localhost:4200/assets/vendor/pdf.worker.js:48877:43\nLocalPdfManager_ensure/<@http://localhost:4200/assets/vendor/pdf.worker.js:49204:21\nLocalPdfManager_ensure@http://localhost:4200/assets/vendor/pdf.worker.js:49202:14\nPage_getOperatorList@http://localhost:4200/assets/vendor/pdf.worker.js:48815:43\nwphSetupRenderPage/<@http://localhost:4200/assets/vendor/pdf.worker.js:49883:13\n"} AnnotationFactory_create@http://localhost:4200/assets/vendor/pdf.worker.js:47999:7 get annotations@http://localhost:4200/assets/vendor/pdf.worker.js:48877:43 LocalPdfManager_ensure/<@http://localhost:4200/assets/vendor/pdf.worker.js:49204:21 LocalPdfManager_ensure@http://localhost:4200/assets/vendor/pdf.worker.js:49202:14 Page_getOperatorList@http://localhost:4200/assets/vendor/pdf.worker.js:48815:43 wphSetupRenderPage/<@http://localhost:4200/assets/vendor/pdf.worker.js:49883:13

konum commented 4 years ago

@Epsiom Are you using latest version of pdf.js? in pdf.worker.js there should be SquareAnnotation class defined.

kjhangiani commented 4 years ago

@Epsiom just to clarify, only this.setFlags(AnnotationFlag.HIDDEN); should be commented. data.fieldValue = null; should be added if its not present, but should NOT be commented.

We are also using the pdfjs-dist package, so we modified that one directly, as per an earlier comment. Are you positive that the code you are modifying is actually being executed? If it's in node_modules, depending on your build, some of those assets might be cached, and you might not actually be using your modified code.

Epsiom commented 4 years ago

@kjhangiani I clearly remember having any added console.log show up in the console, so it is clearly executed. I'll try to add data.fieldValue = null; when I come back to this task, and see if that works.

Epsiom commented 4 years ago

Adding data.fieldValue = null; back in the if (data.fieldType === 'Sig') block, as well as a console.log("TEST") just in case, and nothing happened, while "TEST" correctly appeared in the console, after the usual Warning: Unimplemented widget field type "Sig", falling back to base field type. message.

Any other idea?

yduybya commented 4 years ago

@Epsiom Managed to get the signature to view. In pdf.worker.js add this on line 18480 case "Sig": return new SquareAnnotation(parameters);

Is the switch just above this line. (0, _util.warn)('Unimplemented widget field type "' + fieldType + '", ' + "falling back to base field type.");

That worked for me, you saved my day, thank you.

Epsiom commented 4 years ago

@yduybya How recent is your project ? Is there any particularity about it ? I think I need to update my version of pdf.js and try it again, even though it is pretty recent.

Epsiom commented 4 years ago

Is there any easy way to update pdf.js on an Angular 9 app ? I found the new pdf.worker.js here (http://mozilla.github.io/pdf.js/build/pdf.worker.js), but only replacing that file didn't work (obviously).

Epsiom commented 4 years ago

I traced my project back to https://github.com/mozilla/pdf.js/issues/8836 and https://github.com/ashishyadav0019/pdfJS_angular4_Ts.git, but these projects are using outdated files.

Epsiom commented 4 years ago

The files on the pdf.js github and on pdfJS_angular4_Ts.git are not correlated. Does anybody know how to correctly import pdf.js on an Angular 9 project, with up-to-date files ?

konum commented 4 years ago

@Epsiom For Angular 8 app )I think it should work on angular 9) I am using https://github.com/VadimDez/ng2-pdf-viewer.

From ng2-pdf-viewer docs: By default the worker is loaded from cdnjs.cloudflare.com. In your code update path to the worker to be for example /pdf.worker.js (window as any).pdfWorkerSrc = '/pdf.worker.js'; This should be set before pdf-viewer component is rendered.

Using this project will add the pdfjs-dist dependency to your project. Edit the node_modules/pdfjs-dist/build/pdf.worker.js with the squareAnnotation tip.

Epsiom commented 4 years ago

@konum I'm using pdf.js directly and not ng2-pdf-viewer for multiple reasons, by getting inspired by this: https://github.com/ashishyadav0019/pdfJS_angular4_Ts.git I have the pdfjs-dist dependency, but it's the src/assets/vendor/pdf.worker.js that is used. Any idea ?

konum commented 4 years ago

@Epsiom Have you tried updating both pdf.js and pdf.worker.js files to latest code?

Epsiom commented 4 years ago

@konum That is precisely what I am trying to do, but since pdf.js is rarely used in Angular, as most project seemingly only need ng2-pdf-viewer, and since the files used in the latest version of pdf.js are different, the process of doing so is far from obvious at first glance.

lainosantos commented 4 years ago

Hello,

Commenting this.setFlags(AnnotationFlag.HIDDEN); in pdf.worker.js at v2.6.347 not show the signature image.

Was there a change in that?

stephanrauh commented 4 years ago

@lainosantos Yes, there's a change. If you deactivate form rendering, the signatures are shown again.

lainosantos commented 4 years ago

@stephanrauh Thanks.

PDFViewerApplicationOptions.set('renderInteractiveForms', false);

Is there a way to enable forms and show signatures?

stephanrauh commented 4 years ago

@lainosantos No, but I've found a nice walk-through how to build your own forms. The example assumes that renderInteractiveForms is dectivated. It uses Angular, but I guess you can extract the idea even if you're using a different framework: https://medium.com/factory-mind/angular-pdf-forms-fa72b15c3fbd

Epsiom commented 4 years ago

I have more urgent matters for the moment and cannot spend time on the task, but if anyone manages to import an up-to-date pdf.js version on Angular 9, he would have my deepest thanks.

stephanrauh commented 4 years ago

@Epsiom Here you are: https://www.npmjs.com/package/ngx-extended-pdf-viewer :)

Epsiom commented 4 years ago

@stephanrauh ...Well, except that I need to be compatible IE11 and only use the canvas directly to have a custom-made pdf viewer, so I cannot use that, sadly. I cannot use ng2-pdf-viewer either, or anything of the sort, I am really asking about pdf.js in itself, which is a really niche case.

QZhongyi commented 3 years ago

I've successfully show the signatures by commenting this.setFlags(AnnotationFlag.HIDDEN);, but it disappeared again after I zoom in the pdf by adjusting width of the parent element. Is there any solutions?

lexcorp commented 3 years ago

If anyone is interested in biometric signatures:

https://github.com/DennisWacom/AlphaSign

AlphaSign_SignatureSDK

AlphaSign_SignedDocument

You can even use certificates...

SkyGrass commented 3 years ago

@Epsiom Managed to get the signature to view. In pdf.worker.js add this on line 18480 case "Sig": return new SquareAnnotation(parameters); Is the switch just above this line. (0, _util.warn)('Unimplemented widget field type "' + fieldType + '", ' + "falling back to base field type.");

That worked for me, you saved my day, thank you.

worked for me ! thank you.

earthchie commented 3 years ago

Hi guys. I've been searching for a way to parsing signature information from PDF so at some point I came across this thread. It's not related to PDF.js but this can be a workaround anyway. So I'm gonna share it.

Alright, if you happen to open signed-PDF with a text editor and search for a word adbe.pkcs7.detached you'll found this chunk of data.

image

there will be 5 lines of data for each signature attached to this document.

the first line and last line has nothing much, just ignore it.

on the 2nd line, data in between <</Contents <$HexDerIsHere> is a Der in hex format. You can convert it to ASN.1 then to Signature message with forge.js

you can also get a Location, Timestamp of signature, and Reason on the 3rd and 4th line.

My English is bad I think I talk too much, I should let the code speak.

Dependency: forge.js

<form id="getPDFSignature">
    <input type="file" name="pdf" accept="application/pdf" required>
    <button>get Signatures</button>
</form>
document.getElementById('getPDFSignature').onsubmit = function (e) {
    e.preventDefault();

    let FR = new FileReader();
    FR.onload = function (e) {
        let Signatures = getPDFSignature(e.target.result);
        if(Signatures){
            console.log(Signatures);
        }else{
            alert('This document does not contains any signature.');
        }
    }
    FR.readAsText(this.pdf.files[0]);

    return false;
}

function getPDFSignature(pdf){

    let Signatures = [];
    let PKCS7 = pdf.split(/\n\n/).filter(d=>d.indexOf('adbe.pkcs7.detached') > -1); // search for paragraph with word "adbe.pkcs7.detached"

    PKCS7.forEach(function(sig){
        let sigHex = sig.split(/\n/)[1].slice(13, -1).replace(/(?:00)*$/, ''); // get signature from the second line, also remove 0 padding at the end
        let p7Asn1 = forge.asn1.fromDer(hex2str(sigHex)); // convert hex to String to get Der, convert Der to ASN.1
        let message = forge.pkcs7.messageFromAsn1(p7Asn1); // convert ASN.1 to Signature Message

        let meta = sig.match(/\((.*?)\)/g); // [Location, Timestamp, Reason, ContactInfo]

        // timestamp format from string like this -> "(D:20210114234159+07'00')"
        let timestamp = `${meta[1].slice(3,7)}-${meta[1].slice(7,9)}-${meta[1].slice(9,11)}T${meta[1].slice(11,13)}:${meta[1].slice(13,15)}:${meta[1].slice(15,20)}${meta[1].slice(21,23)}`;

        meta = {
            Location: meta[0].slice(1,-1),
            M: timestamp,
            Reason: meta[2].slice(1,-1),
            ContactInfo: meta[3].slice(1,-1),
        };

        Signatures.push({
            message: message,
            meta: meta
        });
    })
}

function hex2str(str1){
    var hex  = str1.toString();
    var str = '';
    for (var n = 0; n < hex.length; n += 2) {
        str += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
    }
    return str;
}
softboy99 commented 3 years ago

Hi, we need this too

Mageenz commented 3 years ago

@Epsiom Managed to get the signature to view. In pdf.worker.js add this on line 18480 case "Sig": return new SquareAnnotation(parameters); Is the switch just above this line. (0, _util.warn)('Unimplemented widget field type "' + fieldType + '", ' + "falling back to base field type.");

That worked for me, you saved my day, thank you.

worked for me ! thank you. nouse in v2.6.347

stephanrauh commented 3 years ago

@earthchie Your code snippet looks promising. Does it also verify the signature is valid? (I'm new to cryptography, so I really don't know).

softboy99 commented 3 years ago

Hi, I'm confused. Does this lib have api to digitally sign the pdf file? or we discussed here is just to show the signed pdf's digital signature?

stephanrauh commented 3 years ago

@softboy99 No, pdf.js doesn't have an API to sign a PDF file. And it doesn't display digital signatures because the team discovered it's difficult to verify the signature is valid. However, some people found out it's possible to display signatures nonetheless. What I'm interested in is the original idea: displaying valid, correctly signed signatures, and being able to detect fraud signatures. The code snippet provided by earthchie seems to point into this direction. But since I'm new to cryptography, I can't tell whether it can be used to verify the signature is valid. Does anybody following this discussion know?

softboy99 commented 3 years ago

Hi, @stephanrauh, thanks for your clarifications, this issue has been opened almost 10 years. I suggests pdf.js should design this api because it is very important to many officialy published pdf files, otherwise, why the pdf is needed, they can use other file formats. We should use community power to do this. Suggest the following lib for refence: https://github.com/Communication-Systems-Group/pdfsign.js ,which use forge lib for the cryptography.

rmhrisk commented 3 years ago

@softboy99 No, pdf.js doesn't have an API to sign a PDF file. And it doesn't display digital signatures because the team discovered it's difficult to verify the signature is valid. However, some people found out it's possible to display signatures nonetheless. What I'm interested in is the original idea: displaying valid, correctly signed signatures, and being able to detect fraud signatures. The code snippet provided by earthchie seems to point into this direction. But since I'm new to cryptography, I can't tell whether it can be used to verify the signature is valid. Does anybody following this discussion know?

We do both sign and verify with https://verify.ink

softboy99 commented 3 years ago

Hi, @rmhrisk, it is a cloud server side solution. Ther're many colud server side solution in the world. we discussed here is an client side solution.

rmhrisk commented 3 years ago

Hi, @rmhrisk, it is a cloud server side solution. Ther're many colud server side solution in the world. we discussed here is an client side solution.

No, The document doesn’t leave the browser, the validation happens in browser, the signing happens in the browser, the encryption/decryption happens on the browser, the rendering happens in the browser, it’s all client side.

softboy99 commented 3 years ago

Hi, @rmhrisk, it is a cloud server side solution. Ther're many colud server side solution in the world. we discussed here is an client side solution.

No, The document doesn’t leave the browser, the validation happens in browser, the rendering happens in the browser, it’s all client side.

If your computer cannot connect to https://verify.ink/viewer, what will happen?

src="https://verify.ink/viewer?url={YOUR_DOCUMENT_URL}"

rmhrisk commented 3 years ago

Hi, @rmhrisk, it is a cloud server side solution. Ther're many colud server side solution in the world. we discussed here is an client side solution.

No, The document doesn’t leave the browser, the validation happens in browser, the rendering happens in the browser, it’s all client side.

If your computer cannot connect to https://verify.ink/viewer, what will happen?

src="https://verify.ink/viewer?url={YOUR_DOCUMENT_URL}"

It’s possible to host the web component on ones own servers too.

stephanrauh commented 3 years ago

@softboy99 I'm not a member of the pdf.js team. I just maintain of fork of pdf.js for my own PDF viewer. However, what we can do is implement the feature in ngx-extended-pdf-viewer until it's mature. After that, we can offer the implementation to pdf.js. I can't do that alone (for lack of knowledge and lack of leisure time), so I'd appreciate help. Maybe even your help?

earthchie commented 3 years ago

@earthchie Your code snippet looks promising. Does it also verify the signature is valid? (I'm new to cryptography, so I really don't know).

Sorry for the late reply.

The answer is no. However, this might help you: https://github.com/MohammedEssehemy/node-sign-validate-pdf/blob/master/script.js#L338

If I understand correctly. There are two steps to verify a PDF signature.

  1. decrypt the signature message with a public key of the singer. you'll get the hash as a result.
  2. sha256 the PDF content, then compare with step 1. if two hashes match, the signature is valid.

but it is not as easy as it seems. In step 2, you'll need to slice some of the PDF content precisely, or else the hash will shift. The start and end positions can be found in ByteRange on the 4th lines. This is very tricky and I still can't figure what I do wrong because I can't get the right hash yet.

Recently I also found a signed-PDF which signature is not in the 5-line format too. So the snippet I provided earlier might not work all the time.

Please try this instead. (I know, the code is a mess lol. Please forgive me.)

<form id="getPDFSignature">
    <input type="file" name="pdf" accept="application/pdf" required>
    <button>get Signatures</button>
</form>
document.getElementById('getPDFSignature').onsubmit = function (e) {
    e.preventDefault();

    let FR = new FileReader();
    FR.onload = function (e) {
        let Signatures = getPDFSignature(e.target.result);
        if(Signatures){
            console.log(Signatures);
        }else{
            alert('This document does not contains any signature.');
        }
    }
    FR.readAsText(this.pdf.files[0]);

    return false;
}

function getPDFSignature(pdf){
  return pdf.split(/endobj/).filter(l=>l.indexOf('adbe.pkcs7.detached')>-1).map(d=>{
      let sig = {};
      d = d.trim();
      let date = d.match(/\/M\(D:(.*?)\)/)[1].replace("'",'').match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(.{5})/);
      let reason = d.match(/\/Reason\((.*?)\)\//);
      let location = d.match(/\/Location\((.*?)\)\//);
      sig.raw = d;
      sig.header = d.match(/(\d) (\d) obj/)[0];
      sig.ByteRange = d.match(/\/ByteRange.*?\[(.*?)\]/)[1].trim().split(' ').map(i=>+i);
      sig.Date = new Date(`${date[1]}-${date[2]}-${date[3]}T${date[4]}:${date[5]}:${date[6]}${date[7]}`);
      sig.Contents = d.match(/\/Contents *?\<(.*?)\>/)[1].replace(/(?:00)*$/, '');
      sig.Signature = {Der: hex2str(sig.Contents)};
      sig.Signature.ASN1 = forge.asn1.fromDer(sig.Signature.Der);
      sig.Signature.message = forge.pkcs7.messageFromAsn1(sig.Signature.ASN1);

      if(reason){
          sig.Reason = reason[1];
      }else{
          sig.Reason = '';
      }
      if(location){
          sig.Location = location[1];
      }else{
          sig.Location = '';
      }

      return sig;
  });
}

function hex2str(str1){
    var hex  = str1.toString();
    var str = '';
    for (var n = 0; n < hex.length; n += 2) {
        str += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
    }
    return str;
}

Will update ya later once I successfully validate the signature with PDF content.

marco-c commented 3 years ago

We are now displaying signatures (https://github.com/mozilla/pdf.js/pull/13214), but we are not validating them yet.

timvandermeij commented 3 years ago

Closing since digital signatures are now displayed. Validation is tracked in issue #13351 now because this thread has become very large and hard to keep track of.

kjhangiani commented 3 years ago

@timvandermeij do you know which version specifically pdfjs started displaying digital signatures? We are currently still using a forked version with the code snippets in this thread to display them in our app - it would be nice to switch back to mainline. I tried scouring the releases and this thread but I do not see the related PR.

Thanks!

timvandermeij commented 3 years ago

The PR is #13214 and it's released yet.

mayconmarchiori commented 3 years ago

Buenas!

Deu certo aqui, uso na versão NC 21.

Comentei as linhas 30532 até 30536.

Print -> https://prnt.sc/17wlla4

sylvestre commented 2 years ago

@soa-x Sorry but who is "We" in comment #0 ? :) And what is your use case? Thanks