Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
28c747b
Update tcpdf.php
hidasw May 14, 2023
ff9e5a8
Create asn1_function_tcpdf.php
hidasw May 14, 2023
ff20f9f
Create asn1_parser_tcpdf.php
hidasw May 14, 2023
e998624
Create functionLog_tcpdf.php
hidasw May 14, 2023
ffce574
Create Local Root CA.crt
hidasw May 14, 2023
15bf870
Create tcpdf test.pem
hidasw May 14, 2023
a295943
Create index.php
hidasw May 14, 2023
abfaeda
Update tcpdf.php
hidasw May 15, 2023
01fb957
Update index.php
hidasw May 15, 2023
1656d3c
reformatting and cleanup
hidasw May 16, 2023
80f70d3
Update tcpdf_asn1.min.php
hidasw May 17, 2023
3ab497a
Update tcpdf.php
hidasw May 17, 2023
c8e4c7b
major update
hidasw May 25, 2023
c16b58c
Update tcpdf.php
hidasw Jun 4, 2023
f1de348
Update include/tcpdf_cmssignature.php
hidasw Dec 22, 2023
1499b2b
Remove type on properties.
evamtinez Apr 16, 2024
df74f95
Curl close was moved after curl_getinfo.
evamtinez Apr 16, 2024
4a384cb
Fix indentation
evamtinez Apr 16, 2024
fe6c482
Merge pull request #2 from evamtinez/main
hidasw Apr 18, 2024
3a741b6
Add TSA parameter
hidasw Apr 19, 2024
fe247a1
update to support LTV and TSA at once
hidasw Apr 22, 2024
050eec1
Merge branch 'main' into main
hidasw Apr 22, 2024
92f3539
significant improvement on signing process
hidasw Apr 24, 2024
3ecc5ab
Merge branch 'main' of https://github.com/hidasw/TCPDF
hidasw Apr 24, 2024
959052d
combines several functions
hidasw Apr 24, 2024
9950510
Update example_052.php
hidasw Apr 24, 2024
e1297c2
Update tcpdf.php
hidasw May 14, 2024
a7b5ebc
Update tcpdf_cmssignature.php
hidasw May 14, 2024
46ecd34
Update example_052.php
hidasw May 14, 2024
6ab1ed7
Delete RootCATest.pem.crl
hidasw May 14, 2024
f166179
Delete RootCATest.der.crl
hidasw May 14, 2024
f7dc852
Delete Root CA Test.crt
hidasw May 14, 2024
7a766b6
Delete Root CA Test OCSP Signer.pem
hidasw May 14, 2024
2543e57
Delete PDF User.pem
hidasw May 14, 2024
0913f4f
Create PDF Signing CA.crt
hidasw May 14, 2024
58228d7
Create ocspTest.bat
hidasw May 14, 2024
5ea1de8
Create longChain.pfx
hidasw May 14, 2024
d2f6c24
Create longChain.pem
hidasw May 14, 2024
ca7a5e3
Create long.pfx
hidasw May 14, 2024
67ce271
Create b6ce4782.0
hidasw May 14, 2024
42beeea
Create 7262ea48.0
hidasw May 14, 2024
0884ea0
Create 71ab782e.0
hidasw May 14, 2024
5d1c8ec
Create 5a0ce691.0
hidasw May 14, 2024
cbb1aac
Create 26a91b3f.0
hidasw May 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions include/tcpdf_asn1.min.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php
/**
* @file
* This is a PHP function for parsing and creating ASN.1 syntax for handle minimum timeStamp request and response.
* @author Hida
* @version 1.0.1
* Last Update : 17/05/2023
*/
function asn1_first($hex) {
$asn1_Id = substr($hex, 0, 2);
$header = substr($hex, 2, 2);
if($asn1_Id == 'bf') {
if(hexdec($header) > 128) {
$headerLength = hexdec(substr($hex, 6, 2));
$reduced = 8; // the string reduced by id & headerLength
$expNum = (128*(hexdec($header)-128))+hexdec(substr($hex, 4, 2));
$header2 = substr($hex, 4, 2);
if(hexdec($header2) >= 128) {
$headerLength = hexdec(substr($hex, 8, 2));
$reduced = 10;
$expNum = (16384*(hexdec($header)-128))+(128*(hexdec($header2)-128))+hexdec(substr($hex, 6, 2));
}
} else {
$headerLength = hexdec(substr($hex, 4, 2));
$reduced = 6;
$expNum = hexdec(substr($hex, 2, 2));
}
$asn1_Id = "EXP:"."$expNum";
} else {
//echo "$header==";
if($header == '83') {
$headerLength = hexdec(substr($hex, 4, 6));
$reduced = 10;
} elseif ($header == '82') {
$headerLength = hexdec(substr($hex, 4, 4));
$reduced = 8;
} elseif ($header == '81') {
$headerLength = hexdec(substr($hex, 4, 2));
$reduced = 6;
} else {
$l=0;
$l = hexdec(substr($hex, 2, 2));
$headerLength = $l;
$reduced = 4;
//echo "$headerLength --".substr($hex, 2, 2)."--<br>";

}
}
$str_remains = substr($hex, $reduced+($headerLength*2));
$content = substr($hex, $reduced, $headerLength*2);
$return['res'] = array($asn1_Id, $content); // array 0=>iD(sequence be 30, integer be 02, etc) 1=>contents of id
$return['rem'] = $str_remains; // the remain string returned
if($str_remains == '' && $content == '') { // if remains string was empty & contents also empty, function return FALSE
$return = false;
}
return $return;
}

function asn1parse($hex) {
//$return =false;
while(asn1_first($hex) != false) { // while asn1_first() still return string
$r = asn1_first($hex);
$return[] = array($r['res'][0],$r['res'][1]);
$hex = $r['rem']; // $hex now be result of asn1_first()
}
if(!is_array(@$return)) {
return false;
}
return $return;
}

function asn1_header($str) {
$len = strlen($str)/2;
$ret = dechex($len);
if(strlen($ret)%2 != 0) {
$ret = "0$ret";
}

$headerLength = strlen($ret)/2;
if($len > 127) {
$ret = "8".$headerLength.$ret;
}
return $ret;
}

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

?>
55 changes: 51 additions & 4 deletions tcpdf.php
Original file line number Diff line number Diff line change
Expand Up @@ -1274,7 +1274,7 @@ class TCPDF {
* @protected
* @since 4.6.005 (2009-04-24)
*/
protected $signature_max_length = 11742;
Copy link
Copy Markdown
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.

protected $signature_max_length = 16742;

/**
* Data for digital signature appearance.
Expand Down Expand Up @@ -7692,12 +7692,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
Copy Markdown
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)

// add TSA timestamp to signature
$signature = $this->applyTSA($signature);

$signature = str_pad($signature, $this->signature_max_length, '0');
// Add signature to the document

$this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
$this->bufferlen = strlen($this->buffer);
}
Expand Down Expand Up @@ -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
Copy Markdown
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 ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
$this->tsa_data = array();
if (!function_exists('curl_init')) {
Expand Down Expand Up @@ -13674,7 +13678,50 @@ protected function applyTSA($signature) {
if (!$this->tsa_timestamp) {
return $signature;
}
//@TODO: implement this feature
// * @author Hida
require_once(dirname(__FILE__).'/include/tcpdf_asn1.min.php');
// Parse TCPDF's pkcs#7 Signature structure to get sequence of signed hash
$pkcs7 = asn1parse($signature);
$pkcs7ContentInfo = asn1parse($pkcs7[0][1]);
$pkcs7content = asn1parse($pkcs7ContentInfo[1][1]);
$pkcs7SignedData = asn1parse($pkcs7content[0][1]);
$pkcs7signerInfos = asn1parse($pkcs7SignedData[4][1]);
$SignerInfo = asn1parse($pkcs7signerInfos[0][1]);
$pkcs7EncryptedDigest = $SignerInfo[5][1];

// Create timestamp request
$hash = hash('sha1', hex2bin($pkcs7EncryptedDigest));
$tsReqData = seq(int(1).seq(seq("06052B0E03021A"."0500").oct($hash)).int(hash('crc32', rand())).'0101ff');
$binarytsReqData = hex2bin($tsReqData);

//Send request to TSA Server
$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, $binarytsReqData);
if(!$tsResponse = curl_exec($ch)) { return $signature; } // can't send tsRequest, Timestamp failed!
$hexTsaResponse = bin2hex($tsResponse); // parse timestamp response data
if(!$parseTimeStampResp = asn1parse($hexTsaResponse)) { return $signature; } // bad TSA Reponse
if(!$TimeStampResp = asn1parse($parseTimeStampResp[0][1])) { return $signature; } // verify tsa response PKIStatusInfo and TimeStampToken exists

// 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;
}

// Create timestamp pkcs#7 data
$TimeStampToken = seq("060B2A864886F70D010910020E".set(seq($TSTInfo)));
$time = seq($pkcs7signerInfos[0][1].explicit(1,$TimeStampToken));
$pkcs7contentSignedData=seq(int(1).set($pkcs7SignedData[1][1]).seq($pkcs7SignedData[2][1]).explicit(0,$pkcs7SignedData[3][1]).set($time));
$pkcs7ContentInfo = seq("06092A864886F70D010702".explicit(0,($pkcs7contentSignedData)));

$signature = $pkcs7ContentInfo;
return $signature;
}

Expand Down