PHP如何解决使用国密SM4解密Base64数据错误问题?(基于lpilp/guomi)

发布于:2025-09-04 ⋅ 阅读:(16) ⋅ 点赞:(0)

最近遇到与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;
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到