You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PHP中验证PKCS#7签名?需转换可行Java代码

如何在PHP中验证PKCS#7(CMS)格式的数字签名

你遇到的核心问题是误用了普通RSA验证逻辑来处理PKCS#7(CMS)格式的签名——PKCS#7签名是一个包含签名值、签名证书、算法信息等的完整容器,不是单纯的RSA签名串。你之前的PHP代码要么生成了新的密钥对(完全错误),要么从网站SSL证书取公钥(不是签名用的证书),自然无法验证通过。

下面给出两种和你Java代码逻辑完全对齐的可行PHP实现方案:

方案1:使用PHP OpenSSL扩展(原生支持PKCS#7)

OpenSSL的openssl_pkcs7_verify函数专门用于处理PKCS#7签名验证,我们可以用它快速实现需求:

<?php
// 1. 处理输入的签名参数
$JSONDATA = str_replace("'", '"', $_GET["sign"]);
$data = json_decode($JSONDATA, true);
$signatureBase64 = $data['signature'];
$originalData = json_encode($data['param']); // 需要验证的原始业务数据

// 2. 将base64编码的PKCS#7签名解码后写入临时文件(OpenSSL函数要求文件输入)
$signatureDer = base64_decode($signatureBase64);
$sigTempFile = tempnam(sys_get_temp_dir(), 'pkcs7_sig');
file_put_contents($sigTempFile, $signatureDer);

// 3. 将原始业务数据写入临时文件
$dataTempFile = tempnam(sys_get_temp_dir(), 'pkcs7_data');
file_put_contents($dataTempFile, $originalData);

// 4. 执行PKCS#7签名验证
$verifyResult = openssl_pkcs7_verify(
    $dataTempFile, // 原始数据文件路径
    PKCS7_NOVERIFY, // 先只验证签名,不验证证书链;若需验证证书有效性可移除该参数
    $sigTempFile, // PKCS#7签名文件路径
    [], // 可信CA证书列表(若需验证证书链,传入CA证书路径数组)
    null,
    null
);

// 5. 清理临时文件
unlink($sigTempFile);
unlink($dataTempFile);

// 6. 输出验证结果
if ($verifyResult === 1) {
    echo "签名验证成功!";
} elseif ($verifyResult === 0) {
    echo "签名验证失败:" . openssl_error_string();
} else {
    echo "验证过程出错:" . openssl_error_string();
}
?>

关键说明:

  • PKCS7_NOVERIFY参数:如果你的场景只需要验证签名本身(不需要校验签名证书的有效期、信任链),可以保留该参数;如果需要完整复刻Java代码中的证书有效性检查,移除该参数并传入可信CA证书数组即可。
  • 临时文件:openssl_pkcs7_verify要求输入为文件路径,因此需要临时存储解码后的签名和原始数据。

方案2:使用phpseclib的CMS模块(完全对齐Java逻辑)

phpseclib 3.x版本支持CMS(PKCS#7)操作,能直接解析签名容器、提取证书,完全复刻你Java代码的验证流程:

<?php
require 'vendor/autoload.php'; // 确保已通过Composer安装phpseclib 3.x

use phpseclib3\File\CMS;
use phpseclib3\Crypt\PublicKeyLoader;

// 1. 处理输入数据
$JSONDATA = str_replace("'", '"', $_GET["sign"]);
$data = json_decode($JSONDATA, true);
$signatureBase64 = $data['signature'];
$originalData = json_encode($data['param']);

// 2. 解析PKCS#7签名容器
$cms = new CMS();
$cms->load(base64_decode($signatureBase64));

// 3. 遍历签名者,复刻Java代码的验证逻辑
$verified = false;
$signers = $cms->getSigners();

foreach ($signers as $signer) {
    // 获取签名者证书
    $cert = $signer['certificate'];
    
    // 检查证书有效期(对应Java的checkValidity())
    $now = new DateTime();
    if ($cert->getNotBefore() > $now || $cert->getNotAfter() < $now) {
        continue;
    }
    
    // 验证证书自身的签名(对应Java的verify(signedcert.getPublicKey()))
    if (!$cert->verify($cert->getPublicKey())) {
        continue;
    }
    
    // 验证业务数据的签名
    $publicKey = PublicKeyLoader::load($cert->getPublicKey());
    if ($publicKey->verify($originalData, $signer['signature'])) {
        $verified = true;
        break;
    }
}

// 输出结果
echo $verified ? "签名验证成功!" : "签名验证失败";
?>

关键说明:

  • 确保安装phpseclib 3.x版本:执行composer require phpseclib/phpseclib:^3.0完成安装。
  • 这段代码完全对齐你的Java逻辑:遍历签名者、检查证书有效期、验证证书自身合法性、最终验证业务数据签名。

为什么你之前的代码失败?

  • phpseclib方案错误:你生成了新的RSA密钥对,而不是使用签名容器中携带的证书公钥,相当于用自己的密钥验证第三方签名,必然失败。
  • OpenSSL方案错误:你从目标网站的SSL证书获取公钥,但签名是用对方的代码签名证书生成的,和SSL证书完全无关,公钥不匹配自然验证失败。

内容的提问来源于stack exchange,提问作者Prince Expedition

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:50:02