Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement TimeStamping feature #617

Open
wants to merge 44 commits into
base: main
Choose a base branch
from
Open

Implement TimeStamping feature #617

wants to merge 44 commits into from

Conversation

hidasw
Copy link

@hidasw hidasw commented May 14, 2023

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
Copy link

CLAassistant commented May 14, 2023

CLA assistant check
All committers have signed the CLA.

tcpdf.php Outdated
@@ -7692,12 +7693,14 @@ public function Output($name='doc.pdf', $dest='I') {
$signature = $tmparr[1];
// decode signature
$signature = base64_decode(trim($signature));
// add TSA timestamp to signature
$signature = $this->applyTSA($signature);
// convert signature to hex
$signature = current(unpack('H*', $signature));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should before $this->applyTSA($signature)

@@ -1274,7 +1274,8 @@ class TCPDF {
* @protected
* @since 4.6.005 (2009-04-24)
*/
protected $signature_max_length = 11742;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i''m increasing this size to provide enough space to place tsa response data. and sometimes need more, depend on tsa response data size.

add Content-Type and User-Agent header for tsa request. Some server only accept application/timestamp-query
@@ -0,0 +1,49 @@
<?php
// create 11:16 AM 10/17/2011
function tsaLog($str, $type = 'i', $nl=true) { // 11:16 AM 10/17/2011
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this file and function should be removed from this pull-request

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it for debug only

tcpdf.php Outdated
Comment on lines 13681 to 13765
// start timestamping
// by Hida since 5.9.128 (2011-10-06)
if($this->tsa_timestamp) {
//Include asn1 fuction script
require_once(dirname(__FILE__).'/include/asn1_parser_tcpdf.php');
require_once(dirname(__FILE__).'/include/asn1_function_tcpdf.php');
require_once(dirname(__FILE__).'/include/functionLog_tcpdf.php');

$tsaLog = __FILE__." line:(".__LINE__."). Perform timestamping...\n";
//Parse TCPDF Signature structure to get signed hash sequence
$p = asn1parse($signature);
$p1 = asn1parse($p[0][1]);
$p2 = asn1parse($p1[1][1]);
$p3 = asn1parse($p2[0][1]);
$p2 = asn1parse($p3[4][1]);
$pa1 = asn1parse($p2[0][1]);
$pa2 = asn1parse($pa1[3][1]);

//Create timestamp request

//Create hash of encrypted contents TCPDF signature
$hash = hash('sha1', hex2bin($pa1[5][1]));
//Build timestamp request data
$tsReqData = seq(
int(1).
seq(
seq(
"06052B0E03021A". // Obj_sha1
"0500" // Null
).
oct($hash)
).
int(hash('crc32', rand())).
'0101ff'
);
$raw_data = hex2bin($tsReqData);

//Send request to TSA Server with Curl
if(extension_loaded('curl')) {
$tsaLog .= __FILE__." line:(".__LINE__."). Curl was already Loaded\n".__FILE__." line:(".__LINE__."). Curl is sending tsRequest to \"".$this->tsa_data['tsa_host']."\" ...\n";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->tsa_data['tsa_host']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/timestamp-query',
'User-Agent: TCPDF'
curl_setopt($ch, CURLOPT_POSTFIELDS, $raw_data);
$tsResponse = curl_exec($ch);
if($tsResponse != false) {
$tsaLog .= __FILE__." line:(".__LINE__."). tsRequest is sent.\n";
} else {
tsaLog("$tsaLog".__FILE__." line:(".__LINE__."). can't send tsRequest, Timestamp failed!\n",'w');
}
//parse ts response
$hexTs = bin2hex($tsResponse);
$tsparse = asn1parse($hexTs);

$tsparse0 = asn1parse($tsparse[0][1]);
if(count($tsparse0) > 1) { //Remove response status data, only take timeStampToken
$timeStamp = seq($tsparse0[1][1]);
} else {
$timeStamp = seq($tsparse0[0][1]);
}

//Add timestamp to TCPDF Signature
$timeStamp = seq("060B2A864886F70D010910020E".set($timeStamp));
$pkcs7 = int($pa1[0][1]).seq($pa1[1][1]).seq($pa1[2][1]).explicit(0, $pa1[3][1]).seq($pa1[4][1]).oct($pa1[5][1]);
$time = seq($pkcs7.explicit(1,$timeStamp));
$aa=seq(int(1). set($p3[1][1]).seq($p3[2][1]).explicit(0, $p3[3][1]).set($time));
$hdaSignature = seq("06092A864886F70D010702".explicit(0,($aa)))."0000";

$signature = $hdaSignature;
// $tsaLog .= $signature;
tsaLog("$tsaLog".__FILE__." line:(".__LINE__."). Timestamp Success.\n");
} else {
$tsaLog .= __FILE__." line:(".__LINE__."). Curl was not loaded, trying to load it...\n";
if(@dl('php_curl.dll')) {
$tsaLog .= __FILE__." line:(".__LINE__."). Curl successfully Loaded.\n";
} else {
tsaLog("$tsaLog\n".__FILE__." line:(".__LINE__."). Curl failed to load. Timestamping failed!", 'w');
}
}
}
// end timestamping
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you do some code cleanup and formatting please ?
It's really hard to read

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to rewrite with more readable soon..

tcpdf.php Outdated Show resolved Hide resolved
also merge asn.1 function to one file,
remove useless log function file.
tested working trusted timestamp http://timestamp.apple.com/ts01
tcpdf.php Outdated
Comment on lines 13683 to 13801

// Create timestamp request

// Create hash of encrypted contents TCPDF signature
// $this->setTimeStamp() have no options for change tsa req hash alg yet, so sha1 selected
$hash = hash('sha1', hex2bin($pkcs7EncryptedDigest));

// Build timestamp request data
$tsReqData = seq(
int(1).
seq(
seq(
"06052B0E03021A". // Obj_sha1
"0500" // Null
).
oct($hash)
).
int(hash('crc32', rand())). // Add random nonce request
'0101ff' // set certReq true to tell TSA server to include SigningCertificate
);

$raw_data = hex2bin($tsReqData);

//Send request to TSA Server with Curl
if(extension_loaded('curl')) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->tsa_data['tsa_host']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/timestamp-query',
'User-Agent: TCPDF'
)
);
curl_setopt($ch, CURLOPT_POSTFIELDS, $raw_data);


// can't send tsRequest, Timestamp failed!
if(!$tsResponse = curl_exec($ch)) {
return $signature;
}

// parse timestamp response data
$hexTsaResponse = bin2hex($tsResponse);
if(!$parseTimeStampResp = asn1parse($hexTsaResponse)) { // bad TSA Reponse
return $signature;
}

// verify tsa response PKIStatusInfo and TimeStampToken exists
if(!$TimeStampResp = asn1parse($parseTimeStampResp[0][1])) {
return $signature;
}

// Select timeStampToken only. must ignore response status data (in first sequence if exist, select 2nd sequence)
if(count($TimeStampResp) > 1) {
$TSTInfo = $TimeStampResp[1][1]; // TSTInfo
} else if (count($TimeStampResp) == 1) {
$TSTInfo = $TimeStampResp[0][1]; // TSTInfo
} else { // TimeStampResp not containts 1 or 2 fields
return $signature;
}

// Add timestamp in TCPDF Signature
// Create timestamp pkcs#7 data
$TimeStampToken = seq(
"060B2A864886F70D010910020E". // OBJ_id_smime_aa_timeStampToken
set(
seq(
$TSTInfo // TSTInfo
)
)
);

$time = seq(
$pkcs7signerInfos[0][1].
explicit(1,
$TimeStampToken
)
);

$pkcs7contentSignedData=seq(
int(1). // version
set($pkcs7SignedData[1][1]). // digestAlgorithms
seq($pkcs7SignedData[2][1]). // contentInfo
explicit(0,
$pkcs7SignedData[3][1]
). // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates
set(
$time
)
);
$pkcs7ContentInfo = seq(
"06092A864886F70D010702". // ContentType OBJ_pkcs7_signed
explicit(0,($pkcs7contentSignedData)) // content
).
// "0000"; // sometime needed for backward compatibility
"";

$signature = $pkcs7ContentInfo;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you indent this with spaces and move all this code into a protected function you would call here to fetch the signature ?
That would avoid adding that much code into this function

Also, I think you could keep the actual value of signature_max_length and only increase it (if the value < what you need) when calling the signature process ?
Would that keep other pdf documents the same as before (the ones without a signature)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think signature_max_length value have to be reserved before signing process, thats why it not set to 0. will padded by zero after signing. this value affect only signed file size to several kilobytes. Tried to lower this value around 14k and still done with apple tsa.
want move to object oriented but not at the moment. maybe to closed this pull request while i'll periodically update script.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not close this pull request
If you invite me on your repository I may clean up the implementation for you

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I start pushing work or do you have more to push first?

Copy link
Author

@hidasw hidasw May 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, up to you.
i'm currently improving asn.1 parser and have been stuck on recursion for last few days. cz i'm working alone.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait for awhile. i'm updating code to classes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, let me know

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have done, please review and make some corrections if needed.
I've minimized the script, considering most of the general purpose asn.1 encoders/decoders use complex libraries.

Comment on lines 2 to 11
// ASN.1 Parser start 21:31 Sore Kamis 26 Maret 2009
// ASN.1 Parser at 22:10 Sore Kamis 26 Maret 2009 Telah jadi utk standar asn.1
//
// 06:40 Esuk Jumat 27 Maret 2009 ASN.1 Parser kesulitan dlm memecahkan explicit > 9

// 11:18 Esuk Jumat 27 Maret 2009 parse explicit:xx mulai dipecahkan. kemungkinan tlh jadi
// 17:51 Sore Jumat 27 Maret 2009 memecahkan explicit sampai 2097151 (65536 * 32) kurang 1

// 20:04 Sore Jumat 27 Maret 2009 ASN.1 Parser tlh jadi. Congratulation....
// 12:15 Sore 16/05/2023
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all this file seems quite old, what is it source ?
what is the license of this code ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it useless. just progress mark. i'm forget to remove some comment line.

Comment on lines 86 to 110
function SEQ($hex) {
$ret = "30".asn1_header($hex).$hex;
return $ret;
}
function OCT($hex) {
$ret = "04".asn1_header($hex).$hex;
return $ret;
}
function INT($int) {
if(strlen($int)%2 != 0) {
$int = "0$int";
}
$int = "$int";
$ret = "02".asn1_header($int).$int;
return $ret;
}
function SET($hex) {
$ret = "31".asn1_header($hex).$hex;
return $ret;
}
//function EXPLICIT($num="0", $hex) {
function EXPLICIT($num, $hex) {
$ret = "a$num".asn1_header($hex).$hex;
return $ret;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should rename the function starting with asn1_ so it had less changes to collide with existing functions at the user level

Or prefix them with tcpdf_

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

altough renaming that, the procedural style function too exposed indeed, plan to built a class for that but dont know how to do it. cause its so impractice when repeatedly write "new" or "$this->".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use static

class Foo {
public static function doFoo() {}
}

Foo::doFoo();

Should be way easier

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, but I need to simplify and compacting the script first.

migrate procedural to class
@@ -13642,6 +13644,8 @@ protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1,
* @author Richard Stockinger
* @since 6.0.090 (2014-06-16)
*/
// other options suggested to be implement: reqPolicy, nonce, certReq, extensions
// and option to abort signing if timestamping failed and LTV enable (embed crl and or ocsp revocation info)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolaasuni what minimal set of changes would be accepted ?

Some of it could go into a new library ?
Would everything be accepted ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/dealfonso/sapp/blob/main/pdfsign.php

but it for signing, which already implemented here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williamdes Wouldn't be possible to use the default openssl_* PHP functions (as in the current version) instead or re-implementing the cryptography function from scratch?
I will look into port this functionality into tc-lib-pdf but I am not keen on rewriting crypto libs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, @hidasw what do you think ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We using openssl for cryptographic functions (encrypt/decrypt). For ocsp/ts/crl operations, php openssl does not have those functions natively, even for asn.1 parsing. So it requires its own independent function or using a library like phpseclib.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you at least copy the functions from phpseclib so we can know they are safely implemented ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williamdes
Unfortunately in phpseclib there is no function for ocsp, ts and crl. There is asn.1 function but it looks too complicated with namespace and many files included. You can compare with my script.
If you are looking for a library for ocsp, crl and ts functions, it is definitely related to the asn.1 function contained in the library. It will be difficult to separate them.

tcpdf.php Outdated
Comment on lines 13677 to 13685

/**
* Applying TSA for a timestamp.
* @param string $signature Digital signature as hex string
* @return hex string Timestamped digital signature
* @protected
* @author M Hida
* @since 6.6.2 (2023-05-25)
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This documentation is misleading since the function already existed in prior versions
could you update this to reflect that it already existed ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably remove the previous comment, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, that's better

williamdes
williamdes previously approved these changes Jun 2, 2023
Copy link
Contributor

@williamdes williamdes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice !
When I find time I will do some polishing of the code indentation

@evamtinez
Copy link

evamtinez commented Jun 12, 2024

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
Copy link
Author

hidasw commented Jun 12, 2024

@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
Copy link

@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
Copy link
Author

hidasw commented Jun 15, 2024

@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.

$time = $params[0]; //yymmddhhiiss
$oldTz = date_default_timezone_get();
date_default_timezone_set("UTC");
$time = date("ymdHis", $time);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @hidasw ,

The signature time is not being created properly because in this line you are passing a date in the format 'ymdHis' as the second parameter of the date() function instead of a timestamp.

I found this bug while working on a project that uses PHP 7.2.

Feel free to ask if you need any further assistance!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plan to use strtotime or date class, but need to manage while converting 4 digits year to 2 digits. if year is 70, some app treat it as 1970 and other as 2070. also for time zone difference.

@rsvitak
Copy link

rsvitak commented Jul 25, 2024

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
Copy link
Author

hidasw commented Jul 27, 2024

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
Copy link

rsvitak commented Aug 6, 2024

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
Copy link

rsvitak commented Aug 8, 2024

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
Copy link

rsvitak commented Aug 28, 2024

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
Copy link

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
Copy link

rsvitak commented Sep 11, 2024

@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
Copy link
Author

hidasw commented Sep 11, 2024

@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
Copy link

@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
Copy link
Author

hidasw commented Sep 11, 2024

@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
Copy link

rsvitak commented Sep 11, 2024

I have stored my modifications to https://github.com/rsvitak/TCPDF to avoid missunderstandings with description of my modifications in my post #617 (comment).

@KruzstofWren
Copy link

@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
Copy link

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
Copy link

rsvitak commented Sep 13, 2024

@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
Copy link

KruzstofWren commented Sep 13, 2024

@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

@nicolaasuni
Copy link
Member

Hi,
A lot of good work has been put into this feature that I will consider for the new tc-lib-pdf library instead.
I am trying to not accept any new feature for TCPDF so I can deprecate it sooner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants