privateKey = $this->loadKey($privateKey, 'private'); $this->publicKey = $this->loadKey($publicKey, 'public'); $this->encryptType = $encryptType; } /** * 加载密钥 */ private function loadKey($key, $type) { // 如果是文件路径 if (is_file($key)) { $key = file_get_contents($key); } if ($type === 'private') { return openssl_pkey_get_private($key); } else { return openssl_pkey_get_public($key); } } /** * 构建请求(包含签名和加密) * * @param array $bizContent 业务参数(明文) * @param array $extraParams 额外的公共参数(会自动过滤jrgw开头的) * @return array 完整的请求参数 */ public function buildRequest($bizContent, $extraParams = []) { $requestParams = []; // 1. 处理业务内容 if ($this->encryptType === self::ENCRYPT_3DES_RSA) { // 生成随机3DES密钥 $this->secretKey = $this->generate3DesKey(); // 加密业务参数 $encryptedBiz = $this->encrypt3Des(json_encode($bizContent), $this->secretKey); // 使用RSA加密3DES密钥 $encryptedKey = $this->encryptRsa($this->secretKey); $requestParams['encrypt'] = $encryptedKey; $requestParams['biz-content'] = $encryptedBiz; } else { // 明文模式 $requestParams['biz-content'] = json_encode($bizContent); } // 合并额外参数(只保留以jrgw开头的) foreach ($extraParams as $key => $value) { if (strpos($key, 'jrgw') === 0 && $value !== '' && $value !== null) { $requestParams[$key] = $value; } } // 2. 生成签名 $sign = $this->generateSign($requestParams); return $sign; } /** * 解析响应(包含验签和解密) * * @param array $responseParams 响应参数 * @return array 解密后的业务参数 * @throws Exception */ public function parseResponse($responseParams) { // 1. 验签 $sign = isset($responseParams['sign']) ? $responseParams['sign'] : ''; if (!$this->verifySign($responseParams, $sign)) { throw new Exception('响应签名验证失败'); } // 2. 解密 if ($this->encryptType === self::ENCRYPT_3DES_RSA) { if (!isset($responseParams['encrypt']) || !isset($responseParams['biz-content'])) { throw new Exception('加密响应缺少必要参数'); } // 使用私钥解密3DES密钥 $secretKey = $this->decryptRsa($responseParams['encrypt']); // 使用3DES解密业务内容 $bizContent = $this->decrypt3Des($responseParams['biz-content'], $secretKey); return json_decode($bizContent, true); } else { // 明文模式 if (!isset($responseParams['biz-content'])) { throw new Exception('响应缺少biz-content参数'); } return json_decode($responseParams['biz-content'], true); } } /** * 生成签名 * * @param array $params 所有参数(包含biz-content和jrgw开头的参数) * @return string 签名值 */ public function generateSign($params) { // 1. 筛选:获取jrgw开头的参数和biz-content $signParams = []; foreach ($params as $key => $value) { if ((strpos($key, 'jrgw') === 0 || $key === 'biz-content') && $value !== '' && $value !== null) { $signParams[$key] = $value; } } // 2. 排序:按ASCII码升序 ksort($signParams, SORT_STRING); // 3. 拼接:key=value&key=value $stringToSign = []; foreach ($signParams as $key => $value) { $stringToSign[] = $key . '=' . $value; } $stringToSign = implode('&', $stringToSign); // 4. 使用私钥进行SHA256withRSA签名 $signature = ''; openssl_sign($stringToSign, $signature, $this->privateKey, self::SIGN_ALGO); return base64_encode($signature); } /** * 验证签名 * * @param array $params 所有参数(不包含sign) * @param string $sign 待验证的签名值 * @return bool */ public function verifySign($params, $sign) { // 1. 筛选:获取jrgw开头的参数和biz-content(除去sign) $signParams = []; foreach ($params as $key => $value) { if ($key === 'sign') { continue; } if ((strpos($key, 'jrgw') === 0 || $key === 'biz-content') && $value !== '' && $value !== null) { $signParams[$key] = $value; } } // 2. 排序:按ASCII码升序 ksort($signParams, SORT_STRING); // 3. 拼接 $stringToVerify = []; foreach ($signParams as $key => $value) { $stringToVerify[] = $key . '=' . $value; } $stringToVerify = implode('&', $stringToVerify); // 4. 验签 $signature = base64_decode($sign); $result = openssl_verify($stringToVerify, $signature, $this->publicKey, self::SIGN_ALGO); return $result === 1; } /** * 生成3DES密钥(24字节) * * @return string */ private function generate3DesKey() { return substr(base64_encode(openssl_random_pseudo_bytes(24)), 0, 24); } /** * 3DES加密 * * @param string $data 明文 * @param string $key 密钥 * @return string 密文(base64编码) */ private function encrypt3Des($data, $key) { $iv = substr($key, 0, 8); $encrypted = openssl_encrypt($data, 'DES-EDE3-CBC', $key, OPENSSL_RAW_DATA, $iv); return base64_encode($encrypted); } /** * 3DES解密 * * @param string $encryptedData 密文(base64编码) * @param string $key 密钥 * @return string 明文 */ private function decrypt3Des($encryptedData, $key) { $iv = substr($key, 0, 8); $decrypted = openssl_decrypt(base64_decode($encryptedData), 'DES-EDE3-CBC', $key, OPENSSL_RAW_DATA, $iv); return $decrypted; } /** * RSA加密(用于加密3DES密钥) * * @param string $data 明文 * @return string 密文(base64编码) * @throws Exception */ private function encryptRsa($data) { $encrypted = ''; if (!openssl_public_encrypt($data, $encrypted, $this->publicKey)) { throw new Exception('RSA加密失败'); } return base64_encode($encrypted); } /** * RSA解密(用于解密3DES密钥) * * @param string $encryptedData 密文(base64编码) * @return string 明文 * @throws Exception */ private function decryptRsa($encryptedData) { $decrypted = ''; if (!openssl_private_decrypt(base64_decode($encryptedData), $decrypted, $this->privateKey)) { throw new Exception('RSA解密失败'); } return $decrypted; } /** * 获取当前时间(格式:yyyyMMddHHmmssSSS) * * @return string */ private function getCurrentTimeMillis() { $now = new DateTime(); return $now->format('YmdHis') . sprintf('%03d', floor(microtime(true) * 1000) % 1000); } public function request(string $url,array $bizContent = []) { $http = new Http(); $extraParams = [ 'jrgw-request-time' => $this->getCurrentTimeMillis(), 'jrgw-enterprise-user-id' => '', 'jrgw-user-id-type' => '0', 'gw-encrypt-type' => 'NONE', 'gw-sign-type' => 'SHA256withRSA', ]; $extraParams['gw-sign'] = $this->buildRequest($bizContent,$extraParams); $res = $http->postRequest('https://api.jddglobal.com'.$url,$bizContent,$extraParams); $res = json_decode($res, true); if($res && $res['code'] == 00000){ return $res['responseData']; }else{ throw new ValidateException('错误码'.$res['errCode'].'错误信息'.$res['errDesc']); } } public function sendBrokerage(array $bizContent = []) { return $this->request('/smapi/v1/ffp-common/submitNormalSalaryApi',$bizContent); } }