PHP项目在线签章如何校验印章有效性

wen PHP项目 29

本文目录导读:

PHP项目在线签章如何校验印章有效性

  1. 核心校验逻辑
  2. 方案一:使用 OpenSSL 命令行(最通用,适合验证PDF/OFD)
  3. 方案二:使用 PHP 加密扩展(如 phpseclib / OpenSSL PHP扩展)
  4. 方案三:调用第三方签名API(推荐商业级应用)
  5. 方案四:国密SM2/SM3签名校验(使用PHP扩展)
  6. 总结:如何选择?
  7. 关键提示(避坑指南)

在PHP项目中校验电子印章的有效性,通常涉及以下几个核心层面,由于PHP本身不直接处理复杂的加密图形(如国密SM2/SM3签名),通常需要借助扩展库调用外部API/命令行工具来完成。

以下是几种主流的校验方案及其实现逻辑:

核心校验逻辑

一个“有效”的数字印章通常需要满足:

  1. 数字签名有效:印章数据(图片+签章时间+原文Hash)未被篡改。
  2. 证书有效:签名证书在有效期内,且未被吊销。
  3. 可信CA:证书颁发机构(CA)是合法、受信任的。
  4. 唯一性:印章数据未被重复使用或伪造。

使用 OpenSSL 命令行(最通用,适合验证PDF/OFD)

如果你的印章是嵌入在 PDFOFD 中的数字签名,最可靠的办法是调用系统级的 openssl 或专用验证工具(如 pdfsig)。

步骤:

  1. 系统需安装 OpenSSL 或 Poppler(提供 pdfsig 命令)。
  2. PHP 通过 exec() / shell_exec() 执行命令。
  3. 解析返回结果判断有效性。

代码示例(校验PDF数字签名):

<?php
function verifyPdfSignature($pdfFilePath) {
    // 使用 pdfsig 命令(来自 poppler-utils)
    $command = "pdfsig \"" . $pdfFilePath . "\" 2>&1";
    exec($command, $output, $returnCode);
    $resultStr = implode("\n", $output);
    // 解析结果
    $isValid = false;
    $details = [];
    if (strpos($resultStr, 'Signature is VALID') !== false) {
        $isValid = true;
    }
    // 提取签名者信息、时间等
    if (preg_match('/Signer Name: (.+)/', $resultStr, $matches)) {
        $details['signer'] = trim($matches[1]);
    }
    // 提取证书有效期
    if (preg_match('/Signing Time: (.+)/', $resultStr, $matches)) {
        $details['time'] = trim($matches[1]);
    }
    return [
        'valid' => $isValid,
        'details' => $details,
        'raw' => $resultStr
    ];
}
// 使用
$result = verifyPdfSignature('/path/to/signed_document.pdf');
if ($result['valid']) {
    echo "印章有效,签名者:" . $result['details']['signer'];
} else {
    echo "印章无效或已被篡改";
}
?>

优点: 无需商业库,依赖系统命令,支持标准 PKCS#7 签名。
缺点: 需要服务器安装对应工具;解析文本输出不够优雅;不支持国密(除非OpenSSL编译了国密引擎)。


使用 PHP 加密扩展(如 phpseclib / OpenSSL PHP扩展)

适用于校验原始的 PKCS#7 签名数据(即从印章中提取出的签名值)。

流程:

  1. 从印章文件(如 .esl 或自定义xml)中解析出:
    • signedData(原始数据或原文Hash)
    • signature(签名值)
    • certificate(签名证书)
  2. 使用 PHP OpenSSL 或 phpseclib 进行验证。

代码示例(使用PHP内置OpenSSL):

<?php
/**
 * 校验PKCS#7格式的签名数据
 * @param string $data 原始数据(原文或原文Hash)
 * @param string $signature 签名值(一般base64解码后)
 * @param string $certificate 签名证书(PEM格式)
 * @return bool
 */
function verifyPKCS7Signature($data, $signature, $certificate) {
    // 将证书写入临时文件
    $certFile = tempnam(sys_get_temp_dir(), 'cert_');
    file_put_contents($certFile, $certificate);
    // 将要签名的数据写入临时文件
    $dataFile = tempnam(sys_get_temp_dir(), 'data_');
    file_put_contents($dataFile, $data);
    // 将签名写入临时文件
    $sigFile = tempnam(sys_get_temp_dir(), 'sig_');
    file_put_contents($sigFile, $signature);
    // 使用openssl_pkcs7_verify验证
    // 需要签名数据是PKCS#7格式
    $ok = openssl_pkcs7_verify(
        $sigFile,      // 包含签名的文件
        PKCS7_NOVERIFY, // 先不验证证书链,我们自己验证
        $certFile,     // 提取的证书
        array(),       // 中间CA
        $dataFile,     // 原始数据
        $certFile      // 输出证书
    );
    // 清理临时文件
    unlink($certFile);
    unlink($dataFile);
    unlink($sigFile);
    if ($ok) {
        // 进一步校验证书有效期
        $certInfo = openssl_x509_parse(file_get_contents($certFile));
        $currentTime = time();
        $validFrom = strtotime($certInfo['validFrom_time_t']);
        $validTo = strtotime($certInfo['validTo_time_t']);
        return ($currentTime >= $validFrom && $currentTime <= $validTo);
    }
    return false;
}
// 设备:通常这些数据从印章结构中提取
$data = "合同原文内容..."; 
$signature = base64_decode("base64编码的签名值...");
$certificate = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----";
if (verifyPKCS7Signature($data, $signature, $certificate)) {
    echo "印章签名校验通过,证书有效。";
} else {
    echo "印章无效!";
}
?>

优点: 纯PHP实现,不依赖外部命令。
缺点: 不支持国密;需要自己解析印章格式;openssl_pkcs7_verify 对数据格式要求严格。


调用第三方签名API(推荐商业级应用)

对于要求支持国密(SM2/SM3)数据安全大规模并发的场景,建议对接专业的电子签章平台API(如法大大、e签宝、契约锁、腾讯电子签等)。

流程:

  1. 将待验证的签署文件(如PDF/OFD)上传至平台文件托管。
  2. 调用平台的验证接口。
  3. 平台返回签章的真伪、签署人、时间戳等信息。

代码示例(假设使用e签宝API):

<?php
// 假设使用某个云签章平台的验证接口
function cloudSealVerify($filePath) {
    $apiUrl = "https://api.signcloud.com/v2/seal/verify";
    $appId = "your_app_id";
    $secretKey = "your_secret_key";
    // 构造请求
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $apiUrl);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // 上传文件
    $postData = [
        'file' => new CURLFile($filePath),
        'appId' => $appId,
        'timestamp' => time()
    ];
    // 计算签名(具体按平台要求)
    $postData['sign'] = md5($appId . $postData['timestamp'] . $secretKey);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    $response = curl_exec($ch);
    curl_close($ch);
    $result = json_decode($response, true);
    // 解析平台返回的结果
    if ($result['code'] == 0 && $result['data']['verifyResult']) {
        return [
            'valid' => true,
            'signers' => $result['data']['signers'],
            'signedTime' => $result['data']['signedTime']
        ];
    }
    return ['valid' => false, 'message' => $result['msg']];
}
?>

优点: 支持国密、证书链自动验证、通常包含时间戳、免维护CA信任库。
缺点: 依赖网络、可能有费用、数据需要上传至第三方。


国密SM2/SM3签名校验(使用PHP扩展)

如果印章是使用国密算法生成的(国内电子签章强制标准 GM/T 0031-2014),需要使用支持国密的库。

可选库:

  • php-gmssl:PHP的国密扩展(需要编译安装)。
  • TASSL:国密版OpenSSL,通过命令行调用。

代码示例(假设装了php-gmssl扩展):

<?php
// 使用 php-gmssl 库 (伪代码)
// 注意:php-gmssl 的API可能随版本变化
function gmVerifySignature($publicKeyPem, $data, $signatureBase64) {
    // 1. 生成SM2公钥对象
    $sm2pubKey = \GMSSL\SM2\PublicKey::fromPem($publicKeyPem);
    // 2. 计算SM3摘要
    $sm3Hash = \GMSSL\SM3::hash($data);
    // 3. 验证SM2签名
    $signature = base64_decode($signatureBase64);
    return \GMSSL\SM2::verify($sm2pubKey, $sm3Hash, $signature);
}
// 使用
$pubKey = file_get_contents('ca_public_key.pem');
$originalData = '合同原文';
$signature = 'AGSJDCJKD...'; // 从印章中提取的Base64签名
if (gmVerifySignature($pubKey, $originalData, $signature)) {
    echo "国密印章有效";
}
?>

如何选择?

场景 推荐方案 理由
你使用第三方电子签平台 (如法大大、e签宝) 调用平台API 最简单、最合规、支持国密和区块链存证
自建签章系统,文件是PDF OpenSSL命令行 稳定,原生支持PDF签名验证
自建系统,校验自定义签名数据 PHP OpenSSL扩展 纯代码实现,无需外部依赖(仅限RSA)
必须支持国密SM2/SM3 国密扩展方案三(API) 国内合规要求,纯PHP目前无成熟方案直接支持SM2
需要验证证书吊销状态 方案三 API 或 自行实现 OCSP/CRL 检查 需要网络请求和CA支持

关键提示(避坑指南)

  1. 时间戳(TSP):大多数有效印章会包含可信时间戳,仅校验签名是不够的,还需要校验时间戳是否由权威机构(国家授时中心等)签发,可以调用 openssl ts -verify 或平台API来验。
  2. 证书吊销列表(CRL):即使证书在有效期内,也可能已被CA吊销,需要定期下载CRL或使用 openssl crl -verify 检查。
  3. 原始数据完整性:校验时必须使用签署时的原始数据(原文的Hash值),光验图片(印章图像)是无法确认原合同内容是否被篡改的。
  4. 开发环境 vs 生产环境:生产环境强烈建议使用商业级SDK或API,因为你可以把复杂的国密+CRL+时间戳+证书链验证交给专业平台,PHP仅仅做调用和业务逻辑。

抱歉,评论功能暂时关闭!