最近遇到与java对接的时候接收的base64数据,SM4解密一部分正常、一部分乱码乱码的问题,最后仔细看了接收到的base64数据,发现是PHP接收url中的数据问题。
一、下面展示下解决问题之前的问题:
首先、把需要加密的json串整理出来,进行SM4加密

其次、将加密后的base64串添加到接口请求工具中data参数值中

再次、打印PHP接收到的base64数据串

最后、用接收到的数据串进行SM4解密,打印出来发现只解析了前面一点点,后面的是乱码

解决思路:
重点在以下几点:
1、接收到的base64数据;
2、lpilp/guomi扩展包中的SM4加密解密的方法;
思考:扩展包的加密解密方法肯定是没问题的,但是也不保证没有问题,细扒代码,发现是在解密base64_decode数据后,用解密方法解出来后就有问题了,解密方法之前用过,可以肯定没有问题,那就是传入的解密数据有问题了。
发现问题:仔细一看打印出来的加密后的数据和PHP接收到的数据不一样,接收到的数据中有空格。
原因是:+号在 URL 中被浏览器/前端自动转换成空格(空格编码成 %20),而后端 request()->all() 又把空格保留下来,最终造成 Base64 字符串被破坏。
解决办法:将接收到的数据中的空格转换成+号还原。再去调用解密方法
$cipher = str_replace(' ', '+', $cipher); // 把空格全部还原成 +
$plain = (new RtSm4($key))->decrypt($cipher, 'sm4-ecb', '', 'base64');
将数据顺利解密,出来的结果如下:

与加密数据一致。
附件:SM4和SM2加密解密的公共调用方法
<?php
namespace App\Support;
use Rtgm\sm\RtSm4;
use Rtgm\sm\RtSm2;
use Rtgm\util\MyAsn1;
class GuomiHelper
{
/**
* 生成安全随机数(不超过128位)
*
* @param int $length 随机数长度,最大128位
* @return string
*/
public static function generateNonce($length = 32)
{
$length = min($length, 128);
$bytes = random_bytes(ceil($length / 2));
return substr(bin2hex($bytes), 0, $length);
}
/**
* 使用 key 进行 SM4 ECB 加密(在用)
*
* @param string $data
* @param string $key
* @return string
*/
public static function sm4EcbEncrypt($data, $key)
{
//dump($data);
// 使用示例
// $key = hex2bin("30ca0d013a9a782fe4c6b4c30d3e208c");
// $data = "18900112233"; 运行结果;571d9decc557bd9d466f6c90df328bb4
$key = hex2bin($key);//将十六进制字符串转换为二进制数据
$sm4 = new RtSm4($key);
$ciphertext = $sm4->encrypt($data, 'sm4-ecb','','base64');
return $ciphertext;
}
/**
* 使用 key 进行 SM4 ECB 解密(在用)
*
* @param string $data
* @param string $key
* @return string
*/
public static function sm4EcbDecrypt($data, $key)
{
// 移除base64编码中的空格换行
$key = hex2bin($key);//将十六进制字符串转换为二进制数据 $data, $type = 'sm4', $iv = '', $formatInput = 'hex'
$sm4 = new RtSm4($key);
$plaintext = $sm4->decrypt($data, 'sm4-ecb','','base64');
return $plaintext;
}
/**
* 账户公钥SM2加密(在用)
*
* @param string $data
* @param string $publicKey
* @return string
*/
public static function rtSm2Encrypt($plaintext, $publicKeyBase64)
{
// 从DER格式中提取公钥内容(去除ASN.1头部)
$publicKeyHex = MyAsn1::decode($publicKeyBase64,'base64');
// 创建SM2实例
$sm2 = new RtSm2();
// 执行加密(使用公钥的十六进制格式)
$ciphertextBin = $sm2->doEncrypt($plaintext, $publicKeyHex[1]);
return '04'.$ciphertextBin;
}
/**
* 账户私钥SM2解密(在用)
*/
public static function rtSm2Decrypt($ciphertext, $privateKey) {
// 创建SM2实例
$sm2 = new RtSm2();
$m2DecryptData = $sm2->doDecrypt($ciphertext,$privateKey);
return $m2DecryptData;
}
/**
* 账户公钥SM2验签
* 使用 SM2 非对称加密算法结合 SM3 哈希算法对给定的数据进行签名验证
* @param string $publicKeyString:十六进制字符串表示的 SM2 公钥。
* @param string $notifyString:需要验证签名的原始数据字符串。
* @param string $signString:十六进制字符串表示的签名数据。
* @return string
*/
public static function sm2WithSM3Verify($publicKeyString, $notifyString, $signString)
{
$sm2 = new RtSm2();
$result = $sm2->verifySign($notifyString, $signString, $publicKeyString);
return $result;
}
}