253 lines
7.5 KiB
PHP
253 lines
7.5 KiB
PHP
|
<?php
|
|||
|
|
|||
|
namespace app\service\util;
|
|||
|
|
|||
|
use app\exception\LogicException;
|
|||
|
use app\util\sm\cmb\Sm;
|
|||
|
use app\util\StringUtil;
|
|||
|
|
|||
|
class CmbLifeUtils
|
|||
|
{
|
|||
|
protected static string $cmbLifeProtocolPrefix = 'cmblife://';
|
|||
|
|
|||
|
/**
|
|||
|
* 生成掌上生活协议,带有签名
|
|||
|
* @param string $funcName
|
|||
|
* @param array $params
|
|||
|
* @return string
|
|||
|
* @throws LogicException
|
|||
|
*/
|
|||
|
public static function genProtocol(string $funcName, array $params): string
|
|||
|
{
|
|||
|
if (empty($funcName)) {
|
|||
|
throw new LogicException('funcName不能为空');
|
|||
|
}
|
|||
|
// 处理公共参数
|
|||
|
$cmbConfig = config('cmb');
|
|||
|
$params['mid'] = $cmbConfig['mid'];
|
|||
|
$params['aid'] = $cmbConfig['aid'];
|
|||
|
$params['date'] = date('YmdHis');
|
|||
|
$params['random'] = StringUtil::generateRandomString(32);// 随机数
|
|||
|
$params['keyAlias'] = 'CO_PUB_KEY_SM2';
|
|||
|
$params['cmbKeyAlias'] = 'SM2_CMBLIFE';
|
|||
|
$signBody = self::assembleProtocol($funcName, $params, false); // 不需要urlEncode
|
|||
|
$signKey = $cmbConfig['merchant_sm2_pri_key'];
|
|||
|
$sign = Sm::sign($signKey, $signBody); //sm2
|
|||
|
$params['sign'] = $sign;
|
|||
|
return self::assembleProtocol($funcName, $params, true);
|
|||
|
}
|
|||
|
|
|||
|
/***
|
|||
|
* 拼接掌上生活协议
|
|||
|
* @param String $funcName
|
|||
|
* @param array $params
|
|||
|
* @param bool $isUrlEncode
|
|||
|
* @return string
|
|||
|
*/
|
|||
|
public static function assembleProtocol(string $funcName, array $params, bool $isUrlEncode)
|
|||
|
{
|
|||
|
$prefix = self::$cmbLifeProtocolPrefix . $funcName;
|
|||
|
return self::getAssembleUrl($prefix, $params, $isUrlEncode);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 拼接签名字符串
|
|||
|
* @param String $prefix
|
|||
|
* @param String $queryString
|
|||
|
* @return String
|
|||
|
*/
|
|||
|
public static function assembleUrl(string $prefix, string $queryString): string
|
|||
|
{
|
|||
|
if (empty($prefix)) {
|
|||
|
return $queryString;
|
|||
|
} else {
|
|||
|
$char = strpos($prefix, '?') !== false ? '&' : '?';
|
|||
|
return $prefix . $char . $queryString;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 拼接签名字符串
|
|||
|
* @param string $prefix
|
|||
|
* @param array $params
|
|||
|
* @param bool $isUrlEncode
|
|||
|
* @return String
|
|||
|
*/
|
|||
|
public static function getAssembleUrl(string $prefix, array $params, bool $isUrlEncode): string
|
|||
|
{
|
|||
|
return self::assembleUrl($prefix, self::mapToQueryString($params, true, $isUrlEncode));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* 参数转为queryString
|
|||
|
* @param array $params
|
|||
|
* @param bool $isSort 是否排序
|
|||
|
* @param bool $isUrlEncode 是否需要urlEncode
|
|||
|
* @return string
|
|||
|
*/
|
|||
|
public static function mapToQueryString(array $params, bool $isSort, bool $isUrlEncode = true): string
|
|||
|
{
|
|||
|
if ($isSort) {
|
|||
|
ksort($params);
|
|||
|
}
|
|||
|
$queryString = '';
|
|||
|
foreach ($params as $key => $value) {
|
|||
|
if (empty($value)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
if ($isUrlEncode) {
|
|||
|
$value = urlencode($value);
|
|||
|
}
|
|||
|
$queryString .= $key . '=' . $value . '&';
|
|||
|
}
|
|||
|
return trim($queryString, '&');
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 加密
|
|||
|
* @param string $encryptBody
|
|||
|
* @param string $encryptKey 加密使用的key SM2公钥
|
|||
|
* @return string
|
|||
|
* @throws LogicException
|
|||
|
*/
|
|||
|
public static function encrypt(string $encryptBody, string $encryptKey)
|
|||
|
{
|
|||
|
if (empty($encryptBody)) {
|
|||
|
throw new LogicException('报文不能为空');
|
|||
|
}
|
|||
|
if (empty($encryptKey)) {
|
|||
|
throw new LogicException('公钥不能为空');
|
|||
|
}
|
|||
|
return Sm::sm4Encrypt($encryptKey, $encryptBody);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 解密
|
|||
|
* @param string $encryptBody
|
|||
|
* @param string $decryptKey 解密使用的Key SM2私钥
|
|||
|
* @throws LogicException
|
|||
|
*/
|
|||
|
public static function decrypt(string $encryptBody, string $decryptKey)
|
|||
|
{
|
|||
|
if (empty($encryptBody)) {
|
|||
|
throw new LogicException('报文不能为空!');
|
|||
|
}
|
|||
|
if (empty($decryptKey)) {
|
|||
|
throw new LogicException('秘钥不能为空!');
|
|||
|
}
|
|||
|
return json_decode(Sm::sm4decrypt($decryptKey, $encryptBody), true);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 签名
|
|||
|
* @param string $signBody
|
|||
|
* @param string $signKey
|
|||
|
* @param string $algorithmEnum
|
|||
|
* @return string
|
|||
|
* @throws LogicException
|
|||
|
*/
|
|||
|
public static function sign(string $signBody, string $signKey)
|
|||
|
{
|
|||
|
if (empty($signBody)) {
|
|||
|
throw new LogicException('待签名数据不能为空!');
|
|||
|
}
|
|||
|
if (empty($signKey)) {
|
|||
|
throw new LogicException('私钥不能为空');
|
|||
|
}
|
|||
|
return Sm::sign($signKey, $signBody);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 验签,国密算法SM3WithSM2
|
|||
|
* @param string $verifyBody
|
|||
|
* @param string $sign
|
|||
|
* @param string $verifyKey sm2公钥
|
|||
|
* @return bool
|
|||
|
* @throws LogicException
|
|||
|
*/
|
|||
|
public static function verify(string $verifyBody, string $sign, string $verifyKey)
|
|||
|
{
|
|||
|
if (empty($verifyBody)) {
|
|||
|
throw new LogicException('验签数据不能为空');
|
|||
|
}
|
|||
|
if (empty($sign)) {
|
|||
|
throw new LogicException("签名不能为空");
|
|||
|
}
|
|||
|
if (empty($verifyKey)) {
|
|||
|
throw new LogicException("公钥不能为空");
|
|||
|
}
|
|||
|
return Sm::verify($verifyKey, $sign, $verifyBody);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 对请求签名
|
|||
|
* @param string $funcName
|
|||
|
* @param array $params
|
|||
|
* @param string $signKey sm2 私钥
|
|||
|
* @param string $signAlgorithm SM3WithSM2
|
|||
|
* @return string
|
|||
|
*/
|
|||
|
public static function signForRequest(string $funcName, array $params, string $signKey, string $signAlgorithm = 'SM3WithSM2')
|
|||
|
{
|
|||
|
if (!empty($funcName) && !strpos($funcName, '.json')) {
|
|||
|
$funcName = $funcName . '.json';
|
|||
|
}
|
|||
|
$assembleUrl = self::getAssembleUrl($funcName, $params, false);
|
|||
|
return self::sign($assembleUrl, $signKey);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 对响应签名
|
|||
|
* @param array $params
|
|||
|
* @return string
|
|||
|
* @throws LogicException
|
|||
|
*/
|
|||
|
public static function signForResponse(array $params)
|
|||
|
{
|
|||
|
if (empty($params)) {
|
|||
|
throw new LogicException('参数不能为空');
|
|||
|
}
|
|||
|
$cmbConfig = config('cmb');
|
|||
|
$signKey = $cmbConfig['merchant_sm2_pri_key'];
|
|||
|
$assembleUrl = self::getAssembleUrl('', $params, false);
|
|||
|
unset($params['sign']);
|
|||
|
return self::sign($assembleUrl, $signKey);
|
|||
|
}
|
|||
|
/**
|
|||
|
* 对响应验签
|
|||
|
* @param array $data
|
|||
|
* @return bool true 验签成功 false 验签失败
|
|||
|
* @throws LogicException
|
|||
|
*/
|
|||
|
public static function verifyForResponse(array $data): bool
|
|||
|
{
|
|||
|
if (empty($data)) {
|
|||
|
throw new LogicException('参数不能为空');
|
|||
|
}
|
|||
|
$cmbConfig = config('cmb');
|
|||
|
$verifyKey = $cmbConfig['merchant_sm2_pub_key'];
|
|||
|
$sign = $data['sign'];
|
|||
|
unset($data['sign']);
|
|||
|
return self::verify(self::getAssembleUrl('', $data, false), $sign, $verifyKey);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @param array $params
|
|||
|
* @param string $verifyKey 验签所使用的Key,为掌上生活公钥
|
|||
|
* @return bool
|
|||
|
* @throws LogicException
|
|||
|
*/
|
|||
|
public static function verifyForRequest(array $params, string $verifyKey): bool
|
|||
|
{
|
|||
|
if (empty($params)) {
|
|||
|
throw new LogicException('参数不能为空');
|
|||
|
}
|
|||
|
$sign = $params['sign'];
|
|||
|
unset($params['sign']);
|
|||
|
$assembleUrl = self::getAssembleUrl('', $params, false);
|
|||
|
return self::verify($assembleUrl, $sign, $verifyKey);
|
|||
|
}
|
|||
|
|
|||
|
}
|