vbuch / node-signpdf

Simple signing of PDFs in node.
MIT License
719 stars 178 forks source link

[Help] Could not find ByteRange placeholder #17

Closed kokhowchuang closed 5 years ago

kokhowchuang commented 5 years ago

First of all, thank you for this amazing library. I would like to know when I read the PDF, there is an error 'Could not find ByteRange placeholder', how can I append the ByteRange to an existing PDF if it is not found?

vbuch commented 5 years ago

The first thing you need to know is that PDFs are readable as plain text so opening it up with notepad++, vim, nano or just cat should give you an idea of the format. The non-readable part is the actual pages (binary) but in terms of signing that doesn't matter. The next thing I would recommend is for you to check the Apeend a signature section and whateer it links to. I imagine that altering a ready PDF would be a hard task. It is also not something that you should do. PDFs have versioning so that they can be edited and altered, signed by multiple authors, etc. So that would be the right direction if you have an already ready PDF. If you are just creating one, it should be possible to add a placeholder for the signature. If you are working with PdfKit (which I strongly recommend), you can just go through the sample code in the tests and figure it out. Let me know if I can assist any further.

kokhowchuang commented 5 years ago

Thanks for reply. Actually I would like to create something similar to Hellosign, the pdf returned has a digital signature attached on the pdf. Similar to their solution, users can create a signable pdf and get signed then we would like to attach the digital signature on user's pdf (the final step). Is that possible to solve this issue using your library? Or do you have any idea how to solve this problem? I already used 2 days for this problem.

vbuch commented 5 years ago

2 days are not that much. You need to understand the Pdf standard if you are going to do that. On whose behalf Will you be signing? Your organisation sign the documents you received from the user or you are signing it on behalf of the user? Are you generating the pdf or you are getting it ready and viewable? We've tried to add as much info as possible on how this lib works. We've also tried to write the code as much readable as possible. Depending on your usecase you May need to implement incremental update and then signatures or add placeholder to the generated one.

ngvcanh commented 5 years ago

I have the same problem

this is my code

const signer = require('node-signpdf').default;
const fs = require('fs');

const signPDF = signer.sign(
  fs.readFileSync(__dirname + '/5c3812a8c7e5b23da2703569.pdf'),
  fs.readFileSync(__dirname + '/my_key.p12'),
  { passphrase : '12345678' }
)
fs.writeFileSync(__dirname + '/demo.pdf', signPDF);

There is an error 'Could not find ByteRange placeholder'

Error: Could not find ByteRange placeholder: /ByteRange [0 /********** /********** /**********]

When I write another code

const signer = require('node-signpdf').default;
const fs = require('fs');
const { addSignaturePlaceholder } = require('./node-signpdf-master/dist/helpers');
const PDFDocument = require('pdfkit');

const createPdf = (params = {
    placeholder: { reason : 'Digital signed by my key' },
    text: 'this is content pdf',
  }) => new Promise((resolve) => {
    const pdf = new PDFDocument({
        autoFirstPage: true,
        size: 'A4',
        layout: 'portrait',
        bufferPages: true,
    });
    pdf.info.CreationDate = '';
    pdf.fillColor('#333').fontSize(25).moveDown().text(params.text);

    const pdfChunks = [];
    pdf.on('data', (data) => {
        pdfChunks.push(data);
    });
    pdf.on('end', () => {
        resolve(Buffer.concat(pdfChunks));
    });

    const refs = addSignaturePlaceholder({
        pdf,
        reason: 'I am the author',
        ...params.placeholder,
    });

    Object.keys(refs).forEach(key => refs[key].end());
    pdf.end();
});

const action = async () => {
  let pdfBuffer = await createPdf();
  let p12Buffer = fs.readFileSync(__dirname + '/baominh.key.p12')

  let pdf = signer.sign(pdfBuffer, p12Buffer, { passphrase : '12345678', asn1StrictParsing : true });
  fs.writeFileSync(__dirname + '/demo.pdf', pdf);
}

action();

I have demo.pdf file but there is no signature

vbuch commented 5 years ago

@ngvcanh your code looks fine at first glance. I will try to run it locally sometime today and give you feedback. Glad you're experimenting with it. That's what we expect people to do - actually understand what's going on inside not just trust what we've written.

vbuch commented 5 years ago

So, @ngvcanh , I did try your code out. And it does actually work. I did not have your p12 so I used one from this repo. The result, as you can see is a signed PDF: https://imgur.com/a/AnxwZiP Your code that I tried out is here: https://github.com/vbuch/ngvcanh-node-signpdf Here is what I needed to change: https://github.com/vbuch/ngvcanh-node-signpdf/commit/6eff543556c1a5269ca86711a7cf0b9aaf6c839c Let me know if I can help any further

vbuch commented 5 years ago

@zincchuang, @ngvcanh did you guys succeed? Is this issue good to close?

kokhowchuang commented 5 years ago

Yea, you can close it.

angeltg83 commented 5 years ago

I have this: Could not find ByteRange placeholder: /ByteRange [0 /**** /** /**],**

ngvcanh commented 5 years ago

I have this: Could not find ByteRange placeholder: /ByteRange [0 /**** /** /**],**

Using my code above

datpham96 commented 5 years ago

Hi, @ngvcanh , Can I get that code?

ngvcanh commented 5 years ago

Hi, @ngvcanh , Can I get that code?

Code here (Code của bạn đây)

const signer = require('node-signpdf').default;
const fs = require('fs');
const { addSignaturePlaceholder } = require('node-signpdf/dist/helpers');
const PDFDocument = require('pdfkit');

const createPdf = (params = {
    placeholder: { reason : 'Digital signed by my key' },
    text: 'this is content pdf',
  }) => new Promise((resolve) => {
    const pdf = new PDFDocument({
        autoFirstPage: true,
        size: 'A4',
        layout: 'portrait',
        bufferPages: true,
    });
    pdf.info.CreationDate = '';
    pdf.fillColor('#333').fontSize(25).moveDown().text(params.text);

    const pdfChunks = [];
    pdf.on('data', (data) => {
        pdfChunks.push(data);
    });
    pdf.on('end', () => {
        resolve(Buffer.concat(pdfChunks));
    });

    const refs = addSignaturePlaceholder({
        pdf,
        reason: 'I am the author',
        ...params.placeholder,
    });

    Object.keys(refs).forEach(key => refs[key].end());
    pdf.end();
});

const action = async () => {
  let pdfBuffer = await createPdf();
  let p12Buffer = fs.readFileSync(__dirname + '/file.key.p12')

  let pdf = signer.sign(pdfBuffer, p12Buffer, { passphrase : '12345678', asn1StrictParsing : true });
  fs.writeFileSync(__dirname + '/demo.pdf', pdf);
}

action();
juttameerhamza commented 4 years ago

Hi, @ngvcanh , Can I get that code?

Code here (Code của bạn đây)

const signer = require('node-signpdf').default;
const fs = require('fs');
const { addSignaturePlaceholder } = require('node-signpdf/dist/helpers');
const PDFDocument = require('pdfkit');

const createPdf = (params = {
    placeholder: { reason : 'Digital signed by my key' },
    text: 'this is content pdf',
  }) => new Promise((resolve) => {
    const pdf = new PDFDocument({
        autoFirstPage: true,
        size: 'A4',
        layout: 'portrait',
        bufferPages: true,
    });
    pdf.info.CreationDate = '';
    pdf.fillColor('#333').fontSize(25).moveDown().text(params.text);

    const pdfChunks = [];
    pdf.on('data', (data) => {
        pdfChunks.push(data);
    });
    pdf.on('end', () => {
        resolve(Buffer.concat(pdfChunks));
    });

    const refs = addSignaturePlaceholder({
        pdf,
        reason: 'I am the author',
        ...params.placeholder,
    });

    Object.keys(refs).forEach(key => refs[key].end());
    pdf.end();
});

const action = async () => {
  let pdfBuffer = await createPdf();
  let p12Buffer = fs.readFileSync(__dirname + '/file.key.p12')

  let pdf = signer.sign(pdfBuffer, p12Buffer, { passphrase : '12345678', asn1StrictParsing : true });
  fs.writeFileSync(__dirname + '/demo.pdf', pdf);
}

action();

which value you are passing in placeholder ?

erickximenes commented 4 years ago

Olá, @ngvcanh , posso obter esse código?

Código aqui (Código của bạn đây)

const  signer  =  require ( 'node-signpdf' ) . padrão ; 
const  fs  =  require ( 'fs' ) ; 
const  { addSignaturePlaceholder }  =  require ( 'node-signpdf / dist / helpers' ) ; 
const  PDFDocument  =  require ( 'pdfkit' ) ; 

const  createPdf  =  ( params =  { 
    marcador de posição : {  motivo :'Digital assinado pela minha chave'  } , 
    texto : 'este é o conteúdo pdf' , 
  } )  =>  nova  promessa ( ( resolução )  =>  { 
    const  pdf  =  novo  PDFDocument ( { 
        autoFirstPage : true , 
        tamanho : 'A4' , 
        layout : 'portrait' , 
        bufferPages : true , 
    } ) ; 
    pdf . info . CreationDate  = '' ; 
    pdf . fillColor ( '# 333' ) . fontSize ( 25 ) . moveDown ( ) . texto ( params . texto ) ; 

    const  pdfChunks  =  [ ] ; 
    pdf . on ( 'dados' ,  ( dados )  =>  { 
        pdfChunks . push ( data ) ; 
    } ) ; 
    pdf . em( 'end' ,  ( )  =>  { 
        resolve ( Buffer . concat ( pdfChunks ) ) ; 
    } ) ; 

    const  refs  =  addSignaturePlaceholder ( { 
        pdf , 
        motivo : 'Eu sou o autor' , 
        ... params . placeholder , 
    } ) ; 

    Objeto . chaves ( refs ) . forEach ( key  => refs [ chave ] . end ( ) ) ; 
    pdf . end ( ) ; 
} ) ; 

ação const  = async ( ) => { deixe pdfBuffer = aguardar createPdf ( ) ; deixe p12Buffer = fs . readFileSync ( __dirname + '/file.key.p12' ) deixe pdf = signatário . sinal (     

     pdfBuffer ,  p12Buffer ,  {  senha : '12345678' ,  asn1StrictParsing : true  } ) ; 
  fs . writeFileSync ( __dirname  +  '/demo.pdf' ,  pdf ) ; 
} 

action ( ) ;

addSignaturePlaceholder is not function, what to do?

hamidpoorhashemi commented 3 years ago

@ZaunSupremoXV what I see in the test codes addSignaturePlaceholder is not a function is a variable defined in requestParams variable but maybe before version 1.0.0 it was a function and in version 1 that function's name changed to pdfkitAddPlaceholder. you can check it in changelog.md file

//**** My Question

The first question in this issue was about adding a signature in pdf that is exist not creating a new one and.. .

I have the same issue and when I read a pdf file (let pdfBuffer = fs.readFileSync(docPath); ) and I try to adding signature after plainAddPlaceholder it shows this error:

SignPdfError: No ByteRangeStrings found within PDF buffer at findByteRange.

I commented the signer.sign(..) and saved the pdf file using fs.writeFileSync('./save.pdf', pdfBuffer)

in the save.pdf: /ByteRange [0 undefined undefined undefined] and the problem is here.

what should I do to fix this problem in my code (the correct signed file is like this: /ByteRange [0 153 3379 1275] )

and one more thing : ( I guess this is not an issue and after I solve the ByteRange problem it will be okay ) in the correct file after /ByteRange there is /Contents <3082064806092a864886f... but in my save.pdf file it's like this : /Contents <0000000000

Thanks in advanced

vbuch commented 3 years ago

@hamidpoorhashemi I don't know how you edned up with undefined undefined, but here is where this happened: https://github.com/vbuch/node-signpdf/blob/427eac928b7a217482759abe525e688f9244bef9/src/helpers/pdfkitAddPlaceholder.js#L28

Once you have the correct placeholder, signing should just go on, yes.

charanjit-singh commented 2 years ago

I already have one PDF how can I add refs to it?

eliyahu2 commented 2 years ago

@vbuch Hello Sir, we have the same problem as @charanjit-singh - we have created our PDF using pdflibjs and we would like to sign this already created PDF, unfortunately we couldn't find PDFKit load document feature, Also, when trying to load a pdflibjs created file which contain a signature element (will attach the link) into node-signpdf, we receive various types of errors "PDF must contain EOF" (which it does contain BTW), and also "No ByteRangeStrings found within PDF buffer" ...

The helper pdfkitAddPlaceholder uses PDFKIT created PDFs, and we already have a created PDF...

Please, can you direct us to a resource we can find the entire process without using PDFKIT? or give us a tip how it can be done, Thanks in advance!

https://github.com/Hopding/pdf-lib/files/5264763/with_signature.pdf

vbuch commented 2 years ago

Hi @eliyahu2 . pdf-lib is much more advanced than what this lib here is stuck on. I think there was an example written by its author on how to add a signature placeholder. Edit: here is the example https://github.com/Hopding/pdf-lib/issues/112#issuecomment-569085380

If you can't manage, we have a helper that uses plain text instead of pdflib, so you could use it if your pdf does not use streams. It's called plainAddPlaceholder.

eliyahu2 commented 2 years ago

@vbuch Thank you sir for your quick reply! I have finally found it here in this link, if someone requires, it's called "Sign a PDF with pdflib-js on node-signpdf" it was pretty useful! Thanks for you assistance :)

https://github.com/RichardBray/pdf_sign/tree/4ea5a89012a5a8de999a0ada08970f6c4464e570

vbuch commented 2 years ago

Would be a good contribution if someone creates pdflibAddPlaceholder... The code is already in @RichardBray's repo. It will only need some testing.

RichardBray commented 2 years ago

Would be a good contribution if someone creates pdflibAddPlaceholder... The code is already in @RichardBray's repo. It will only need some testing.

I'm saving this one for Hacktoberfect 2022 😁 jk jk. I'll take a look at creating a PR with tests at some point in the future.

srxfalso commented 7 months ago

hi @vbuch My English is not very good, but I will try to explain my problem. I have been trying to sign a PDF document for a few days and I ran into this problem "SignPdfError: No ByteRangeStrings found within PDF buffer" and I have not been able to solve it. I leave you my code for your observation and I tried to add a marker for the signature but it hasn't worked, can you help or guide me.

`const fs = require('fs'); const { PDFDocument } = require('pdf-lib') const { sign } = require('node-signpdf').default; const forge = require('node-forge');

async function FirmaDigital() { // Carga el documento PDF que deseas firmar const pdfDocPath = 'pdf/original/1fd25424-6158-4df9-a253-971aef41340f.pdf'; const pdfBuffer = fs.readFileSync(pdfDocPath);

// Carga el certificado y la clave privada
const certPem = fs.readFileSync('cert/certificado.pem', 'utf8');
const keyPem = fs.readFileSync('cert/certificado.key', 'utf8');

// Convertir la clave PEM en formato PKCS12
const p12Asn1 = forge.pkcs12.toPkcs12Asn1(
    forge.pki.privateKeyFromPem(keyPem),
    forge.pki.certificateFromPem(certPem),
    null, // No password
    { generateLocalKeyId: true, algorithm: '3des' }
);
const p12Der = forge.asn1.toDer(p12Asn1).getBytes();
const p12Buffer = Buffer.from(p12Der, 'binary');

// Firmar el PDF
const signedPdf = sign(pdfBuffer, p12Buffer, {
    passphrase: '', // Si tu clave privada tiene una contraseña, ponla aquí
});

// agrega marcador de la firma.
// Añadir la firma al PDF

// const { ByteRange } = pdfDoc.catalog.get(PDFName.of('Root')).dict; // const firstSigOffset = ByteRange[1].toString(); // const firstSigLength = ByteRange[2].toString(); // const signatureDict = PDFDict.fromObject({ // Filter: 'Adobe.PPKLite', // SubFilter: new PDFName('adbe.pkcs7.detached'), // ByteRange: [0, firstSigOffset, firstSigLength, 0], // Contents: PDFHexString.fromText(signature), // Type: 'Sig', // });

//    pdfDoc.getPages()[0].node.addField('Signature', signatureDict);
// Guardar el PDF firmado
const signedPdfPath = 'documento_firmado.pdf';
fs.writeFileSync(signedPdfPath, signedPdf);

//console.log('PDF firmado con éxito');

} cert.zip `