vbuch / node-signpdf

Simple signing of PDFs in node.
MIT License
688 stars 175 forks source link

Sign with P7 #144

Closed leandroparedes closed 3 years ago

leandroparedes commented 3 years ago

Hello everyone.

In my company we are developing a feature that involves signing pdf's. We sent the pdf to an external API which responds with something like:

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

The API documentation says the returned string is in format PKCS#7 so we added the header -----BEGIN PKCS7----- and footer -----END PKCS7-----. Then we do forge.pkcs7.messageFromPem and this give us a p7 object. Then I tried to sign the PDF using some code from this library:

// Check if the PDF has a good enough placeholder to fit the signature.
    const raw = forge.asn1.toDer(p7.toAsn1()).getBytes();

    // placeholderLength represents the length of the HEXified symbols but we're
    // checking the actual lengths.
    if (raw.length * 2 > placeholderLength) {
        throw new SignPdfError(
            `Signature exceeds placeholder length: ${
                raw.length * 2
            } > ${placeholderLength}`,
            SignPdfError.TYPE_INPUT
        );
    }

    let signature = Buffer.from(raw, "binary").toString("hex");

    // Pad the signature with zeroes so the it is the same length as the placeholder
    signature += Buffer.from(
        String.fromCharCode(0).repeat(placeholderLength / 2 - raw.length)
    ).toString("hex");

    // Place it in the document.
    pdf = Buffer.concat([
        pdf.slice(0, byteRange[1]),
        Buffer.from(`<${signature}>`),
        pdf.slice(byteRange[1]),
    ]);

And it seems to be adding the signature just right on the PDF

13 0 obj
<<
/Type /Sig
/Filter /Adobe.PPKLite
/SubFilter /adbe.pkcs7.detached
/ByteRange [0 1158 17544 794]
/Contents <30820d9...0000000000000000>
/Reason (Verificacion de documento publico)
/M (D:20210809213702Z)
/ContactInfo (emailfromp1289@gmail.com)
/Name (Name from p12)
/Location (Location from p12)
>>
endobj

But when I open the PDF with Adobe Reader its says:

At least one signature is invalid. There are errors in the formatting or information contained in this signature.

This is my first time working with pdf signatures so any guidance is appreciated! Thanks.

fdimarh commented 2 years ago

has this problem been resolved?

ParitoshKarnatak commented 1 month ago

Is there any solution on this for signing a pdf using pkcs7 in nodejs?