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

如何用PHP根据模数和指数生成与JS一致的RSA公钥并加密

问题:PHP生成RSA公钥与Node.js不一致,无法匹配Postman加密逻辑

需求背景

现有Postman前置脚本实现了RSA加密登录逻辑,需用PHP复刻相同流程:先请求获取包含id、模数modulus、指数exponent的session,再将session.id + '**' + 密码加密后完成登录并获取Cookie。

Postman前置脚本

const modulus = session.rsa.M;
const exponent = session.rsa.E;           
const rsaKey = pmlib.rs.KEYUTIL.getKey({ n: modulus, e: exponent });
const encodedPassword = pmlib.rs.KJUR.crypto.Cipher.encrypt(session.id + '**' + password, rsaKey, 'RSA');

Node.js成功实现代码

使用jsrsasign库可正常生成正确公钥:

const KEYUTIL = require('jsrsasign').KEYUTIL;
const KJUR = require('jsrsasign');

// 示例模数(十六进制)
const modulus = session.rsa.M; // ADA6FB0A9B123950AE71EB0A4A90426156CC7545A3F5617096E7A0C095773B413FCA9DF50C231CA4804991C76BB477D721E363E0C7B61852AAC66134D43E21F3A7433C275C62B05E1CA9C5E750FB13B5DD3E6FBC2415C0EE8D23119CA375CE35307DDF828D40FC15CBA2CA545786B7D236B4A45508775250CC5AC376B29DB129
// 示例指数(十六进制)
const exponent = session.rsa.E; // 010001

const rsaKey = KEYUTIL.getKey({ n: modulus, e: exponent });
const pem = KJUR.KEYUTIL.getPEM(rsaKey);
console.log(pem);

PHP当前实现(存在问题)

使用相同的模数和指数,PHP生成的公钥与Node.js不一致,导致加密逻辑失效:

use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Math\BigInteger;

$modulus = $session['rsa']['M'];
$exponent = $session['rsa']['E'];

$rsa_key = PublicKeyLoader::load([
    'e' => new BigInteger(base64_decode($exponent), 256),
    'n' => new BigInteger(base64_decode($modulus), 256),
]);

echo '<pre>';
echo ($rsa_key);
echo '</pre>';

openssl_public_encrypt(data, $encoded, $rsa_key);
$encoded = bin2hex($encoded);

公钥示例对比

JS生成的正确公钥

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtpvsKmxI5UK5x6wpKkEJhVsx1
RaP1YXCW56DAlXc7QT/KnfUMIxykgEmRx2u0d9ch42Pgx7YYUqrGYTTUPiHzp0M8
J1xisF4cqcXnUPsTtd0+b7wkFcDujSMRnKN1zjUwfd+CjUD8FcuiylRXhrfSNrSk
VQh3UlDMWsN2sp2xKQIDAQAB
-----END PUBLIC KEY-----

PHP生成的错误公钥

-----BEGIN PUBLIC KEY-----
MIHgMA0GCSqGSIb3DQEBAQUAA4HOADCBygKBwA/RBAPQ/AfeOQOQtuQthAutwONR
QtdONd9tvBfNfeNPBOxdRRduOAdAwee9+9QOtdBNtu/dvPAfQ+txfew9xNRevfRA
de/e+A+d+d/eu+9OudRfRfAgvPOfPeOAtfQuAd+w9uhfeBNxfddQ9QdwOwOe9g+A
QNdNwudt+/dRdhNPQhNQ9BQPdAhNfQewQuwew/egNNgAg++e+NQABPee+wNRe9Ou
eguxffRfBOAweQdwhA9edfQQ+BNBetwuROghNQIFANNdNNM=
-----END PUBLIC KEY-----

问题原因与修正方案

问题根源

PHP代码错误地对十六进制格式的模数和指数执行了base64_decode,导致解析出的BigInteger完全错误,生成的公钥自然和JS不一致。而JS的KEYUTIL.getKey是直接将十六进制字符串解析为BigInteger。

修正后的PHP代码

use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Math\BigInteger;

$modulus = $session['rsa']['M']; // 十六进制字符串
$exponent = $session['rsa']['E']; // 十六进制字符串
$password = '你的密码'; // 替换为实际密码

// 直接从十六进制字符串创建BigInteger,指定参数为16(表示输入是十六进制)
$rsa_key = PublicKeyLoader::load([
    'e' => new BigInteger($exponent, 16),
    'n' => new BigInteger($modulus, 16),
]);

// 验证生成的公钥是否与JS一致
echo '<pre>';
echo $rsa_key;
echo '</pre>';

// 按照Postman逻辑拼接加密内容
$data = $session['id'] . '**' . $password;
// 使用PKCS#1 v1.5填充(与JS默认加密填充一致)
$encoded = $rsa_key->encrypt($data, 'pkcs1');
// 转十六进制输出,匹配Postman的加密结果格式
$encodedPassword = bin2hex($encoded);

关键细节

  • JS的KJUR.crypto.Cipher.encrypt默认使用PKCS#1 v1.5填充,PHP中需明确指定'pkcs1',避免使用默认的OAEP填充,否则加密结果不匹配。
  • 确保$session['id']、$modulus、$exponent的取值与Postman/Node.js完全一致,避免字符编码或格式差异。

内容的提问来源于stack exchange,提问作者Elyas Behroozizade

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 09:15:18