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);
 | ||
|     }
 | ||
| 
 | ||
| } |