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

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

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

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

首先是使用PHP SM2签名

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//根据实际调整引用
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版本的示例运行起来才确定是可以用。不然都怀疑用不了。