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

PHP(Laravel)中使用phpseclib实现RSA验签失败求助

RSA-PSS验签:Node.js正常但Laravel+phpseclib始终返回False的排查方案

我有一套可正常运行的Node.js签名验证代码,采用RSA-SHA256算法与RSA_PKCS1_PSS_PADDING填充方式。但在Laravel框架中使用phpseclib实现相同逻辑时,验签结果始终返回false,请求帮忙排查问题。

Node.js 代码示例

const crypto = require('crypto');
const fs = require('fs');
const constants = crypto.constants;

app.post('/api/verify', (req, res) => {  
    const signature = req.headers['signature']
    let rawBody = req.body;
    rawBody = JSON.stringify(rawBody);
    const result = verifySignature(signature, rawBody)
    res.send(`Signature verification status: ${result}`); // 返回True
});

const verifySignature = (signature, rawBody) => {
    const publicKey = fs.readFileSync('public.pem', 'utf8');
    const verifier = crypto.createVerify('RSA-SHA256')
    verifier.update(rawBody)
    return verifier.verify({ key: publicKey, padding: constants.RSA_PKCS1_PSS_PADDING }, signature, 'base64')
}

Laravel 代码示例

use phpseclib\Crypt\RSA;

$signature = $request->header('signature');
$bodyContent = $request->getContent();
$result = $this->verify_signature($signature , $bodyContent); // 始终返回False

protected function verify_signature($signature, $requestBody): bool
{
    $rsa = new RSA();
    $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
    $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
    $rsa->setHash("sha256");
    $public_key = <<<EOD
-----BEGIN PUBLIC KEY-----
XXXXXXXXXXX
-----END PUBLIC KEY-----
EOD;

    $rsa->loadKey($public_key);
    $result = $rsa->verify($requestBody , base64_decode($signature));

    return $result;
}

排查关键点及修正方案

1. 请求体序列化一致性问题

Node.js中是将req.body对象通过JSON.stringify()序列化为字符串,而Laravel的$request->getContent()获取的是原始请求体,两者可能存在差异:

  • 原始请求体的键顺序、空格、转义规则可能和JSON.stringify()的输出不一致
  • 如果请求是JSON格式,Laravel解析后再序列化的结果要和Node.js完全匹配

修正:
将Laravel中的请求体处理改为和Node.js一致的序列化方式:

// 替换原有的$bodyContent = $request->getContent();
$bodyContent = json_encode($request->all(), JSON_UNESCAPED_UNICODE);

2. PSS填充的MGF1哈希算法未显式指定

Node.js的RSA_PKCS1_PSS_PADDING默认使用与签名哈希一致的SHA256作为MGF1的哈希算法,但phpseclib需要显式配置,否则可能使用默认的其他哈希算法导致验签失败。

修正:
在phpseclib的配置中添加MGF1哈希设置:

$rsa = new RSA();
$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
$rsa->setHash("sha256");
$rsa->setMGFHash('sha256'); // 新增这一行,指定MGF1哈希算法为SHA256
// ... 其余代码不变

3. 公钥完整性验证

确保Laravel代码中硬编码的公钥与Node.js使用的public.pem完全一致,包括首尾的标记行、换行符和公钥内容,避免复制时遗漏或修改字符。

4. 签名的编码与解码一致性

确认Laravel获取的signature header未被自动转义或编码(比如URL编码),base64_decode($signature)能正确还原签名的原始二进制数据。

修正后的Laravel完整代码

use phpseclib\Crypt\RSA;

$signature = $request->header('signature');
// 改为与Node.js一致的序列化方式
$bodyContent = json_encode($request->all(), JSON_UNESCAPED_UNICODE);
$result = $this->verify_signature($signature , $bodyContent);

protected function verify_signature($signature, $requestBody): bool
{
    $rsa = new RSA();
    $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
    $rsa->setHash("sha256");
    $rsa->setMGFHash('sha256'); // 指定MGF1哈希算法
    $public_key = <<<EOD
-----BEGIN PUBLIC KEY-----
XXXXXXXXXXX
-----END PUBLIC KEY-----
EOD;

    $rsa->loadKey($public_key);
    // 确保signature是正确的base64字符串
    $binarySignature = base64_decode($signature);
    if ($binarySignature === false) {
        return false;
    }
    return $rsa->verify($requestBody, $binarySignature);
}

内容的提问来源于stack exchange,提问作者Ala Shariaty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 21:44:57