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

Java生成的SHA1withRSA签名无法在PHP中验证,求解决办法

Java生成RSA签名在PHP端验证失败的解决方法

核心问题分析

你的场景中Java本地验证通过但PHP端失败,主要出在签名生成的重复调用、二进制数据的字符串转换、待签名字符串一致性这几个关键点上,以下是具体问题和修复方案:

1. Java原签名代码的致命错误

原代码中两次调用sign.sign():

verifySignature(body, sign.sign());
return new String(sign.sign());

Signature对象在调用sign()后会重置内部状态,第二次调用时未重新执行update(body.toString().getBytes()),生成的签名是无效的。同时,直接将二进制签名转成字符串(new String(sign.sign()))会因字符编码(如UTF-8)无法正确解析二进制数据,导致签名损坏。

修复后的正确签名生成(你更新后的代码已修正部分问题,需补充编码指定):

Signature sign = Signature.getInstance("SHA1withRSA");
sign.initSign(privKey);
// 明确指定UTF-8编码,避免环境默认编码差异
sign.update(body.toString().getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = sign.sign();
String signStr = Base64.getEncoder().encodeToString(signatureBytes);
// 本地验证需传入原始签名字节,而非Base64字符串的字节
verifySignature(body, signatureBytes);
return signStr;

2. PHP端的关键问题修复

(1)待签名字符串必须与Java完全一致

Java端使用body.toString()生成JSON字符串,PHP端需生成完全相同格式的JSON:

  • 确保JSON的键顺序、空格、引号类型和Java一致(Java的JsonObject.toString()默认无空格,PHP需禁用格式化输出)
  • 明确指定UTF-8编码,与Java端对齐

修正后的PHP待签名数据处理:

$dd = ["email"=>"kkamran@gmail.com","password"=>"!Pass1234","platform"=>"con"];
// 生成与Java一致的无空格JSON字符串,保留原始字符编码
$data = json_encode($dd, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
// 强制转成UTF-8编码
$data = mb_convert_encoding($data, 'UTF-8');

(2)公钥读取有效性校验

确保PHP读取的公钥与Java使用的是同一个PEM文件,且格式完整(包含-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----头部尾部)。可通过返回值判断读取是否成功:

$pub_key = file_get_contents("public.pem");
$pubkeyid = openssl_pkey_get_public($pub_key);
if (!$pubkeyid) {
    die("公钥读取失败:" . openssl_error_string());
}

(3)完整PHP验证代码

$pub_key = file_get_contents("public.pem");
$pubkeyid = openssl_pkey_get_public($pub_key);
if (!$pubkeyid) {
    die("公钥读取失败:" . openssl_error_string());
}

// 生成与Java一致的待签名JSON字符串
$dd = ["email"=>"kkamran@gmail.com","password"=>"!Pass1234","platform"=>"con"];
$data = json_encode($dd, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$data = mb_convert_encoding($data, 'UTF-8');

// 解码Java传来的Base64签名
$ss = base64_decode("dHIG77+9fu+/ve+/ve+/vVTvv73vv70677+9QiHGnmjHu++/vQYm77+977+977+9bXM+Pe+/ve+/vRtlJcyYY++/vdiu77+9fu+/vUkM77+9RQI2BO+/ve+/vX4YFRwt77+9GO+/vWrvv71F77+9C++/ve+/ve+/ve+/vWTvv73vv71zB++/vXFaQ86277+9LHXvv71+Q3Dvv73vv73vv71TEzMo77+9SgLvv73vv73vv70377+9IO+/vVFW0rYc77+9QX8a77+977+977+977+9Iu+/ve+/ve+/vUYkIU5heO+/vc6e77+977+977+9Rmnvv73vv71+PRxy77+9zLTvv73vv71aDe+/vQjvv71UY++/ve+/vUTvv704FyZjBkB/77+977+9fe+/ve+/ve+/ve+/vXJgUAYUVO+/ve+/ve+/vXNE77+9fmAc77+977+9Ye+/vUAKF8izTO+/ve+/vSPvv700Ru+/ve+/ve+/vWHvv70bDtyoNmxpKd2UKe+/ve+/ve+/vXgCLe+/ve+/vQPvv70p77+9S2Xvv73vv71bX++/vUhw77+9Oe+/vV0+77+9De+/vQs=");

// 执行验证,OPENSSL_ALGO_SHA1对应Java的SHA1withRSA
$ok = openssl_verify($data, $ss, $pubkeyid, OPENSSL_ALGO_SHA1);

switch ($ok) {
    case 1:
        echo "签名验证通过";
        break;
    case 0:
        echo "签名验证失败";
        break;
    default:
        echo "验证出错:" . openssl_error_string();
}

3. 通用排查步骤

  • 对比待签名字符串:分别在Java和PHP端输出待签名的字符串,确保完全一致(包括空格、换行、编码)
  • 检查签名的Base64编码:Java生成的Base64字符串和PHP解码后的字节是否一致
  • 验证公钥一致性:用openssl rsa -pubin -in public.pem -text查看公钥信息,确保Java和PHP使用的公钥完全相同
  • 查看错误信息:PHP端用openssl_error_string()获取具体的验证错误,快速定位问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 07:20:31