PHP如何正确识别数字签名PDF的签名人?
问题诊断与修复方案
常见问题点
- 误将证书链节点当作签名人:PDF签名的PKCS7数据包含完整证书链(签名人证书+CA证书),若脚本未过滤CA证书,会把CA机构和重复的签名人信息一并返回。
- 仅读取第一个签名域:多签名PDF存在多个独立签名域,若脚本只遍历第一个域,自然无法获取后续的CAROL签名。
- 未正确提取叶子证书:签名人信息存储在PKCS7的叶子证书(最底层用户证书)中,脚本可能错误遍历了整个证书链的所有证书。
修复步骤
1. 遍历所有签名域
PDF签名信息存于AcroForm的/Fields数组,需遍历所有类型为Sig的域:
// 示例:用Zend_PDF遍历全部签名域 $pdf = Zend_PDF::load('signed.pdf'); $acroForm = $pdf->getAcroForm(); $signatureFields = []; if ($acroForm && isset($acroForm->fields)) { foreach ($acroForm->fields as $field) { if ($field->getType() === 'Sig') { $signatureFields[] = $field; } } }
2. 从PKCS7中提取签名人证书
解析PKCS7数据时,只取证书链的叶子节点(签名人证书),排除CA证书:
// 解析PKCS7,提取签名人姓名 function getSignerName($pkcs7Data) { $certStore = openssl_pkcs7_read($pkcs7Data, $certs); if (!$certStore) return null; // 证书链第一个节点为签名人叶子证书 $signerCert = $certs[0]; $certInfo = openssl_x509_parse($signerCert); return $certInfo['subject']['CN'] ?? null; }
3. 过滤CA证书与去重
通过证书扩展字段判断是否为CA,过滤后去重签名人信息:
// 判断证书是否为CA机构证书 function isCACert($cert) { $certInfo = openssl_x509_parse($cert); $basicConstraints = $certInfo['extensions']['basicConstraints'] ?? ''; return str_contains($basicConstraints, 'CA:TRUE'); } // 整理有效签名人列表 $validSigners = []; foreach ($signatureFields as $field) { $pkcs7 = $field->getSignature()->getContents(); $certStore = openssl_pkcs7_read($pkcs7, $certs); foreach ($certs as $cert) { if (!isCACert($cert)) { $signerName = openssl_x509_parse($cert)['subject']['CN']; if (!in_array($signerName, $validSigners)) { $validSigners[] = $signerName; } } } }
结果验证
修复后用pdfsig命令对比结果:
pdfsig signed.pdf
确保脚本输出的签名人列表与pdfsig返回的Signer字段完全一致。
内容的提问来源于stack exchange,提问作者Vinicius Menezes
相关产品推荐
相关产品推荐

