mirror of
https://gitee.com/liuxioabin/fengketrade.git
synced 2026-04-17 12:57:32 +08:00
227 lines
6.4 KiB
PHP
227 lines
6.4 KiB
PHP
<?php
|
||
|
||
namespace addons\shopro\library\ccblife;
|
||
|
||
/**
|
||
* 建行生活URL参数解密类
|
||
* 处理ccbParamSJ参数的DES解密
|
||
*/
|
||
class CcbUrlDecrypt
|
||
{
|
||
/**
|
||
* 解密建行URL参数ccbParamSJ
|
||
* 流程:双层BASE64解码 -> DES解密
|
||
*
|
||
* @param string $ccbParamSJ 加密的参数字符串
|
||
* @param string $serviceId 服务ID(用于生成DES密钥)
|
||
* @return array|false 解密后的参数数组,失败返回false
|
||
*/
|
||
public static function decrypt($ccbParamSJ, $serviceId)
|
||
{
|
||
try {
|
||
// 第一次BASE64解码
|
||
$firstDecode = base64_decode($ccbParamSJ);
|
||
if ($firstDecode === false) {
|
||
throw new \Exception('第一次BASE64解码失败');
|
||
}
|
||
|
||
// 第二次BASE64解码
|
||
$secondDecode = base64_decode($firstDecode);
|
||
if ($secondDecode === false) {
|
||
throw new \Exception('第二次BASE64解码失败');
|
||
}
|
||
|
||
// 获取DES密钥(服务ID前8位)
|
||
$desKey = substr($serviceId, 0, 8);
|
||
|
||
// DES解密
|
||
$decrypted = self::desDecrypt($secondDecode, $desKey);
|
||
if ($decrypted === false) {
|
||
throw new \Exception('DES解密失败');
|
||
}
|
||
|
||
// 解析参数字符串为数组
|
||
parse_str($decrypted, $params);
|
||
|
||
return $params;
|
||
|
||
} catch (\Exception $e) {
|
||
// 记录错误日志
|
||
trace('建行URL参数解密失败: ' . $e->getMessage(), 'error');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* DES解密
|
||
* 使用ECB模式,PKCS5Padding填充
|
||
*
|
||
* @param string $encryptedData 加密的数据
|
||
* @param string $key 密钥(8字节)
|
||
* @return string|false 解密后的数据,失败返回false
|
||
*/
|
||
private static function desDecrypt($encryptedData, $key)
|
||
{
|
||
// 确保密钥长度为8字节
|
||
if (strlen($key) !== 8) {
|
||
return false;
|
||
}
|
||
|
||
// 使用openssl进行DES解密
|
||
$decrypted = openssl_decrypt(
|
||
$encryptedData,
|
||
'DES-ECB',
|
||
$key,
|
||
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
|
||
);
|
||
|
||
if ($decrypted === false) {
|
||
return false;
|
||
}
|
||
|
||
// 移除PKCS5填充
|
||
$decrypted = self::removePKCS5Padding($decrypted);
|
||
|
||
return $decrypted;
|
||
}
|
||
|
||
/**
|
||
* DES加密(用于测试)
|
||
* 使用ECB模式,PKCS5Padding填充
|
||
*
|
||
* @param string $data 待加密的数据
|
||
* @param string $key 密钥(8字节)
|
||
* @return string|false 加密后的数据,失败返回false
|
||
*/
|
||
public static function desEncrypt($data, $key)
|
||
{
|
||
// 确保密钥长度为8字节
|
||
if (strlen($key) !== 8) {
|
||
return false;
|
||
}
|
||
|
||
// 添加PKCS5填充
|
||
$data = self::addPKCS5Padding($data, 8);
|
||
|
||
// 使用openssl进行DES加密
|
||
$encrypted = openssl_encrypt(
|
||
$data,
|
||
'DES-ECB',
|
||
$key,
|
||
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
|
||
);
|
||
|
||
return $encrypted;
|
||
}
|
||
|
||
/**
|
||
* 生成建行URL参数ccbParamSJ(用于测试)
|
||
*
|
||
* @param array $params 参数数组
|
||
* @param string $serviceId 服务ID
|
||
* @return string 加密后的ccbParamSJ参数
|
||
*/
|
||
public static function encrypt($params, $serviceId)
|
||
{
|
||
// 将参数数组转换为查询字符串
|
||
$queryString = http_build_query($params);
|
||
|
||
// 获取DES密钥(服务ID前8位)
|
||
$desKey = substr($serviceId, 0, 8);
|
||
|
||
// DES加密
|
||
$encrypted = self::desEncrypt($queryString, $desKey);
|
||
|
||
// 双层BASE64编码
|
||
$firstEncode = base64_encode($encrypted);
|
||
$secondEncode = base64_encode($firstEncode);
|
||
|
||
return $secondEncode;
|
||
}
|
||
|
||
/**
|
||
* 添加PKCS5填充
|
||
*
|
||
* @param string $text 待填充的文本
|
||
* @param int $blocksize 块大小
|
||
* @return string 填充后的文本
|
||
*/
|
||
private static function addPKCS5Padding($text, $blocksize)
|
||
{
|
||
$pad = $blocksize - (strlen($text) % $blocksize);
|
||
return $text . str_repeat(chr($pad), $pad);
|
||
}
|
||
|
||
/**
|
||
* 移除PKCS5填充
|
||
*
|
||
* @param string $text 已填充的文本
|
||
* @return string 移除填充后的文本
|
||
*/
|
||
private static function removePKCS5Padding($text)
|
||
{
|
||
$pad = ord($text[strlen($text) - 1]);
|
||
if ($pad > strlen($text)) {
|
||
return false;
|
||
}
|
||
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
|
||
return false;
|
||
}
|
||
return substr($text, 0, -1 * $pad);
|
||
}
|
||
|
||
/**
|
||
* 解析建行跳转URL中的所有参数
|
||
* 处理URL中的ccbParamSJ和其他参数
|
||
*
|
||
* @param string $url 完整的URL或查询字符串
|
||
* @param string $serviceId 服务ID
|
||
* @return array 包含所有参数的数组
|
||
*/
|
||
public static function parseUrl($url, $serviceId)
|
||
{
|
||
// 解析URL获取查询参数
|
||
$urlParts = parse_url($url);
|
||
$queryString = isset($urlParts['query']) ? $urlParts['query'] : $url;
|
||
|
||
// 解析查询字符串
|
||
parse_str($queryString, $params);
|
||
|
||
// 如果存在ccbParamSJ参数,进行解密
|
||
if (isset($params['ccbParamSJ']) && !empty($params['ccbParamSJ'])) {
|
||
$decryptedParams = self::decrypt($params['ccbParamSJ'], $serviceId);
|
||
if ($decryptedParams !== false) {
|
||
// 合并解密后的参数
|
||
$params = array_merge($params, $decryptedParams);
|
||
}
|
||
}
|
||
|
||
return $params;
|
||
}
|
||
|
||
/**
|
||
* 生成测试URL
|
||
* 用于生成包含加密参数的完整URL
|
||
*
|
||
* @param string $baseUrl 基础URL
|
||
* @param array $encryptedParams 需要加密的参数
|
||
* @param array $plainParams 明文参数
|
||
* @param string $serviceId 服务ID
|
||
* @return string 完整的URL
|
||
*/
|
||
public static function generateUrl($baseUrl, $encryptedParams, $plainParams, $serviceId)
|
||
{
|
||
// 加密参数
|
||
$ccbParamSJ = self::encrypt($encryptedParams, $serviceId);
|
||
|
||
// 合并所有参数
|
||
$allParams = array_merge($plainParams, ['ccbParamSJ' => $ccbParamSJ]);
|
||
|
||
// 构建查询字符串
|
||
$queryString = http_build_query($allParams);
|
||
|
||
// 拼接URL
|
||
$separator = strpos($baseUrl, '?') === false ? '?' : '&';
|
||
return $baseUrl . $separator . $queryString;
|
||
}
|
||
} |