如何通过代码验证TCPDF中setSignature()生成的签名PDF文件?
自行编码验证TCPDF生成的签名PDF方法
先说明:TCPDF只负责生成带签名的PDF,没有内置的签名验证函数。要自己写代码验证,得解析PDF的签名结构,再借助密码学工具完成验证,以下是具体思路和实现步骤:
核心逻辑
PDF签名遵循PKCS#7标准,TCPDF会把签名数据(含证书链、签名算法)嵌入PDF的/Contents字段,同时用/ByteRange标记未被签名覆盖的文档范围。验证流程分三步:
- 从PDF里提取签名相关的关键数据
- 验证证书链的有效性(信任度、有效期、是否吊销)
- 用证书公钥校验签名值和原始文档哈希是否匹配
具体实现步骤
1. 解析PDF的签名结构
首先要定位PDF里的签名域,提取/ByteRange(未签名的字节范围)和/Contents(Base64编码的PKCS#7签名数据)。可以用现成的PDF解析库简化操作,比如PHP环境用FPDI(它能读取PDF结构,虽然不做验证):
// PHP示例:用FPDI读取签名域信息 $pdf = new \setasign\Fpdi\Fpdi(); $pdf->setSourceFile('your_signed.pdf'); $acroForm = $pdf->getAcroForm(); $signatureFields = $acroForm->getFieldsOfType(\setasign\Fpdi\PdfParser\PdfAcroForm::FIELD_TYPE_SIGNATURE); foreach ($signatureFields as $field) { $byteRange = $field->getDictionary()->getValue('/ByteRange')->toPhp(); $contents = $field->getDictionary()->getValue('/Contents')->toPhp(); // 后续处理这两个变量 }
如果不想用第三方库,也可以手动解析PDF的对象结构,找到签名字段对应的字典。
2. 计算原始文档的哈希
根据/ByteRange的四个值(格式是[起始偏移1, 长度1, 起始偏移2, 长度2]),读取PDF中这两段的内容,拼接后计算哈希——注意要和TCPDF签名时用的算法一致(比如SHA-256):
// PHP示例:计算原始文档哈希 $file = fopen('your_signed.pdf', 'rb'); // 读取第一段 fseek($file, $byteRange[0]); $part1 = fread($file, $byteRange[1]); // 读取第二段 fseek($file, $byteRange[2]); $part2 = fread($file, $byteRange[3]); // 拼接并计算哈希 $originalContent = $part1 . $part2; $calculatedHash = hash('sha256', $originalContent, true); fclose($file);
3. 验证PKCS#7签名
把/Contents里的Base64字符串解码成PKCS#7数据,用密码学库验证。PHP里可以直接用openssl扩展:
// PHP示例:验证PKCS#7签名并校验证书链 $pkcs7Data = base64_decode($contents); // 先验证签名本身,PKCS7_NOVERIFY表示先不验证证书链 $verifyResult = openssl_pkcs7_verify($pkcs7Data, PKCS7_NOVERIFY); if ($verifyResult === 1) { // 签名格式合法,接下来验证证书链 $certInfo = openssl_pkcs7_read($pkcs7Data); $certificates = $certInfo['certificates']; foreach ($certificates as $cert) { // 检查证书有效期 $parsedCert = openssl_x509_parse($cert); if (time() > $parsedCert['validTo_time_t'] || time() < $parsedCert['validFrom_time_t']) { die('证书已过期或未生效'); } // 检查证书是否在信任链里(需要提前导入信任的根证书) $trustedRoot = file_get_contents('your_trusted_root_cert.pem'); $chainVerify = openssl_x509_verify($cert, $trustedRoot); if ($chainVerify !== 1) { die('证书不受信任'); } } echo '签名验证通过'; } else { die('签名验证失败:' . openssl_error_string()); }
4. 额外检查项
- 确认TCPDF签名时用的哈希算法和你验证时一致(建议在生成签名时指定SHA-256:
$pdf->setSignature($cert, $key, '', '', 2, array('algorithm' => 'sha256'))) - 可以查询证书的吊销状态(OCSP或CRL方式),避免使用已吊销的证书
- 检查签名域的
/M字段(签名时间)是否符合预期
其他语言环境的思路
- Python:用
PyPDF2提取签名数据,cryptography库处理PKCS#7验证和证书链校验 - Java:用
BouncyCastle解析PDF签名,iText辅助处理PDF结构,完成证书链和签名验证
内容的提问来源于stack exchange,提问作者Chlaris Butler
相关产品推荐
相关产品推荐

