最近接触到宏结算的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版本的示例运行起来才确定是可以用。不然都怀疑用不了。