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