fpdf官网:http://www.fpdf.org/?lang=zh
tcpdf官网:https://tcpdf.org/
1、对证书签名与验签逻辑优化,在linux系统中生成根证书CA,用CA为子证书签名,最后使用子证书为pdf做签名,防止pdf伪造自签证书。
1 2 3 4 5 6 7 8 | #1、生成根证书 #a).生成根证书私钥(key文件) openssl genrsa -aes256 -out ca.key 2048 #b).生成根证书签发申请文件(csr文件) openssl req -new -key ca.key -out ca.csr -config /usr/lib/ssl/openssl.cnf #c).自签发根证书(crt文件) openssl x509 -req -days 3650 -sha1 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.crt |
1 2 3 4 5 6 7 8 9 10 11 | #2、用根证书签发server端证书 #a).生成根证书私钥(key文件) openssl genrsa -aes256 -out tcpdf.key 2048 #b).生成根证书签发申请文件(csr文件) openssl req -new -key tcpdf.key -out tcpdf.csr -config /usr/lib/ssl/openssl.cnf #c).使用根证书签发服务端证书 openssl ca -in tcpdf.csr -out tcpdf.crt -days 730 -cert ca.crt -keyfile ca.key -config /usr/lib/ssl/openssl.cnf #The organizationName field needed to be the same in the CA certificate (Timeswealth Global Root CA) |
若发生错误: I am unable to access the ./demoCA/newcerts directory ./demoCA/newcerts: No such file or directory
做如下处理
1 2 3 4 5 | mkdir demoCA mkdir demoCA/newcerts mkdir demoCA/private touch demoCA/index.txt echo "01" >> demoCA/serial |
备注:一下是tcpdf官方提供的生成自签名证书方法,测试用,如果用于实际会有逻辑漏洞,没有根证书约束。
1 2 3 4 5 6 7 8 9 10 11 | /* NOTES: - To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt - To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 - To export p12 to pfx: openssl pkcs12 -export -inkey tcpdf.crt -in tcpdf.crt -out tcpdf.pfx - To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes */ |
自己封装的tcpdf操作类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | <pre><?php require_once __DIR__."/tcpdf/tcpdf.php"; class TcpdfProxy { //tcpdf对象 private $pdf = null; //证书路径及密码 private $_certificate = array( 'signing_cert' => '', 'private_key' => '', 'private_key_password' => 'tcpdfpass', ); private $_signConf = array( 'image' => '', 'image_position' => array( 'x' => 80, 'y' => 25, 'w' => 50, 'h' => 50, ), 'sign_info' => array( 'Name' => 'tcpdf', 'Location' => 'location', 'Reason' => 'Digital signature of PDF file', //'ContactInfo' => SITE_DOMAIN, ), ); /*********** 自签名证书配置 ***************/ private $prikeyPass = '123456789'; private $numberOfDays = '730'; private $cerPath = ""; private $cerConfig = array( "digest_alg" => "sha256", "private_key_bits" => 1024, //字节数 512 1024 2048 4096 等 "private_key_type" => OPENSSL_KEYTYPE_RSA, //加密类型 //"config" => "/etc/ssl/openssl.cnf" ); private $dn = array( "countryName" => "CN", //所在国家 "stateOrProvinceName" => "LiaoNing", //所在省份 "localityName" => "FuShun", //所在城市 "organizationName" => "Richter59 Global Root CA", //注册人姓名 "organizationalUnitName" => "www.richter59.cn", //组织名称 "commonName" => "www.richter59.cn", //公共名称 "emailAddress" => "postmaster@richter59.cn" //邮箱 ); /************ 自签名证书配置END **************/ public function __construct() { $this->pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); $this->setConfig(); $this->cerPath = $_SERVER["DOCUMENT_ROOT"]."/public/"; $this->_certificate['signing_cert'] = 'file://'.$this->cerPath.'tcpdf.crt'; $this->_certificate['private_key'] = 'file://'.$this->cerPath.'tcpdf.crt'; } private function setConfig() { //页面头部横线取消 $this->pdf->setPrintHeader(false); //页面底部更显取消 //$pdf->setPrintFooter(false); //自动分页 $this->pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); //设置页面margin $this->pdf->SetMargins(PDF_MARGIN_LEFT, 15, PDF_MARGIN_RIGHT); //设置页码 $this->pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); $this->pdf->SetFooterMargin(PDF_MARGIN_FOOTER); //设置字体,注意在循环里面一定要把new都一起放在循环里面,不然会报错,没有设置字体,因为这个需要上下文来读取配置 $this->pdf->SetFont('stsongstdlight', '', 12); } public function setSignConf(array $conf){ $this->_signConf['image'] = $conf['image'] ?: ''; isset($conf['image_position']['x']) ? $this->_signConf['image_position']['x'] = $conf['image_position']['x'] : ''; isset($conf['image_position']['y']) ? $this->_signConf['image_position']['y'] = $conf['image_position']['y'] : ''; isset($conf['image_position']['w']) ? $this->_signConf['image_position']['w'] = $conf['image_position']['w'] : ''; isset($conf['image_position']['h']) ? $this->_signConf['image_position']['h'] = $conf['image_position']['h'] : ''; if(isset($conf['Name']) && !empty($conf['Name'])){ $this->_signConf['sign_info']['Name'] = $conf['sign_info']['Name']; } if(isset($conf['Location']) && !empty($conf['Location'])){ $this->_signConf['sign_info']['Location'] = $conf['sign_info']['Location']; } if(isset($conf['Reason']) && !empty($conf['Reason'])){ $this->_signConf['sign_info']['Reason'] = $conf['sign_info']['Reason']; } if(isset($conf['ContactInfo']) && !empty($conf['ContactInfo'])){ $this->_signConf['ContactInfo']['Name'] = $conf['sign_info']['ContactInfo']; } } private function signatureWithPdf($img,array $signInfo){ // set certificate file if($this->_certificate){ $certificate = $this->_certificate; }else{ $certificate = $this->getCrtFile(); } if($certificate){ // set additional information if(!empty($signInfo)){ isset($signInfo['Name']) && !empty($signInfo['Name']) ? $this->sign_info['Name']=$signInfo['Name'] : ""; isset($signInfo['Location']) && !empty($signInfo['Location']) ? $this->sign_info['Location']=$signInfo['Location'] : ""; isset($signInfo['Reason']) && !empty($signInfo['Reason']) ? $this->sign_info['Reason']=$signInfo['Reason'] : ""; isset($signInfo['ContactInfo']) && !empty($signInfo['ContactInfo']) ? $this->sign_info['ContactInfo']=$signInfo['ContactInfo'] : ""; } // set document signature $this->pdf->setSignature($certificate['signing_cert'], $certificate['private_key'], $certificate['private_key_password'], '', 1, $this->sign_info); // reset pointer to the last page //$pdf->lastPage(); $this->pdf->setPage(1); // *** set signature appearance *** $x = $this->_signConf['image_position']['x']; $y = $this->_signConf['image_position']['y']; $w = $this->_signConf['image_position']['w']; $h = $this->_signConf['image_position']['h']; // create content for signature (image and/or text) $this->pdf->Image($img, $x, $y, $w, $h); // define active area for signature appearance $this->pdf->setSignatureAppearance($x, $y, $w, $h); // *** set an empty signature appearance *** //$this->pdf->addEmptySignatureAppearance(180, 80, 15, 15); } } public function outputPdf($html,$fileName) { $this->pdf->AddPage(); // output the HTML content $this->pdf->writeHTML($html, true, false, true, false, ''); if($this->_signConf['image'] != '' && file_exists($this->_signConf['image'])){ $this->signatureWithPdf($this->_signConf['image'],$this->_signConf['sign_info']); } //Close and output PDF document $this->pdf->Output($fileName, 'I'); } //本地生成.cer自签名证书文件 public function getCrtFile() { $prikeyPass = $this->prikeyPass; //私钥密码 $numberOfDays = $this->numberOfDays; //有效时长 $cerPath = $this->cerPath.md5($prikeyPass.$numberOfDays.'cer').".cer"; //生成证书路径 //$pfxpath = APP_ROOT_PATH."public/".md5($prikeyPass.$numberOfDays.'pfx').".pfx"; //密钥文件路径 if(!file_exists($cerPath)){ $config = $this->cerConfig; $dn = $this->dn; //生成证书文件 $priKey = openssl_pkey_new($config); $csr = openssl_csr_new($dn, $priKey,$config); $x509 = openssl_csr_sign($csr, null, $priKey, $numberOfDays); openssl_x509_export($x509,$cerStr); openssl_pkey_export($priKey, $pkStr); $data = $cerStr.$pkStr; $writeResult = file_put_contents($cerPath,$data); if($writeResult === false){ throw new Exception("证书或签名生成失败!"); } } $data = array(); $data['signing_cert'] = 'file://'.realpath($cerPath); $data['private_key'] = 'file://'.realpath($cerPath); $data['private_key_password'] = $prikeyPass; return $data; } }</pre> |
操作类的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <pre>require_once APP_ROOT_PATH."/system/utils/tcpdf_proxy.php"; $tcpdf = new TcpdfProxy(); $html = '<html>content</html>'; $singConf = array(); $singConf['image'] = APP_ROOT_PATH.app_conf('CONTRACT_SEAL'); $singConf['sign_info'] = array( 'Name' => app_conf('SHOP_TITLE'), ); $tcpdf->setSignConf($singConf); $tcpdf->outputPdf($html,'contract.pdf');</pre> |