tecnickcom / TCPDF

Official clone of PHP library to generate PDF documents and barcodes
https://tcpdf.org
Other
4.18k stars 1.51k forks source link

Implement TimeStamping feature #617

Open hidasw opened 1 year ago

hidasw commented 1 year ago

Also add some file for parsing asn.1 data, and logging (debug only). should merged with main tcpdf.php but its not meet oop standar yet. still use procedural style.

CLAassistant commented 1 year ago

CLA assistant check
All committers have signed the CLA.

hidasw commented 10 months ago

@williamdes i think this will be merged. :)

williamdes commented 9 months ago

@williamdes i think this will be merged. :)

did you get some news ?

hidasw commented 9 months ago

@williamdes i think this will be merged. :)

did you get some news ?

No, what news?

williamdes commented 9 months ago

No, what news?

None. So we still have to wait for more review by users that can merge this PR

csalzano commented 6 months ago

Hello. I need this feature. @hidasw are you willing to update your fork until this gets merged so my project can use the fork as a dependency?

hidasw commented 6 months ago

@csalzano There are no updates planned at this time. Still waiting for everyone's suggestions regarding what updates and features need to be made. Currently it works quite well with existing tcpdf signatures.

evamtinez commented 5 months ago

Hi @hidasw

I would like to use this functionallity in my project and I identified a few lines that require adjustments.

I've just sent you some feedback on your code, but I'm unsure of the next steps. Should I perhaps suggest the changes via a pull request to your fork?

Thank you for your efforts.

hidasw commented 5 months ago

@evamtinez of course you are welcome

evamtinez commented 5 months ago

@evamtinez of course you are welcome

I created a PR in your fork https://github.com/hidasw/TCPDF/pull/2

Feel free to give feedback or to make changes @hidasw 🙂

hidasw commented 5 months ago

Hello. I need this feature. @hidasw are you willing to update your fork until this gets merged so my project can use the fork as a dependency?

see example_052.php on my fork

hidasw commented 5 months ago

The latest update has support for LTV.

evamtinez commented 3 months ago

Hi @hidasw,

I have noticed that the code does not validate the TSA URL at any point, and passing an invalid URL does not display any error. It simply does not apply the timestamp to the signature. I wanted to ask if this is a functionality planned to be added to the pull request.

Thanks

hidasw commented 3 months ago

@evamtinez Last time I checked the script was running fine on php 7.4. I haven't had time to test thoroughly on php 8 or php 5.4. What version of PHP are you currently using? For the display error problem, I can't display it in the output buffer because it will go into the PDF file (currently I haven't used the error handler in TCPDF). To view the log, it is in the '../signature.log' file in the TCPDF directory. Can be set in the tcpdf_cmssignature class by setting $writeLog which is true by default. To really make sure that there are no unhandled error notifications in the PDF file, please open the PDF file in a text editor and make sure there are no PHP error notifications in the PDF file.

evamtinez commented 3 months ago

@evamtinez Last time I checked the script was running fine on php 7.4. I haven't had time to test thoroughly on php 8 or php 5.4. What version of PHP are you currently using? For the display error problem, I can't display it in the output buffer because it will go into the PDF file (currently I haven't used the error handler in TCPDF). To view the log, it is in the '../signature.log' file in the TCPDF directory. Can be set in the tcpdf_cmssignature class by setting $writeLog which is true by default. To really make sure that there are no unhandled error notifications in the PDF file, please open the PDF file in a text editor and make sure there are no PHP error notifications in the PDF file.

I'm using PHP 7.2.

I can see the log file correctly, but it would be helpful to get an error if the timestamp is invalid. For instance, if I set a timestamp to something like tsa.example.org (which doesn’t exist), the document appears to be signed correctly, and the process returns OK, but the PDF file doesn’t have the timestamp.

I’m handling this error in the class I'm using in my project, but I'm unsure if it needs to be directly integrated into the TCPDF library. In my implementation, before setting the timestamp, I call a validation function to check if the TSA is valid. However, this validation could be directly implemented in the setTimestamp function. What do you think?

Thanks

hidasw commented 3 months ago

@evamtinez Generally tsa is optional, so it's probably better to ignore it if it fails than to abort the entire signature.

I think so. Since first PR, I thought about providing an option to cancel the process if timestamp or LTV validation fails. But maybe this does not cancel the signature field/signature appearance that has been called, so it will provide null data in the signature which makes the signature invalid. I haven't tried it yet, maybe someday.

As a note, for the current version, if the validation process fails on one of certificate chains, then this certificate chain up to the root CA is not embedded in the pdf. (to be fixed in the next update). Thank's for suggestion.

rsvitak commented 2 months ago

I am testing this fork and after I added the curl_setopt($ch, CURLOPT_USERPWD, 'username:password'); in the sendReq function (tcpdf_cmssignature.php) it successfuly requested the specified TSA, but I don't think the TSR was added correctly (I am testing the final pdf file with pdfsig utility though).

However, I would like to ask you if you have idea how to add ONLY the the timestamp signature, not the ID signiture /certificate?

hidasw commented 2 months ago

whats the mean "but I don't think the TSR was added correctly", is there a problem with tsresponse? About timestamp only, I see that Adobe reader can do it, but I have never tried with php script. I also don't understand what the timestamp is for without a digital signature.

rsvitak commented 1 month ago

I am sorry for the late reply, I am struggling with this topic. Everything about PDF format and cryptography is new to me. What I am looking for perfectly demonstrates this: https://stackoverflow.com/questions/65807104/can-you-add-a-timestamped-no-tamper-proof-to-a-pdf-without-signing-it

My situation is: I have a working TSA, I am able to create a valid TSQ of a whole PDF file, send it to the TSA and I receive the valid TSR. As I am generating my PDF files in PHP (currently with DomPdf, but I believe I can switch to TCPDF) I am trying to get to the state the TSR is embedded into the PDF.

Currently I am able to embed the timestamp into the signiture, but as I have only a self generated certificate the acrobat says the validity of the signiture is unknown.

signinfo

Therefore my question is how to embed ONLY the RFC3161, i.e. /Type /DocTimeStamp object into the PDF.

I do appologize for chaotic and unprecise expressions but hopefully it's understandable what I mean.

rsvitak commented 1 month ago

I was able to get access to an Adobe Acrobat, configure time stamp authority and sign a PDF document only with the DTS. Here is a screenshot how it's then displayed in Acrobat. This is what I need to achieve with TCPDF: dts

rsvitak commented 1 month ago

I was finally ably to achieve the same result as when adding the TSA timestamp to the document by modifying (locally) this fork.

I made a dirty solution - added support to "special" signiture certificate (signature_data['signcert']) "DTS-ONLY" which turns the signing procedure to adding just the TSR response into the /Type /DocTimeStamp PDF section.

I modified these files: tcpdf.php:

7660c7660
<               if ($this->sign) {
---
>               if ($this->sign || !empty($this->signature_data_tsa)) {
13415a13416,13423
>               if ($this->signature_data['signcert']==='DTS-ONLY') {
>                       $out .= '<<'."\n".'/Filter /Adobe.PPKLite'."\n";
>                       $out .= '/SubFilter /ETSI.RFC3161'."\n";
>                       $out .= TCPDF_STATIC::$byterange_string."\n";
>                       $out .= '/Contents<'.str_repeat('0', $this->signature_max_length).'>'."\n";
>                       $out .= '/Type /DocTimeStamp'."\n";
>                       $out .= '/V 0'."\n";
>               } else {
13475a13484
>       }

include/tcpdf_cmssignature.php:

77a78
>     curl_setopt($ch, CURLOPT_USERPWD, $this->signature_data_tsa['username'].':'.$this->signature_data_tsa['password']);
425a429,430
> 
>     if ($this->signature_data['signcert']!=='DTS-ONLY') {
580a586,596
>    
>     } else {
>       $this->log .= ("info: DTS-ONLY starts ...\n");
>       if ($TSTInfo=self::createTimestamp($binaryData, $hashAlgorithm)) {
>          $this->log .= ("info: DTS-ONLY SUCCESS.\n");
>       } else {
>          $this->log .= ("info: DTS-ONLY FAILED!\n");
>       }
>       $pkcs7ContentInfo=$TSTInfo;
>     }
> 

I am signing my documents with this simple code now:

$pdf=new TCPDF();
$pdf->setSignature('DTS-ONLY', file_get_contents('postsignum_tsa_tsu1.pem'), '', '', 1, []);
$pdf->setTimeStamp(TSA_URL, TSA_USERNAME, TSA_PASSWORD);
$pdf->loadHtml($html);
$pdf->setSignatureAppearance(20,10,50,15);
$output=$pdf->Output($filename,'S');

I get results like this which is exactly what I wanted to achieve: obrazek

KruzstofWren commented 2 weeks ago

I was finally ably to achieve the same result as when adding the TSA timestamp to the document by modifying (locally) this fork.

I made a dirty solution - added support to "special" signiture certificate (signature_data['signcert']) "DTS-ONLY" which turns the signing procedure to adding just the TSR response into the /Type /DocTimeStamp PDF section.

I modified these files: tcpdf.php:

7660c7660
<               if ($this->sign) {
---
>               if ($this->sign || !empty($this->signature_data_tsa)) {
13415a13416,13423
>               if ($this->signature_data['signcert']==='DTS-ONLY') {
>                       $out .= '<<'."\n".'/Filter /Adobe.PPKLite'."\n";
>                       $out .= '/SubFilter /ETSI.RFC3161'."\n";
>                       $out .= TCPDF_STATIC::$byterange_string."\n";
>                       $out .= '/Contents<'.str_repeat('0', $this->signature_max_length).'>'."\n";
>                       $out .= '/Type /DocTimeStamp'."\n";
>                       $out .= '/V 0'."\n";
>               } else {
13475a13484
>       }

include/tcpdf_cmssignature.php:

425a429,430
> 
>     if ($this->signature_data['signcert']!=='DTS-ONLY') {
580a586,596
>    
>     } else {
>       $this->log .= ("info: DTS-ONLY starts ...\n");
>       if ($TSTInfo=self::createTimestamp($binaryData, $hashAlgorithm)) {
>          $this->log .= ("info: DTS-ONLY SUCCESS.\n");
>       } else {
>          $this->log .= ("info: DTS-ONLY FAILED!\n");
>       }
>       $pkcs7ContentInfo=$TSTInfo;
>     }
> 

I am signing my documents with this simple code now:

$pdf=new TCPDF();
$pdf->setSignature('DTS-ONLY', file_get_contents('postsignum_tsa_tsu1.pem'), '', '', 1, []);
$pdf->setTimeStamp(TSA_URL, TSA_USERNAME, TSA_PASSWORD);
$pdf->loadHtml($html);
$pdf->setSignatureAppearance(20,10,50,15);
$output=$pdf->Output($filename,'S');

I get results like this which is exactly what I wanted to achieve: obrazek

Please, can I ask @rsvitak i see you use postsignum TSA. Please can you help me with URL to create on this. I cant go throw autenticatin process. Many thx... $pdf->setTimeStamp('https://tsa.postsignum.cz:444/TSS/HttpTspServer/', TSA_USERNAME, TSA_PASSWORD); ?? Many thx

rsvitak commented 2 weeks ago

@KruzstofWren I had to update the list of changes I made to the @hidasw code in my comment above, thank you for tour post. This is neccessary to add to the sendReq function in the include/tcpdf_cmssignature.pdf to make the code send the autentication data to the server: curl_setopt($ch, CURLOPT_USERPWD, $this->signature_data_tsa['username'].':'.$this->signature_data_tsa['password']);

hidasw commented 2 weeks ago

@rsvitak Good news, you can add support for timestamp only. I don't know much about pdf structure. Maybe in the future it would be nice to add an option for timestamp only.

KruzstofWren commented 2 weeks ago

@KruzstofWren I had to update the list of changes I made to the @hidasw code in my comment above, thank you for tour post. This is neccessary to add to the sendReq function in the include/tcpdf_cmssignature.pdf to make the code send the autentication data to the server: curl_setopt($ch, CURLOPT_USERPWD, $this->signature_data_tsa['username'].':'.$this->signature_data_tsa['password']);

Many thx for reaction. But still, in logs i have this fail...

info : Timestamping process start...info: sending TSA query to "https://tsa.postsignum.cz:444/TSS/HttpTspServer/"... info : TSA query OK info : Parsing Timestamp response...FAILED!

Something more i need edit?

hidasw commented 2 weeks ago

@KruzstofWren I had to update the list of changes I made to the @hidasw code in my comment above, thank you for tour post. This is neccessary to add to the sendReq function in the include/tcpdf_cmssignature.pdf to make the code send the autentication data to the server: curl_setopt($ch, CURLOPT_USERPWD, $this->signature_data_tsa['username'].':'.$this->signature_data_tsa['password']);

Many thx for reaction. But still, in logs i have this fail...

info : Timestamping process start...info: sending TSA query to "https://tsa.postsignum.cz:444/TSS/HttpTspServer/"... info : TSA query OK info : Parsing Timestamp response...FAILED!

Something more i need edit?

I can't test it since tsa uses authentication. But I can analyze it if I get the tsa response. You can save the response to a file. In the sendReq() function before return:

  $h = fopen('tsaResp.der','w');
  fwrite($h, $body);
  fclose($h);

parse the response with openssl: openssl asn1parse -i -inform der -in tsaResp.der

Let me know whats wrong.

rsvitak commented 2 weeks ago

I have stored my modifications to https://github.com/rsvitak/TCPDF to avoid missunderstandings with description of my modifications in my post https://github.com/tecnickcom/TCPDF/pull/617#issuecomment-2315346099.

KruzstofWren commented 2 weeks ago

@KruzstofWren I had to update the list of changes I made to the @hidasw code in my comment above, thank you for tour post. This is neccessary to add to the sendReq function in the include/tcpdf_cmssignature.pdf to make the code send the autentication data to the server: curl_setopt($ch, CURLOPT_USERPWD, $this->signature_data_tsa['username'].':'.$this->signature_data_tsa['password']);

Many thx for reaction. But still, in logs i have this fail... info : Timestamping process start...info: sending TSA query to "https://tsa.postsignum.cz:444/TSS/HttpTspServer/"... info : TSA query OK info : Parsing Timestamp response...FAILED! Something more i need edit?

I can't test it since tsa uses authentication. But I can analyze it if I get the tsa response. You can save the response to a file. In the sendReq() function before return:

  $h = fopen('tsaResp.der','w');
  fwrite($h, $body);
  fclose($h);

parse the response with openssl: openssl asn1parse -i -inform der -in tsaResp.der

Let me know whats wrong.

All is working great now. Only mistake is on my side....right URL for https://www.postsignum.cz is: https://www3.postsignum.cz/TSS/TSS_user/ Many thx to all, your are the best!

KruzstofWren commented 2 weeks ago

If i may, last question. Do you think is much diference bettwen SIGN or CERTIFICATE document? Images are down ... I dont see any diference. Both document have timestamp...

obrazek

obrazek

rsvitak commented 2 weeks ago

@KruzstofWren I am not very sure in this topic, but the difference could be in the authority that proves the "stamp"? Maybe if you could paste the "TCPDF" commands used to create the documents, or if you can provide the final PDF files, it would be easier to tell more...

KruzstofWren commented 2 weeks ago

@KruzstofWren I am not very sure in this topic, but the difference could be in the authority that proves the "stamp"? Maybe if you could paste the "TCPDF" commands used to create the documents, or if you can provide the final PDF files, it would be easier to tell more...

This two type of commands

$pdf->setTimeStamp('https://www3.postsignum.cz/TSS/TSS_user/', USER, PASS); VS $pdf->setTimeStamp('DTS-ONLY', USER, PASS);

And final PDF are diferent with show type of certification.... SIGN or CERTIFICATE in pannel of certification in Adobe Reader

obrazek VS obrazek