如何在PHP中验证TCPDF签名的PDF是否匹配指定公私密钥
TCPDF签名PDF的密钥匹配验证方法
核心流程说明
整个验证分两个核心环节:先从已签名PDF中提取待校验的原始数据$data和签名值$signature,再用你持有的公钥做签名匹配校验。
步骤1:提取$signature和$data参数
推荐使用SetaPDF组件解析PDF结构,也可以自行解析PDF字典实现,示例代码如下:
// 引入SetaPDF核心组件(可通过composer require setasign/setapdf-core安装) require_once 'vendor/autoload.php'; $pdfPath = '你要验证的已签名PDF路径'; // 加载PDF文件 $parser = new \SetaPDF_Core_Parser_Pdf($pdfPath); $document = $parser->getDocument(); // 获取表单中的签名字段(TCPDF默认签名字段名为Signature) $form = $document->getCatalog()->getAcroForm(); $signatureFields = $form->getFieldsByName('Signature'); $signature = ''; $data = ''; foreach ($signatureFields as $field) { $sigDict = $field->getValue(); // 提取签名值:转成二进制格式,得到$signature参数 $signatureHex = trim($sigDict->getValue('Contents')->getValue(), '<>'); $signature = hex2bin($signatureHex); // 提取待签名原始数据:按ByteRange指定的范围读取PDF字节,得到$data参数 $byteRange = $sigDict->getValue('ByteRange')->toPhp(); $fh = fopen($pdfPath, 'rb'); for ($i = 0; $i < count($byteRange); $i += 2) { fseek($fh, $byteRange[$i]); $data .= fread($fh, $byteRange[$i + 1]); } fclose($fh); break; // 仅验证第一个签名,多签名场景可自行扩展 }
步骤2:用公钥验证签名匹配性
使用PHP内置的openssl扩展做PKCS7签名校验即可,不需要用到私钥:
// 写入临时文件供openssl函数调用 $tempDataFile = tempnam(sys_get_temp_dir(), 'pdf_verify'); file_put_contents($tempDataFile, $data); $tempSigFile = tempnam(sys_get_temp_dir(), 'pdf_sig'); // 把二进制签名转成PEM格式 $pemSign = "-----BEGIN PKCS7-----\n" . chunk_split(base64_encode($signature), 64, "\n") . "-----END PKCS7-----\n"; file_put_contents($tempSigFile, $pemSign); // 你的公钥文件路径 $publicKeyPath = 'file://' . realpath('你的公钥.pem'); // 执行验证:返回1为匹配,0为不匹配,-1为执行错误 $verifyResult = openssl_pkcs7_verify( $tempDataFile, PKCS7_BINARY | PKCS7_NOVERIFY | PKCS7_NOCHAIN, // 仅校验签名归属,不校验证书有效性 $tempSigFile, [], $publicKeyPath ); // 清理临时文件 unlink($tempDataFile); unlink($tempSigFile); if ($verifyResult === 1) { echo '验证通过:该PDF为对应密钥签名'; } else { echo '验证失败:签名不匹配或文件被篡改'; }
注意:如果你的公钥是DER格式,需要先转成PEM格式再传入验证函数。
内容的提问来源于stack exchange,提问作者Vitalicus
相关产品推荐
相关产品推荐

