「宏结算支付国密SM2签名和国密SM4」PHP对接API


最近接触到宏结算的API对接,第一次使用国密算法签名,记录一下遇到的问题。

宏结算使用SM2签名,SM4加密

示例代码只有java版本,没有其他版本,我们使用PHP,于是开始寻找支持的方式,最后openssl支持但是有限制,后来发现和招商银行的签名流程差不多,参考其他关于招商对接的流程,完成了签名和加密对接。

首先是使用PHP SM2签名

拿到手的私钥和公钥是Base64编码的,需要特殊处理一下,另外安装这个sm2加密库,lpilp/guomi,里面涵盖了对接API的全部方法。

下面是PHP版本实现SM2签名和SM4加密。

//根据实际调整引用
require_once __DIR__ . '/../vendor/autoload.php';

use Rtgm\sm\RtSm2;
use Rtgm\sm\RtSm4;


//宏结算接口
class ApplyHJS
{


    private const API_URL = 'api.xxx.com'; //API
    private const API_PRE = '/xxx'; //API前綴
    private const USER_CODE = 'xxx'; //用戶編碼
    private const PK = 'xxx'; //base64私鑰
    private const PUK = 'xxx'; //base64公鑰
    private const BUK = 'xxxx'; //base64業務密鑰


    private $sm4;
    private $sm2;
    private $userId;


    function __construct()
    {

        $this->sm2 = new RtSm2("base64");
        $this->sm4 = new RtSm4(base64_decode(self::BUK));
        $this->userId =  sprintf('%-016s', self::USER_CODE);
    }

    /**
     * 签名&加密:SM2&SM4
     */
    function signAndCrypt($data)
    {

        // base64私钥转二进制
        $privateKey = base64_decode(self::PK);
        // 二进制转十六进制字符串
        $privateKey = unpack("H*", $privateKey)[1];
        //私钥生成签名
        $sign   = $this->sm2->doSign($data, $privateKey, $this->userId);
        // 将base64的签名还原为二进制
        $sign   = base64_decode($sign);
        // 处理二进制数据
        $point  = \FG\ASN1\ASNObject::fromBinary($sign)->getChildren();
        $pointX = $this->formatHex($point[0]->getContent());
        $pointY = $this->formatHex($point[1]->getContent());
        $sign   = $pointX . $pointY;
        $sign   = base64_encode(hex2bin($sign));
        // 替换签名字段
        $data = str_replace('__signature_sigdat__', $sign, $data);
        // 对数据进行对称加密
        $sm4 = new RtSm4(base64_decode(self::BUK));
        $encryptData = $sm4->encrypt($data, 'sm4-cbc',  $this->userId, "base64");

        return $encryptData;
    }

    function decryptSign($decryptData)
    {

        // 返回结果解密
        $json = $this->sm4->decrypt($decryptData, $type = 'sm4-cbc',  $this->userId, $formatInput = 'base64');
        $data = json_decode($json, true);

        return $data;
    }

    function formatHex($dec)
    {
        $hex = gmp_strval(gmp_init($dec, 10), 16);
        $len = strlen($hex);
        if ($len == 64) {
            return $hex;
        }
        if ($len < 64) {
            $hex = str_pad($hex, 64, "0", STR_PAD_LEFT);
        } else {
            $hex = substr($hex, $len - 64, 64);
        }
        return $hex;
    }




}


宏结算平台呢,基本上搜索不到任何相关的文档和手册,拿到手的所谓API对接,也只是一个内部使用的word版本,简单的java示例。对接很麻烦,文档也没有解释和说明。最终在本地部署了Java版本的示例运行起来才确定是可以用。不然都怀疑用不了。


文章作者: 2winter
文章链接: https://2winter.com
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 2winter !