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

