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
相关产品推荐
相关产品推荐

