php使用rsa进行公钥解密

问题

最近对接一个三方的通道,对方给了一个java的rsa公钥解密的demo。我想当然直接用php的openssl_public_decrypt一顿操作,出来个null。WTF?

贴一下java的demo如下

package com.test;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;

import java.nio.charset.Charset;

/**
* 3.0api 加解密 加签验签
*
* @author Admin
*/
public class VerifyRsaTest {


/**
* 加解密 加签验签
*/
public static void main(String[] args) throws Exception {


String publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyxVgchYx/xx3jWyKPxb4s7boVOSDLxu7MP2U0NNAqtWSNMRRF+5sAjMd3RyeftsRT/QWAf2Rs9P/oZikGP0yqpsrWYHgbLcYLqIsgNPr6yOk855gvHB5szRsQggK0d4hl562Lx5T9DvHuM5u2jChsqZ7PvRXmNa+SDBtEB/3fnwIDAQAB";
String aa="C5rKZS/HWcEcaSFVyjmmOThYeEaEUwzK7kJf9NK5MkzkWxFH7VoQJS77c19jm9mt1YlhL5RGIficRi2W2+O8yMsT1Jps/7zvtzgV3lMp4ZBWciRg7/UU5CJq/CrVtfxHYrNQKn5eFTpaDX/cENb8u47vxtjZpwPhbNIc7HSCCLVZ0qSUgKCFHS/JNMGnin8rpCOL0ON5nEzmJnyDhlQikTVDTgDYdDyGokOkGnIUrD9JcMQ0sSLQXRk3qT3zfCAamgRWhDUDgUG+7HHoya5nFwjJ+UHq7pfxcu+gFDDWvHqAN1q+ayl1kb7hf0NSYjG0/d/MB2LT5XLxhDgVIQSwGiHRB/1nHIiNJGQnEirtsNcGSl9hXZ7PPaAm5Oo2gadIFh8ro0BKwnlmha6WmRS47sf7De3zlRVeSLWYXJYyniFj9+LumdUOgwWaZHrJXSpQSTj2GQtwLy0yjirkWKJXt2YfpeEXsUpaZib0Rbo8yuruvZqM1aAKtMwGRdBwTvKXECA6uCqizBPBtyNRRAUL7AvwZCKHJQdAY1u4yhKngDYEwO651PPVO/IkKw9CbC4mRpfhD5sIFoqIcFyl0f4ns/USip5gKUofKGv0hStQD8Hq9JIQEvR9M5lkx2KZarudIttZiZKycqMUA4xCHsJetbohBDQwMHY3I0tsKjy3WuiWbzJsuJrQD4qfbp72UOKjD+zdeyTzykdKOPEYvmsEucSlubFrd8jmQlyWjR/3roQCqlhGlZmH7CGf+epSewULOjQP6geUh1Zl2p0CJNBNd8olCqIHFt4AQqb1GhdVLcDpOg63HBGj8O+WiLbM9sE7umDYizmC7ImNIQ0mPRPuBw==";

String str = URLUtil.decode(aa, Charset.defaultCharset(),false);

String decrypt = decryptRsaPublicKey(str, publicKey);
System.out.println("对接方解密,decrypt = " + decrypt);

}

// RSA公钥解密
public static String decryptRsaPublicKey(String targetData, String publicKeyBase64) {
// RSA公钥解密
RSA rsa = SecureUtil.rsa(null, publicKeyBase64);
byte[] decrypt = rsa.decrypt(Base64.decode(targetData), KeyType.PublicKey);
return new String(decrypt, CharsetUtil.CHARSET_UTF_8);
}
}

再贴一下一开始的php尝试如下

$publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyxVgchYx/xx3jWyKPxb4s7boVOSDLxu7MP2U0NNAqtWSNMRRF+5sAjMd3RyeftsRT/QWAf2Rs9P/oZikGP0yqpsrWYHgbLcYLqIsgNPr6yOk855gvHB5szRsQggK0d4hl562Lx5T9DvHuM5u2jChsqZ7PvRXmNa+SDBtEB/3fnwIDAQAB";
$txt="C5rKZS/HWcEcaSFVyjmmOThYeEaEUwzK7kJf9NK5MkzkWxFH7VoQJS77c19jm9mt1YlhL5RGIficRi2W2+O8yMsT1Jps/7zvtzgV3lMp4ZBWciRg7/UU5CJq/CrVtfxHYrNQKn5eFTpaDX/cENb8u47vxtjZpwPhbNIc7HSCCLVZ0qSUgKCFHS/JNMGnin8rpCOL0ON5nEzmJnyDhlQikTVDTgDYdDyGokOkGnIUrD9JcMQ0sSLQXRk3qT3zfCAamgRWhDUDgUG+7HHoya5nFwjJ+UHq7pfxcu+gFDDWvHqAN1q+ayl1kb7hf0NSYjG0/d/MB2LT5XLxhDgVIQSwGiHRB/1nHIiNJGQnEirtsNcGSl9hXZ7PPaAm5Oo2gadIFh8ro0BKwnlmha6WmRS47sf7De3zlRVeSLWYXJYyniFj9+LumdUOgwWaZHrJXSpQSTj2GQtwLy0yjirkWKJXt2YfpeEXsUpaZib0Rbo8yuruvZqM1aAKtMwGRdBwTvKXECA6uCqizBPBtyNRRAUL7AvwZCKHJQdAY1u4yhKngDYEwO651PPVO/IkKw9CbC4mRpfhD5sIFoqIcFyl0f4ns/USip5gKUofKGv0hStQD8Hq9JIQEvR9M5lkx2KZarudIttZiZKycqMUA4xCHsJetbohBDQwMHY3I0tsKjy3WuiWbzJsuJrQD4qfbp72UOKjD+zdeyTzykdKOPEYvmsEucSlubFrd8jmQlyWjR/3roQCqlhGlZmH7CGf+epSewULOjQP6geUh1Zl2p0CJNBNd8olCqIHFt4AQqb1GhdVLcDpOg63HBGj8O+WiLbM9sE7umDYizmC7ImNIQ0mPRPuBw==";
// $txt = urldecode($txt);
$publicKey = "-----BEGIN PUBLIC KEY-----\n"
. wordwrap($publicKey, 64, "\n", true)
. "\n-----END PUBLIC KEY-----";
$publicKeyResource = openssl_pkey_get_public($publicKey);
openssl_public_decrypt(base64_decode($txt), $decryptedData, $publicKeyResource);
dd($decryptedData);
//null

解决

然后开始google,百度一顿搜,搜出来的不能说一点用没有吧,也可以说是啥忙也帮不上了。于是learnku求助了一下社区的大佬们,得到了解答,特此记录一下。

原因

rsa加密和解密根据秘钥长度不同,对密文长度是有限制的。

秘钥对应密文长度

秘钥长度 加密最大长度 解密最大长度
1024 117 128
2048 245 256
4096 501 512

解决开始对应的java demo转译php


function decryptRsaPublicKey($targetData, $publicKeyBase64)
{
// 构建公钥
$publicKey = "-----BEGIN PUBLIC KEY-----\n" .
chunk_split($publicKeyBase64, 64, "\n") .
'-----END PUBLIC KEY-----';

// 将目标数据进行 Base64 解码
$decodedData = base64_decode($targetData);

// 加载公钥
$keyResource = openssl_pkey_get_public($publicKey);
if (!$keyResource) {
throw new Exception('Invalid public key');
}

$decrypted = '';
$maxLength = 128; // 1024位密钥的块大小为128字节
$offset = 0;

// 分片解密
while ($offset < strlen($decodedData)) {
$chunk = substr($decodedData, $offset, $maxLength);
$offset += $maxLength;
$decryptedChunk = '';

// 公钥解密
if (!openssl_public_decrypt($chunk, $decryptedChunk, $keyResource)) {
throw new Exception('Failed to decrypt data');
}
$decrypted .= $decryptedChunk;
}

// 释放密钥资源
openssl_free_key($keyResource);

return $decrypted;
}

// 测试数据
$publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyxVgchYx/xx3jWyKPxb4s7boVOSDLxu7MP2U0NNAqtWSNMRRF+5sAjMd3RyeftsRT/QWAf2Rs9P/oZikGP0yqpsrWYHgbLcYLqIsgNPr6yOk855gvHB5szRsQggK0d4hl562Lx5T9DvHuM5u2jChsqZ7PvRXmNa+SDBtEB/3fnwIDAQAB';
$txt = 'C5rKZS/HWcEcaSFVyjmmOThYeEaEUwzK7kJf9NK5MkzkWxFH7VoQJS77c19jm9mt1YlhL5RGIficRi2W2+O8yMsT1Jps/7zvtzgV3lMp4ZBWciRg7/UU5CJq/CrVtfxHYrNQKn5eFTpaDX/cENb8u47vxtjZpwPhbNIc7HSCCLVZ0qSUgKCFHS/JNMGnin8rpCOL0ON5nEzmJnyDhlQikTVDTgDYdDyGokOkGnIUrD9JcMQ0sSLQXRk3qT3zfCAamgRWhDUDgUG+7HHoya5nFwjJ+UHq7pfxcu+gFDDWvHqAN1q+ayl1kb7hf0NSYjG0/d/MB2LT5XLxhDgVIQSwGiHRB/1nHIiNJGQnEirtsNcGSl9hXZ7PPaAm5Oo2gadIFh8ro0BKwnlmha6WmRS47sf7De3zlRVeSLWYXJYyniFj9+LumdUOgwWaZHrJXSpQSTj2GQtwLy0yjirkWKJXt2YfpeEXsUpaZib0Rbo8yuruvZqM1aAKtMwGRdBwTvKXECA6uCqizBPBtyNRRAUL7AvwZCKHJQdAY1u4yhKngDYEwO651PPVO/IkKw9CbC4mRpfhD5sIFoqIcFyl0f4ns/USip5gKUofKGv0hStQD8Hq9JIQEvR9M5lkx2KZarudIttZiZKycqMUA4xCHsJetbohBDQwMHY3I0tsKjy3WuiWbzJsuJrQD4qfbp72UOKjD+zdeyTzykdKOPEYvmsEucSlubFrd8jmQlyWjR/3roQCqlhGlZmH7CGf+epSewULOjQP6geUh1Zl2p0CJNBNd8olCqIHFt4AQqb1GhdVLcDpOg63HBGj8O+WiLbM9sE7umDYizmC7ImNIQ0mPRPuBw==';


$d = decryptRsaPublicKey($txt, $publicKey);


var_dump($d);
// string(489) "{"amt":1000.0000,"createTime":1694663267000,"errorCode":"36020","errorMsg":"账户可用余额不足","idCard":"500231198602096055","merchantBatchId":"20230914114746614223","merchantId":1701443988206407682,"merchantOrderId":"202309141147466142231","mobile":"13983211095","orderBatchNo":"202309141147478V2Y1X","orderNo":"202309141147478V2Y1X00001","payeeAcc":"oDely66AF4G-lD17AZTSx6D_rTLk","payeeName":"杨小军","platFee":0.0000,"splitFlag":"0","status":"60","tradeEndTime":1694663267000}"