新增
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Alipay\EasySDK\Kernel;
|
||||
|
||||
|
||||
class AlipayConstants
|
||||
{
|
||||
/**
|
||||
* Config配置参数Key值
|
||||
*/
|
||||
const PROTOCOL_CONFIG_KEY = "protocol";
|
||||
const HOST_CONFIG_KEY = "gatewayHost";
|
||||
const ALIPAY_CERT_PATH_CONFIG_KEY = "alipayCertPath";
|
||||
const MERCHANT_CERT_PATH_CONFIG_KEY = "merchantCertPath";
|
||||
const ALIPAY_ROOT_CERT_PATH_CONFIG_KEY = "alipayRootCertPath";
|
||||
const SIGN_TYPE_CONFIG_KEY = "signType";
|
||||
const NOTIFY_URL_CONFIG_KEY = "notifyUrl";
|
||||
|
||||
/**
|
||||
* 与网关HTTP交互中涉及到的字段值
|
||||
*/
|
||||
const BIZ_CONTENT_FIELD = "biz_content";
|
||||
const ALIPAY_CERT_SN_FIELD = "alipay_cert_sn";
|
||||
const SIGN_FIELD = "sign";
|
||||
const BODY_FIELD = "http_body";
|
||||
const NOTIFY_URL_FIELD = "notify_url";
|
||||
const METHOD_FIELD = "method";
|
||||
const RESPONSE_SUFFIX = "_response";
|
||||
const ERROR_RESPONSE = "error_response";
|
||||
const SDK_VERSION = "alipay-easysdk-php-2.2.3";
|
||||
|
||||
/**
|
||||
* 默认字符集编码,EasySDK统一固定使用UTF-8编码,无需用户感知编码,用户面对的总是String而不是bytes
|
||||
*/
|
||||
const DEFAULT_CHARSET = "UTF-8";
|
||||
|
||||
/**
|
||||
* 默认的签名算法,EasySDK统一固定使用RSA2签名算法(即SHA_256_WITH_RSA),但此参数依然需要用户指定以便用户感知,因为在开放平台接口签名配置界面中需要选择同样的算法
|
||||
*/
|
||||
const RSA2 = "RSA2";
|
||||
|
||||
/**
|
||||
* RSA2对应的真实签名算法名称
|
||||
*/
|
||||
const SHA_256_WITH_RSA = "SHA256WithRSA";
|
||||
|
||||
/**
|
||||
* RSA2对应的真实非对称加密算法名称
|
||||
*/
|
||||
const RSA = "RSA";
|
||||
|
||||
/**
|
||||
* 申请生成的重定向网页的请求类型,GET表示生成URL
|
||||
*/
|
||||
const GET = "GET";
|
||||
|
||||
/**
|
||||
* 申请生成的重定向网页的请求类型,POST表示生成form表单
|
||||
*/
|
||||
const POST = "POST";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Alipay\EasySDK\Kernel;
|
||||
|
||||
|
||||
use Alipay\EasySDK\Kernel\Util\AntCertificationUtil;
|
||||
use http\Exception\RuntimeException;
|
||||
|
||||
class CertEnvironment
|
||||
{
|
||||
private $rootCertSN;
|
||||
|
||||
private $merchantCertSN;
|
||||
|
||||
private $cachedAlipayPublicKey;
|
||||
|
||||
/**
|
||||
* 构造证书运行环境
|
||||
* @param $merchantCertPath string 商户公钥证书路径
|
||||
* @param $alipayCertPath string 支付宝公钥证书路径
|
||||
* @param $alipayRootCertPath string 支付宝根证书路径
|
||||
*/
|
||||
public function certEnvironment($merchantCertPath, $alipayCertPath, $alipayRootCertPath)
|
||||
{
|
||||
if (empty($merchantCertPath) || empty($alipayCertPath) || empty($alipayRootCertPath)) {
|
||||
throw new RuntimeException("证书参数merchantCertPath、alipayCertPath或alipayRootCertPath设置不完整。");
|
||||
}
|
||||
$antCertificationUtil = new AntCertificationUtil();
|
||||
$this->rootCertSN = $antCertificationUtil->getRootCertSN($alipayRootCertPath);
|
||||
$this->merchantCertSN = $antCertificationUtil->getCertSN($merchantCertPath);
|
||||
$this->cachedAlipayPublicKey = $antCertificationUtil->getPublicKey($alipayCertPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRootCertSN()
|
||||
{
|
||||
return $this->rootCertSN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMerchantCertSN()
|
||||
{
|
||||
return $this->merchantCertSN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCachedAlipayPublicKey()
|
||||
{
|
||||
return $this->cachedAlipayPublicKey;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Alipay\EasySDK\Kernel;
|
||||
|
||||
use AlibabaCloud\Tea\Model;
|
||||
|
||||
class Config extends Model
|
||||
{
|
||||
public $protocol;
|
||||
public $gatewayHost;
|
||||
public $appId;
|
||||
public $signType;
|
||||
public $alipayPublicKey;
|
||||
public $merchantPrivateKey;
|
||||
public $merchantCertPath;
|
||||
public $alipayCertPath;
|
||||
public $alipayRootCertPath;
|
||||
public $merchantCertSN;
|
||||
public $alipayCertSN;
|
||||
public $alipayRootCertSN;
|
||||
public $notifyUrl;
|
||||
public $encryptKey;
|
||||
public $httpProxy;
|
||||
public $ignoreSSL;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Alipay\EasySDK\Kernel;
|
||||
|
||||
use Alipay\EasySDK\Kernel\Util\AES;
|
||||
use Alipay\EasySDK\Kernel\Util\JsonUtil;
|
||||
use Alipay\EasySDK\Kernel\Util\PageUtil;
|
||||
use Alipay\EasySDK\Kernel\Util\SignContentExtractor;
|
||||
use Alipay\EasySDK\Kernel\Util\Signer;
|
||||
use AlibabaCloud\Tea\FileForm\FileForm;
|
||||
use AlibabaCloud\Tea\FileForm\FileForm\FileField;
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class EasySDKKernel
|
||||
{
|
||||
|
||||
private $config;
|
||||
|
||||
private $optionalTextParams;
|
||||
|
||||
private $optionalBizParams;
|
||||
|
||||
private $textParams;
|
||||
|
||||
private $bizParams;
|
||||
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function injectTextParam($key, $value)
|
||||
{
|
||||
if ($key != null) {
|
||||
$this->optionalTextParams[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function injectBizParam($key, $value)
|
||||
{
|
||||
if ($key != null) {
|
||||
$this->optionalBizParams[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取时间戳,格式yyyy-MM-dd HH:mm:ss
|
||||
* @return false|string 当前时间戳
|
||||
*/
|
||||
public function getTimestamp()
|
||||
{
|
||||
return date("Y-m-d H:i:s");
|
||||
}
|
||||
|
||||
public function getConfig($key)
|
||||
{
|
||||
return $this->config->$key;
|
||||
}
|
||||
|
||||
public function getSdkVersion()
|
||||
{
|
||||
return AlipayConstants::SDK_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将业务参数和其他额外文本参数按www-form-urlencoded格式转换成HTTP Body中的字节数组,注意要做URL Encode
|
||||
*
|
||||
* @param $bizParams array 业务参数
|
||||
* @return false|string|null
|
||||
*/
|
||||
public function toUrlEncodedRequestBody($bizParams)
|
||||
{
|
||||
$sortedMap = $this->getSortedMap(null, $bizParams, null);
|
||||
if (empty($sortedMap)) {
|
||||
return null;
|
||||
}
|
||||
return $this->buildQueryString($sortedMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析网关响应内容,同时将API的接口名称和响应原文插入到响应数组的method和body字段中
|
||||
*
|
||||
* @param $response ResponseInterface HTTP响应
|
||||
* @param $method string 调用的OpenAPI的接口名称
|
||||
* @return array 响应的结果
|
||||
*/
|
||||
public function readAsJson($response, $method)
|
||||
{
|
||||
$responseBody = (string)$response->getBody();
|
||||
$map = [];
|
||||
$map[AlipayConstants::BODY_FIELD] = $responseBody;
|
||||
$map[AlipayConstants::METHOD_FIELD] = $method;
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机分界符,用于multipart格式的HTTP请求Body的多个字段间的分隔
|
||||
*
|
||||
* @return string 随机分界符
|
||||
*/
|
||||
public function getRandomBoundary()
|
||||
{
|
||||
return date("Y-m-d H:i:s") . '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 将其他额外文本参数和文件参数按multipart/form-data格式转换成HTTP Body中
|
||||
* @param $textParams
|
||||
* @param $fileParams
|
||||
* @param $boundary
|
||||
* @return false|string
|
||||
*/
|
||||
public function toMultipartRequestBody($textParams, $fileParams, $boundary)
|
||||
{
|
||||
$this->textParams = $textParams;
|
||||
if ($textParams != null && $this->optionalTextParams != null) {
|
||||
$this->textParams = array_merge($textParams, $this->optionalTextParams);
|
||||
} else if ($textParams == null) {
|
||||
$this->textParams = $this->optionalTextParams;
|
||||
}
|
||||
if (count($fileParams) > 0) {
|
||||
|
||||
foreach ($fileParams as $key => $value) {
|
||||
$fileField = new FileField();
|
||||
$fileField->filename = $value;
|
||||
$fileField->contentType = 'multipart/form-data;charset=utf-8;boundary=' . $boundary;
|
||||
$fileField->content = new Stream(fopen($value, 'r'));
|
||||
$this->textParams[$key] = $fileField;
|
||||
}
|
||||
}
|
||||
$stream = FileForm::toFileForm($this->textParams, $boundary);
|
||||
|
||||
// do {
|
||||
// $readLength = $stream->read(1024);
|
||||
// } while (0 != $readLength);
|
||||
return $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成页面类请求所需URL或Form表单
|
||||
* @param $method
|
||||
* @param $systemParams
|
||||
* @param $bizParams
|
||||
* @param $textParams
|
||||
* @param $sign
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function generatePage($method, $systemParams, $bizParams, $textParams, $sign)
|
||||
{
|
||||
if ($method == AlipayConstants::GET) {
|
||||
//采集并排序所有参数
|
||||
$sortedMap = $this->getSortedMap($systemParams, $bizParams, $textParams);
|
||||
$sortedMap[AlipayConstants::SIGN_FIELD] = $sign;
|
||||
return $this->getGatewayServerUrl() . '?' . $this->buildQueryString($sortedMap);
|
||||
} elseif ($method == AlipayConstants::POST) {
|
||||
//采集并排序所有参数
|
||||
$sortedMap = $this->getSortedMap($systemParams, $this->bizParams, $this->textParams);
|
||||
$sortedMap[AlipayConstants::SIGN_FIELD] = $sign;
|
||||
$pageUtil = new PageUtil();
|
||||
return $pageUtil->buildForm($this->getGatewayServerUrl(), $sortedMap);
|
||||
} else {
|
||||
throw new \Exception("不支持" . $method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商户应用公钥证书序列号,从证书模式运行时环境对象中直接读取
|
||||
*
|
||||
* @return mixed 商户应用公钥证书序列号
|
||||
*/
|
||||
public function getMerchantCertSN()
|
||||
{
|
||||
return $this->config->merchantCertSN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从响应Map中提取支付宝公钥证书序列号
|
||||
*
|
||||
* @param array $respMap string 响应Map
|
||||
* @return mixed 支付宝公钥证书序列号
|
||||
*/
|
||||
public function getAlipayCertSN(array $respMap)
|
||||
{
|
||||
if (!empty($this->config->merchantCertSN)) {
|
||||
$body = json_decode($respMap[AlipayConstants::BODY_FIELD]);
|
||||
$alipayCertSN = $body->alipay_cert_sn;
|
||||
return $alipayCertSN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付宝根证书序列号,从证书模式运行时环境对象中直接读取
|
||||
*
|
||||
* @return mixed 支付宝根证书序列号
|
||||
*/
|
||||
public function getAlipayRootCertSN()
|
||||
{
|
||||
return $this->config->alipayRootCertSN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是证书模式
|
||||
* @return mixed true:是;false:不是
|
||||
*/
|
||||
public function isCertMode()
|
||||
{
|
||||
return $this->config->merchantCertSN;
|
||||
}
|
||||
|
||||
public function extractAlipayPublicKey($alipayCertSN)
|
||||
{
|
||||
// PHP 版本只存储一个版本支付宝公钥
|
||||
return $this->config->alipayPublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*
|
||||
* @param $respMap string 响应内容,可以从中提取出sign和body
|
||||
* @param $alipayPublicKey string 支付宝公钥
|
||||
* @return bool true:验签通过;false:验签不通过
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function verify($respMap, $alipayPublicKey)
|
||||
{
|
||||
$resp = json_decode($respMap[AlipayConstants::BODY_FIELD], true);
|
||||
$sign = $resp[AlipayConstants::SIGN_FIELD];
|
||||
$signContentExtractor = new SignContentExtractor();
|
||||
$content = $signContentExtractor->getSignSourceData($respMap[AlipayConstants::BODY_FIELD],
|
||||
$respMap[AlipayConstants::METHOD_FIELD]);
|
||||
$signer = new Signer();
|
||||
return $signer->verify($content, $sign, $alipayPublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算签名,注意要去除key或value为null的键值对
|
||||
*
|
||||
* @param $systemParams array 系统参数集合
|
||||
* @param $bizParams array 业务参数集合
|
||||
* @param $textParams array 其他额外文本参数集合
|
||||
* @param $privateKey string 私钥
|
||||
* @return string 签名值的Base64串
|
||||
*/
|
||||
public function sign($systemParams, $bizParams, $textParams, $privateKey)
|
||||
{
|
||||
$sortedMap = $this->getSortedMap($systemParams, $bizParams, $textParams);
|
||||
$data = $this->getSignContent($sortedMap);
|
||||
$sign = new Signer();
|
||||
return $sign->sign($data, $privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* AES加密
|
||||
* @param $content
|
||||
* @param $encryptKey
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function aesEncrypt($content, $encryptKey)
|
||||
{
|
||||
$aes = new AES();
|
||||
return $aes->aesEncrypt($content, $encryptKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* AES解密
|
||||
* @param $content
|
||||
* @param $encryptKey
|
||||
* @return false|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function aesDecrypt($content, $encryptKey)
|
||||
{
|
||||
$aes = new AES();
|
||||
return $aes->aesDecrypt($content, $encryptKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成sdkExecute类请求所需URL
|
||||
*
|
||||
* @param $systemParams
|
||||
* @param $bizParams
|
||||
* @param $textParams
|
||||
* @param $sign
|
||||
* @return string
|
||||
*/
|
||||
public function generateOrderString($systemParams, $bizParams, $textParams, $sign)
|
||||
{
|
||||
//采集并排序所有参数
|
||||
$sortedMap = $this->getSortedMap($systemParams, $bizParams, $textParams);
|
||||
$sortedMap[AlipayConstants::SIGN_FIELD] = $sign;
|
||||
return http_build_query($sortedMap);
|
||||
}
|
||||
|
||||
public function sortMap($randomMap)
|
||||
{
|
||||
return $randomMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从响应Map中提取返回值对象的Map,并将响应原文插入到body字段中
|
||||
*
|
||||
* @param $respMap string 响应内容
|
||||
* @return mixed
|
||||
*/
|
||||
public function toRespModel($respMap)
|
||||
{
|
||||
$body = $respMap[AlipayConstants::BODY_FIELD];
|
||||
$methodName = $respMap[AlipayConstants::METHOD_FIELD];
|
||||
$responseNodeName = str_replace(".", "_", $methodName) . AlipayConstants::RESPONSE_SUFFIX;
|
||||
|
||||
$model = json_decode($body, true);
|
||||
if (strpos($body, AlipayConstants::ERROR_RESPONSE)) {
|
||||
$result = $model[AlipayConstants::ERROR_RESPONSE];
|
||||
$result[AlipayConstants::BODY_FIELD] = $body;
|
||||
} else {
|
||||
$result = $model[$responseNodeName];
|
||||
$result[AlipayConstants::BODY_FIELD] = $body;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function verifyParams($parameters, $publicKey)
|
||||
{
|
||||
$sign = new Signer();
|
||||
return $sign->verifyParams($parameters, $publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串拼接
|
||||
*
|
||||
* @param $a
|
||||
* @param $b
|
||||
* @return string 字符串a和b拼接后的字符串
|
||||
*/
|
||||
public function concatStr($a, $b)
|
||||
{
|
||||
return $a . $b;
|
||||
}
|
||||
|
||||
|
||||
private function buildQueryString(array $sortedMap)
|
||||
{
|
||||
$requestUrl = null;
|
||||
foreach ($sortedMap as $sysParamKey => $sysParamValue) {
|
||||
$requestUrl .= "$sysParamKey=" . urlencode($this->characet($sysParamValue, AlipayConstants::DEFAULT_CHARSET)) . "&";
|
||||
}
|
||||
$requestUrl = substr($requestUrl, 0, -1);
|
||||
return $requestUrl;
|
||||
|
||||
}
|
||||
|
||||
private function getSortedMap($systemParams, $bizParams, $textParams)
|
||||
{
|
||||
$this->textParams = $textParams;
|
||||
$this->bizParams = $bizParams;
|
||||
if ($textParams != null && $this->optionalTextParams != null) {
|
||||
$this->textParams = array_merge($textParams, $this->optionalTextParams);
|
||||
} else if ($textParams == null) {
|
||||
$this->textParams = $this->optionalTextParams;
|
||||
}
|
||||
if ($bizParams != null && $this->optionalBizParams != null) {
|
||||
$this->bizParams = array_merge($bizParams, $this->optionalBizParams);
|
||||
} else if ($bizParams == null) {
|
||||
$this->bizParams = $this->optionalBizParams;
|
||||
}
|
||||
$json = new JsonUtil();
|
||||
if ($this->bizParams != null) {
|
||||
$bizParams = $json->toJsonString($this->bizParams);
|
||||
}
|
||||
$sortedMap = $systemParams;
|
||||
if (!empty($bizParams)) {
|
||||
$sortedMap[AlipayConstants::BIZ_CONTENT_FIELD] = json_encode($bizParams, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
if (!empty($this->textParams)) {
|
||||
if (!empty($sortedMap)) {
|
||||
$sortedMap = array_merge($sortedMap, $this->textParams);
|
||||
} else {
|
||||
$sortedMap = $this->textParams;
|
||||
}
|
||||
}
|
||||
if ($this->getConfig(AlipayConstants::NOTIFY_URL_CONFIG_KEY) != null) {
|
||||
$sortedMap[AlipayConstants::NOTIFY_URL_FIELD] = $this->getConfig(AlipayConstants::NOTIFY_URL_CONFIG_KEY);
|
||||
}
|
||||
return $sortedMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签名字符串
|
||||
*
|
||||
* @param $params
|
||||
* @return string
|
||||
*/
|
||||
private function getSignContent($params)
|
||||
{
|
||||
ksort($params);
|
||||
$stringToBeSigned = "";
|
||||
$i = 0;
|
||||
foreach ($params as $k => $v) {
|
||||
if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
|
||||
// 转换成目标字符集
|
||||
$v = $this->characet($v, AlipayConstants::DEFAULT_CHARSET);
|
||||
if ($i == 0) {
|
||||
$stringToBeSigned .= "$k" . "=" . "$v";
|
||||
} else {
|
||||
$stringToBeSigned .= "&" . "$k" . "=" . "$v";
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
unset ($k, $v);
|
||||
return $stringToBeSigned;
|
||||
}
|
||||
|
||||
private function setNotifyUrl($params)
|
||||
{
|
||||
if ($this->config(AlipayConstants::NOTIFY_URL_CONFIG_KEY) != null && $params(AlipayConstants::NOTIFY_URL_CONFIG_KEY) == null) {
|
||||
$params[AlipayConstants::NOTIFY_URL_CONFIG_KEY] = $this->config(AlipayConstants::NOTIFY_URL_CONFIG_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
private function getGatewayServerUrl()
|
||||
{
|
||||
return $this->getConfig(AlipayConstants::PROTOCOL_CONFIG_KEY) . '://' . $this->getConfig(AlipayConstants::HOST_CONFIG_KEY) . '/gateway.do';
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验$value是否非空
|
||||
*
|
||||
* @param $value
|
||||
* @return bool if not set ,return true;if is null , return true;
|
||||
*/
|
||||
function checkEmpty($value)
|
||||
{
|
||||
if (!isset($value))
|
||||
return true;
|
||||
if ($value === null)
|
||||
return true;
|
||||
if (trim($value) === "")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换字符集编码
|
||||
* @param $data
|
||||
* @param $targetCharset
|
||||
* @return string
|
||||
*/
|
||||
function characet($data, $targetCharset)
|
||||
{
|
||||
if (!empty($data)) {
|
||||
$fileType = AlipayConstants::DEFAULT_CHARSET;
|
||||
if (strcasecmp($fileType, $targetCharset) != 0) {
|
||||
$data = mb_convert_encoding($data, $targetCharset, $fileType);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class RuntimeException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
namespace Alipay\EasySDK\Kernel;
|
||||
|
||||
use Alipay\EasySDK\Base\Image\Client as imageClient;
|
||||
use Alipay\EasySDK\Base\OAuth\Client as oauthClient;
|
||||
use Alipay\EasySDK\Base\Qrcode\Client as qrcodeClient;
|
||||
use Alipay\EasySDK\Base\Video\Client as videoClient;
|
||||
use Alipay\EasySDK\Marketing\OpenLife\Client as openLifeClient;
|
||||
use Alipay\EasySDK\Marketing\Pass\Client as passClient;
|
||||
use Alipay\EasySDK\Marketing\TemplateMessage\Client as templateMessageClient;
|
||||
use Alipay\EasySDK\Member\Identification\Client as identificationClient;
|
||||
use Alipay\EasySDK\Payment\App\Client as appClient;
|
||||
use Alipay\EasySDK\Payment\Common\Client as commonClient;
|
||||
use Alipay\EasySDK\Payment\FaceToFace\Client as faceToFaceClient;
|
||||
use Alipay\EasySDK\Payment\Huabei\Client as huabeiClient;
|
||||
use Alipay\EasySDK\Payment\Page\Client as pageClient;
|
||||
use Alipay\EasySDK\Payment\Wap\Client as wapClient;
|
||||
use Alipay\EasySDK\Security\TextRisk\Client as textRiskClient;
|
||||
use Alipay\EasySDK\Util\Generic\Client as genericClient;
|
||||
use Alipay\EasySDK\Util\AES\Client as aesClient;
|
||||
|
||||
class Factory
|
||||
{
|
||||
public $config = null;
|
||||
public $kernel = null;
|
||||
private static $instance;
|
||||
protected static $base;
|
||||
protected static $marketing;
|
||||
protected static $member;
|
||||
protected static $payment;
|
||||
protected static $security;
|
||||
protected static $util;
|
||||
|
||||
private function __construct($config)
|
||||
{
|
||||
if (!empty($config->alipayCertPath)) {
|
||||
$certEnvironment = new CertEnvironment();
|
||||
$certEnvironment->certEnvironment(
|
||||
$config->merchantCertPath,
|
||||
$config->alipayCertPath,
|
||||
$config->alipayRootCertPath
|
||||
);
|
||||
$config->merchantCertSN = $certEnvironment->getMerchantCertSN();
|
||||
$config->alipayRootCertSN = $certEnvironment->getRootCertSN();
|
||||
$config->alipayPublicKey = $certEnvironment->getCachedAlipayPublicKey();
|
||||
}
|
||||
|
||||
$kernel = new EasySDKKernel($config);
|
||||
self::$base = new Base($kernel);
|
||||
self::$marketing = new Marketing($kernel);
|
||||
self::$member = new Member($kernel);
|
||||
self::$payment = new Payment($kernel);
|
||||
self::$security = new Security($kernel);
|
||||
self::$util = new Util($kernel);
|
||||
}
|
||||
|
||||
public static function setOptions($config)
|
||||
{
|
||||
if (!(self::$instance instanceof self)) {
|
||||
self::$instance = new self($config);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
public static function base()
|
||||
{
|
||||
return self::$base;
|
||||
}
|
||||
|
||||
public static function marketing()
|
||||
{
|
||||
return self::$marketing;
|
||||
}
|
||||
|
||||
public static function member()
|
||||
{
|
||||
return self::$member;
|
||||
}
|
||||
|
||||
public static function payment()
|
||||
{
|
||||
return self::$payment;
|
||||
}
|
||||
|
||||
public static function security()
|
||||
{
|
||||
return self::$security;
|
||||
}
|
||||
|
||||
public static function util()
|
||||
{
|
||||
return self::$util;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Base
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function image()
|
||||
{
|
||||
return new imageClient($this->kernel);
|
||||
}
|
||||
|
||||
public function oauth()
|
||||
{
|
||||
return new oauthClient($this->kernel);
|
||||
}
|
||||
|
||||
public function qrcode()
|
||||
{
|
||||
return new qrcodeClient($this->kernel);
|
||||
}
|
||||
|
||||
public function video()
|
||||
{
|
||||
return new videoClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Marketing
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function openLife()
|
||||
{
|
||||
return new openLifeClient($this->kernel);
|
||||
}
|
||||
|
||||
public function pass()
|
||||
{
|
||||
return new passClient($this->kernel);
|
||||
}
|
||||
|
||||
public function templateMessage()
|
||||
{
|
||||
return new templateMessageClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Member
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function identification()
|
||||
{
|
||||
return new identificationClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Payment
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function app()
|
||||
{
|
||||
return new appClient($this->kernel);
|
||||
}
|
||||
|
||||
public function common()
|
||||
{
|
||||
return new commonClient($this->kernel);
|
||||
}
|
||||
|
||||
public function faceToFace()
|
||||
{
|
||||
return new faceToFaceClient($this->kernel);
|
||||
}
|
||||
|
||||
public function huabei()
|
||||
{
|
||||
return new huabeiClient($this->kernel);
|
||||
}
|
||||
|
||||
public function page()
|
||||
{
|
||||
return new pageClient($this->kernel);
|
||||
}
|
||||
|
||||
public function wap()
|
||||
{
|
||||
return new wapClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Security
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function textRisk()
|
||||
{
|
||||
return new textRiskClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Util
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function generic()
|
||||
{
|
||||
return new genericClient($this->kernel);
|
||||
}
|
||||
|
||||
public function aes(){
|
||||
return new aesClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace Alipay\EasySDK\Kernel;
|
||||
|
||||
use Alipay\EasySDK\Base\Image\Client as imageClient;
|
||||
use Alipay\EasySDK\Base\OAuth\Client as oauthClient;
|
||||
use Alipay\EasySDK\Base\Qrcode\Client as qrcodeClient;
|
||||
use Alipay\EasySDK\Base\Video\Client as videoClient;
|
||||
use Alipay\EasySDK\Marketing\OpenLife\Client as openLifeClient;
|
||||
use Alipay\EasySDK\Marketing\Pass\Client as passClient;
|
||||
use Alipay\EasySDK\Marketing\TemplateMessage\Client as templateMessageClient;
|
||||
use Alipay\EasySDK\Member\Identification\Client as identificationClient;
|
||||
use Alipay\EasySDK\Payment\App\Client as appClient;
|
||||
use Alipay\EasySDK\Payment\Common\Client as commonClient;
|
||||
use Alipay\EasySDK\Payment\FaceToFace\Client as faceToFaceClient;
|
||||
use Alipay\EasySDK\Payment\Huabei\Client as huabeiClient;
|
||||
use Alipay\EasySDK\Payment\Page\Client as pageClient;
|
||||
use Alipay\EasySDK\Payment\Wap\Client as wapClient;
|
||||
use Alipay\EasySDK\Security\TextRisk\Client as textRiskClient;
|
||||
use Alipay\EasySDK\Util\Generic\Client as genericClient;
|
||||
use Alipay\EasySDK\Util\AES\Client as aesClient;
|
||||
|
||||
/**
|
||||
* 多账号实例使用
|
||||
* Class MultipleFactory
|
||||
* @package Alipay\EasySDK\Kernel
|
||||
*/
|
||||
class MultipleFactory
|
||||
{
|
||||
public $config = null;
|
||||
public $kernel = null;
|
||||
private static $instance;
|
||||
protected static $base;
|
||||
protected static $marketing;
|
||||
protected static $member;
|
||||
protected static $payment;
|
||||
protected static $security;
|
||||
protected static $util;
|
||||
|
||||
private function __construct($config)
|
||||
{
|
||||
if (!empty($config->alipayCertPath)) {
|
||||
$certEnvironment = new CertEnvironment();
|
||||
$certEnvironment->certEnvironment(
|
||||
$config->merchantCertPath,
|
||||
$config->alipayCertPath,
|
||||
$config->alipayRootCertPath
|
||||
);
|
||||
$config->merchantCertSN = $certEnvironment->getMerchantCertSN();
|
||||
$config->alipayRootCertSN = $certEnvironment->getRootCertSN();
|
||||
$config->alipayPublicKey = $certEnvironment->getCachedAlipayPublicKey();
|
||||
}
|
||||
|
||||
$kernel = new EasySDKKernel($config);
|
||||
self::$base = new Base($kernel);
|
||||
self::$marketing = new Marketing($kernel);
|
||||
self::$member = new Member($kernel);
|
||||
self::$payment = new Payment($kernel);
|
||||
self::$security = new Security($kernel);
|
||||
self::$util = new Util($kernel);
|
||||
}
|
||||
|
||||
public static function setOptions($config)
|
||||
{
|
||||
self::$instance = new self($config);
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
public static function base()
|
||||
{
|
||||
return self::$base;
|
||||
}
|
||||
|
||||
public static function marketing()
|
||||
{
|
||||
return self::$marketing;
|
||||
}
|
||||
|
||||
public static function member()
|
||||
{
|
||||
return self::$member;
|
||||
}
|
||||
|
||||
public static function payment()
|
||||
{
|
||||
return self::$payment;
|
||||
}
|
||||
|
||||
public static function security()
|
||||
{
|
||||
return self::$security;
|
||||
}
|
||||
|
||||
public static function util()
|
||||
{
|
||||
return self::$util;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Base
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function image()
|
||||
{
|
||||
return new imageClient($this->kernel);
|
||||
}
|
||||
|
||||
public function oauth()
|
||||
{
|
||||
return new oauthClient($this->kernel);
|
||||
}
|
||||
|
||||
public function qrcode()
|
||||
{
|
||||
return new qrcodeClient($this->kernel);
|
||||
}
|
||||
|
||||
public function video()
|
||||
{
|
||||
return new videoClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Marketing
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function openLife()
|
||||
{
|
||||
return new openLifeClient($this->kernel);
|
||||
}
|
||||
|
||||
public function pass()
|
||||
{
|
||||
return new passClient($this->kernel);
|
||||
}
|
||||
|
||||
public function templateMessage()
|
||||
{
|
||||
return new templateMessageClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Member
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function identification()
|
||||
{
|
||||
return new identificationClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Payment
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function app()
|
||||
{
|
||||
return new appClient($this->kernel);
|
||||
}
|
||||
|
||||
public function common()
|
||||
{
|
||||
return new commonClient($this->kernel);
|
||||
}
|
||||
|
||||
public function faceToFace()
|
||||
{
|
||||
return new faceToFaceClient($this->kernel);
|
||||
}
|
||||
|
||||
public function huabei()
|
||||
{
|
||||
return new huabeiClient($this->kernel);
|
||||
}
|
||||
|
||||
public function page()
|
||||
{
|
||||
return new pageClient($this->kernel);
|
||||
}
|
||||
|
||||
public function wap()
|
||||
{
|
||||
return new wapClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Security
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function textRisk()
|
||||
{
|
||||
return new textRiskClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
class Util
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct($kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
public function generic()
|
||||
{
|
||||
return new genericClient($this->kernel);
|
||||
}
|
||||
|
||||
public function aes()
|
||||
{
|
||||
return new aesClient($this->kernel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Util;
|
||||
|
||||
|
||||
class AES
|
||||
{
|
||||
/**
|
||||
* AES加密
|
||||
*
|
||||
* @param $plainText String 明文
|
||||
* @param $key String 对称密钥
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function aesEncrypt($plainText, $key)
|
||||
{
|
||||
try {
|
||||
if(strlen($key) == 0){
|
||||
throw new \Exception("AES加密失败,plainText=".$plainText.",AES密钥为空。");
|
||||
}
|
||||
//AES, 128 模式加密数据 CBC
|
||||
$screct_key = base64_decode($key);
|
||||
$str = trim($plainText);
|
||||
$str = $this->addPKCS7Padding($str);
|
||||
$iv = str_repeat("\0", 16);
|
||||
$encrypt_str = openssl_encrypt($str, 'aes-128-cbc', $screct_key, OPENSSL_NO_PADDING, $iv);
|
||||
return base64_encode($encrypt_str);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception("AES加密失败,plainText=".$plainText.",keySize=".strlen($key)."。".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* AES解密
|
||||
*
|
||||
* @param $cipherText String 密文
|
||||
* @param $key String 对称密钥
|
||||
* @return false|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function aesDecrypt($cipherText, $key)
|
||||
{
|
||||
try{
|
||||
if(strlen($key) == 0){
|
||||
throw new \Exception("AES加密失败,plainText=".$cipherText.",AES密钥为空。");
|
||||
}
|
||||
//AES, 128 模式加密数据 CBC
|
||||
$str = base64_decode($cipherText);
|
||||
$screct_key = base64_decode($key);
|
||||
$iv = str_repeat("\0", 16);
|
||||
$decrypt_str = openssl_decrypt($str, 'aes-128-cbc', $screct_key, OPENSSL_NO_PADDING, $iv);
|
||||
$decrypt_str = $this->stripPKSC7Padding($decrypt_str);
|
||||
return $decrypt_str;
|
||||
}catch (\Exception $e){
|
||||
throw new \Exception("AES解密失败,cipherText=".$cipherText.",keySize=".strlen($key)."。".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充算法
|
||||
* @param string $source
|
||||
* @return string
|
||||
*/
|
||||
private function addPKCS7Padding($source)
|
||||
{
|
||||
$source = trim($source);
|
||||
$block = 16;
|
||||
|
||||
$pad = $block - (strlen($source) % $block);
|
||||
if ($pad <= $block) {
|
||||
$char = chr($pad);
|
||||
$source .= str_repeat($char, $pad);
|
||||
}
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移去填充算法
|
||||
* @param string $source
|
||||
* @return string
|
||||
*/
|
||||
private function stripPKSC7Padding($source)
|
||||
{
|
||||
$char = substr($source, -1);
|
||||
$num = ord($char);
|
||||
if ($num == 62) return $source;
|
||||
$source = substr($source, 0, -$num);
|
||||
return $source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Util;
|
||||
|
||||
|
||||
class AlipayEncrypt
|
||||
{
|
||||
const AES_ALG = "AES";
|
||||
const AES_CBC_PCK_ALG = "AES/CBC/PKCS5Padding";
|
||||
|
||||
}
|
||||
+612
@@ -0,0 +1,612 @@
|
||||
<?php
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Util;
|
||||
|
||||
class AntCertificationUtil
|
||||
{
|
||||
private $rootCertContent;
|
||||
|
||||
/**
|
||||
* 从证书中提取序列号
|
||||
* @param $certPath
|
||||
* @return string
|
||||
*/
|
||||
public function getCertSN($certPath)
|
||||
{
|
||||
$cert = file_get_contents($certPath);
|
||||
$ssl = openssl_x509_parse($cert);
|
||||
$SN = md5($this->array2string(array_reverse($ssl['issuer'])) . $ssl['serialNumber']);
|
||||
return $SN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从证书中提取公钥
|
||||
* @param $certPath
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPublicKey($certPath)
|
||||
{
|
||||
$cert = file_get_contents($certPath);
|
||||
$pkey = openssl_pkey_get_public($cert);
|
||||
$keyData = openssl_pkey_get_details($pkey);
|
||||
$public_key = str_replace('-----BEGIN PUBLIC KEY-----', '', $keyData['key']);
|
||||
$public_key = trim(str_replace('-----END PUBLIC KEY-----', '', $public_key));
|
||||
return $public_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取根证书序列号
|
||||
* @param $certPath string 根证书
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRootCertSN($certPath)
|
||||
{
|
||||
$cert = file_get_contents($certPath);
|
||||
$this->rootCertContent = $cert;
|
||||
$array = explode("-----END CERTIFICATE-----", $cert);
|
||||
$SN = null;
|
||||
for ($i = 0; $i < count($array) - 1; $i++) {
|
||||
$ssl[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----");
|
||||
if (strpos($ssl[$i]['serialNumber'], '0x') === 0) {
|
||||
$ssl[$i]['serialNumber'] = $this->hex2dec($ssl[$i]['serialNumberHex']);
|
||||
}
|
||||
if ($ssl[$i]['signatureTypeLN'] == "sha1WithRSAEncryption" || $ssl[$i]['signatureTypeLN'] == "sha256WithRSAEncryption") {
|
||||
if ($SN == null) {
|
||||
$SN = md5($this->array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
|
||||
} else {
|
||||
|
||||
$SN = $SN . "_" . md5($this->array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $SN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 0x转高精度数字
|
||||
* @param $hex
|
||||
* @return int|string
|
||||
*/
|
||||
function hex2dec($hex)
|
||||
{
|
||||
$dec = 0;
|
||||
$len = strlen($hex);
|
||||
for ($i = 1; $i <= $len; $i++) {
|
||||
$dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
|
||||
}
|
||||
return $dec;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证支付宝公钥证书是否可信
|
||||
* @param $alipayCert 支付宝公钥证书
|
||||
* @param $rootCert 支付宝根证书
|
||||
* @return bool 验证证书是否可信
|
||||
*/
|
||||
function isTrusted($alipayCert, $rootCert)
|
||||
{
|
||||
$alipayCerts = $this->readPemCertChain($alipayCert);
|
||||
$rootCerts = $this->readPemCertChain($rootCert);
|
||||
if ($this->verifyCertChain($alipayCerts, $rootCerts)) {
|
||||
return $this->verifySignature($alipayCert, $rootCert);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function verifySignature($alipayCert, $rootCert)
|
||||
{
|
||||
$alipayCertArray = explode("-----END CERTIFICATE-----", $alipayCert);
|
||||
$rootCertArray = explode("-----END CERTIFICATE-----", $rootCert);
|
||||
$length = count($rootCertArray) - 1;
|
||||
$checkSign = $this->isCertSigner($alipayCertArray[0] . "-----END CERTIFICATE-----", $alipayCertArray[1] . "-----END CERTIFICATE-----");
|
||||
if (!$checkSign) {
|
||||
$checkSign = $this->isCertSigner($alipayCertArray[1] . "-----END CERTIFICATE-----", $alipayCertArray[0] . "-----END CERTIFICATE-----");
|
||||
if ($checkSign) {
|
||||
$issuer = openssl_x509_parse($alipayCertArray[0] . "-----END CERTIFICATE-----")['issuer'];
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$subject = openssl_x509_parse($rootCertArray[$i] . "-----END CERTIFICATE-----")['subject'];
|
||||
if ($issuer == $subject) {
|
||||
$this->isCertSigner($alipayCertArray[0] . "-----END CERTIFICATE-----", $rootCertArray[$i] . $rootCertArray);
|
||||
return $checkSign;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return $checkSign;
|
||||
}
|
||||
} else {
|
||||
$issuer = openssl_x509_parse($alipayCertArray[1] . "-----END CERTIFICATE-----")['issuer'];
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$subject = openssl_x509_parse($rootCertArray[$i] . "-----END CERTIFICATE-----")['subject'];
|
||||
if ($issuer == $subject) {
|
||||
$checkSign = $this->isCertSigner($alipayCertArray[1] . "-----END CERTIFICATE-----", $rootCertArray[$i] . "-----END CERTIFICATE-----");
|
||||
return $checkSign;
|
||||
}
|
||||
}
|
||||
return $checkSign;
|
||||
}
|
||||
}
|
||||
|
||||
function readPemCertChain($cert)
|
||||
{
|
||||
$array = explode("-----END CERTIFICATE-----", $cert);
|
||||
$certs[] = null;
|
||||
for ($i = 0; $i < count($array) - 1; $i++) {
|
||||
$certs[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----");
|
||||
}
|
||||
return $certs;
|
||||
}
|
||||
|
||||
function verifyCert($prev, $rootCerts)
|
||||
{
|
||||
$nowTime = time();
|
||||
if ($nowTime < $prev['validFrom_time_t']) {
|
||||
echo "证书未激活";
|
||||
return false;
|
||||
}
|
||||
if ($nowTime > $prev['validTo_time_t']) {
|
||||
echo "证书已经过期";
|
||||
return false;
|
||||
}
|
||||
$subjectMap = null;
|
||||
for ($i = 0; $i < count($rootCerts); $i++) {
|
||||
$subjectDN = $this->array2string($rootCerts[$i]['subject']);
|
||||
$subjectMap[$subjectDN] = $rootCerts[$i];
|
||||
}
|
||||
$issuerDN = $this->array2string(($prev['issuer']));
|
||||
if (!array_key_exists($issuerDN, $subjectMap)) {
|
||||
echo "证书链验证失败";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证证书链是否是信任证书库中证书签发的
|
||||
* @param $alipayCerts array 目标验证证书列表
|
||||
* @param $rootCerts array 可信根证书列表
|
||||
* @return bool
|
||||
*/
|
||||
function verifyCertChain($alipayCerts, $rootCerts)
|
||||
{
|
||||
$sorted = $this->sortByDn($alipayCerts);
|
||||
if (!$sorted) {
|
||||
echo "证书链验证失败:不是完整的证书链";
|
||||
return false;
|
||||
}
|
||||
//先验证第一个证书是不是信任库中证书签发的
|
||||
$prev = $alipayCerts[0];
|
||||
$firstOK = $this->verifyCert($prev, $rootCerts);
|
||||
$length = count($alipayCerts);
|
||||
if (!$firstOK || $length == 1) {
|
||||
return $firstOK;
|
||||
}
|
||||
|
||||
$nowTime = time();
|
||||
//验证证书链
|
||||
for ($i = 1; $i < $length; $i++) {
|
||||
$cert = $alipayCerts[$i];
|
||||
if ($nowTime < $cert['validFrom_time_t']) {
|
||||
echo "证书未激活";
|
||||
return false;
|
||||
}
|
||||
if ($nowTime > $cert['validTo_time_t']) {
|
||||
echo "证书已经过期";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将证书链按照完整的签发顺序进行排序,排序后证书链为:[issuerA, subjectA]-[issuerA, subjectB]-[issuerB, subjectC]-[issuerC, subjectD]...
|
||||
* @param $certs array 证书链
|
||||
* @return bool
|
||||
*/
|
||||
function sortByDn(&$certs)
|
||||
{
|
||||
//是否包含自签名证书
|
||||
$hasSelfSignedCert = false;
|
||||
$subjectMap = null;
|
||||
$issuerMap = null;
|
||||
for ($i = 0; $i < count($certs); $i++) {
|
||||
if ($this->isSelfSigned($certs[$i])) {
|
||||
if ($hasSelfSignedCert) {
|
||||
return false;
|
||||
}
|
||||
$hasSelfSignedCert = true;
|
||||
}
|
||||
$subjectDN = $this->array2string($certs[$i]['subject']);
|
||||
$issuerDN = $this->array2string(($certs[$i]['issuer']));
|
||||
$subjectMap[$subjectDN] = $certs[$i];
|
||||
$issuerMap[$issuerDN] = $certs[$i];
|
||||
}
|
||||
$certChain = null;
|
||||
$this->addressingUp($subjectMap, $certChain, $certs[0]);
|
||||
$this->addressingDown($issuerMap, $certChain, $certs[0]);
|
||||
|
||||
//说明证书链不完整
|
||||
if (count($certs) != count($certChain)) {
|
||||
return false;
|
||||
}
|
||||
//将证书链复制到原先的数据
|
||||
for ($i = 0; $i < count($certs); $i++) {
|
||||
$certs[$i] = $certChain[count($certs) - $i - 1];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证证书是否是自签发的
|
||||
* @param $cert array 目标证书
|
||||
* @return bool
|
||||
*/
|
||||
function isSelfSigned($cert)
|
||||
{
|
||||
$subjectDN = $this->array2string($cert['subject']);
|
||||
$issuerDN = $this->array2string($cert['issuer']);
|
||||
return ($subjectDN == $issuerDN);
|
||||
}
|
||||
|
||||
|
||||
function array2string($array)
|
||||
{
|
||||
$string = [];
|
||||
if ($array && is_array($array)) {
|
||||
foreach ($array as $key => $value) {
|
||||
$string[] = $key . '=' . $value;
|
||||
}
|
||||
}
|
||||
return implode(',', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向上构造证书链
|
||||
* @param $subjectMap array 主题和证书的映射
|
||||
* @param $certChain array 证书链
|
||||
* @param $current 当前需要插入证书链的证书,include
|
||||
*/
|
||||
function addressingUp($subjectMap, &$certChain, $current)
|
||||
{
|
||||
$certChain[] = $current;
|
||||
if ($this->isSelfSigned($current)) {
|
||||
return;
|
||||
}
|
||||
$issuerDN = $this->array2string($current['issuer']);
|
||||
|
||||
if (!array_key_exists($issuerDN, $subjectMap)) {
|
||||
return;
|
||||
}
|
||||
$this->addressingUp($subjectMap, $certChain, $subjectMap[$issuerDN]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向下构造证书链
|
||||
* @param $issuerMap 签发者和证书的映射
|
||||
* @param $certChain 证书链
|
||||
* @param $current 当前需要插入证书链的证书,exclude
|
||||
* @return mixed
|
||||
*/
|
||||
function addressingDown($issuerMap, &$certChain, $current)
|
||||
{
|
||||
$subjectDN = $this->array2string($current['subject']);
|
||||
if (!array_key_exists($subjectDN, $issuerMap)) {
|
||||
return $certChain;
|
||||
}
|
||||
$certChain[] = $issuerMap[$subjectDN];
|
||||
$this->addressingDown($issuerMap, $certChain, $issuerMap[$subjectDN]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract signature from der encoded cert.
|
||||
* Expects x509 der encoded certificate consisting of a section container
|
||||
* containing 2 sections and a bitstream. The bitstream contains the
|
||||
* original encrypted signature, encrypted by the public key of the issuing
|
||||
* signer.
|
||||
* @param string $der
|
||||
* @return string on success
|
||||
* @return bool false on failures
|
||||
*/
|
||||
function extractSignature($der = false)
|
||||
{
|
||||
if (strlen($der) < 5) {
|
||||
return false;
|
||||
}
|
||||
// skip container sequence
|
||||
$der = substr($der, 4);
|
||||
// now burn through two sequences and the return the final bitstream
|
||||
while (strlen($der) > 1) {
|
||||
$class = ord($der[0]);
|
||||
$classHex = dechex($class);
|
||||
switch ($class) {
|
||||
// BITSTREAM
|
||||
case 0x03:
|
||||
$len = ord($der[1]);
|
||||
$bytes = 0;
|
||||
if ($len & 0x80) {
|
||||
$bytes = $len & 0x0f;
|
||||
$len = 0;
|
||||
for ($i = 0; $i < $bytes; $i++) {
|
||||
$len = ($len << 8) | ord($der[$i + 2]);
|
||||
}
|
||||
}
|
||||
return substr($der, 3 + $bytes, $len);
|
||||
break;
|
||||
// SEQUENCE
|
||||
case 0x30:
|
||||
$len = ord($der[1]);
|
||||
$bytes = 0;
|
||||
if ($len & 0x80) {
|
||||
$bytes = $len & 0x0f;
|
||||
$len = 0;
|
||||
for ($i = 0; $i < $bytes; $i++) {
|
||||
$len = ($len << 8) | ord($der[$i + 2]);
|
||||
}
|
||||
}
|
||||
$contents = substr($der, 2 + $bytes, $len);
|
||||
$der = substr($der, 2 + $bytes + $len);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get signature algorithm oid from der encoded signature data.
|
||||
* Expects decrypted signature data from a certificate in der format.
|
||||
* This ASN1 data should contain the following structure:
|
||||
* SEQUENCE
|
||||
* SEQUENCE
|
||||
* OID (signature algorithm)
|
||||
* NULL
|
||||
* OCTET STRING (signature hash)
|
||||
* @return bool false on failures
|
||||
* @return string oid
|
||||
*/
|
||||
function getSignatureAlgorithmOid($der = null)
|
||||
{
|
||||
// Validate this is the der we need...
|
||||
if (!is_string($der) or strlen($der) < 5) {
|
||||
return false;
|
||||
}
|
||||
$bit_seq1 = 0;
|
||||
$bit_seq2 = 2;
|
||||
$bit_oid = 4;
|
||||
if (ord($der[$bit_seq1]) !== 0x30) {
|
||||
die('Invalid DER passed to getSignatureAlgorithmOid()');
|
||||
}
|
||||
if (ord($der[$bit_seq2]) !== 0x30) {
|
||||
die('Invalid DER passed to getSignatureAlgorithmOid()');
|
||||
}
|
||||
if (ord($der[$bit_oid]) !== 0x06) {
|
||||
die('Invalid DER passed to getSignatureAlgorithmOid');
|
||||
}
|
||||
// strip out what we don't need and get the oid
|
||||
$der = substr($der, $bit_oid);
|
||||
// Get the oid
|
||||
$len = ord($der[1]);
|
||||
$bytes = 0;
|
||||
if ($len & 0x80) {
|
||||
$bytes = $len & 0x0f;
|
||||
$len = 0;
|
||||
for ($i = 0; $i < $bytes; $i++) {
|
||||
$len = ($len << 8) | ord($der[$i + 2]);
|
||||
}
|
||||
}
|
||||
$oid_data = substr($der, 2 + $bytes, $len);
|
||||
// Unpack the OID
|
||||
$oid = floor(ord($oid_data[0]) / 40);
|
||||
$oid .= '.' . ord($oid_data[0]) % 40;
|
||||
$value = 0;
|
||||
$i = 1;
|
||||
while ($i < strlen($oid_data)) {
|
||||
$value = $value << 7;
|
||||
$value = $value | (ord($oid_data[$i]) & 0x7f);
|
||||
if (!(ord($oid_data[$i]) & 0x80)) {
|
||||
$oid .= '.' . $value;
|
||||
$value = 0;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return $oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get signature hash from der encoded signature data.
|
||||
* Expects decrypted signature data from a certificate in der format.
|
||||
* This ASN1 data should contain the following structure:
|
||||
* SEQUENCE
|
||||
* SEQUENCE
|
||||
* OID (signature algorithm)
|
||||
* NULL
|
||||
* OCTET STRING (signature hash)
|
||||
* @return bool false on failures
|
||||
* @return string hash
|
||||
*/
|
||||
function getSignatureHash($der = null)
|
||||
{
|
||||
// Validate this is the der we need...
|
||||
if (!is_string($der) or strlen($der) < 5) {
|
||||
return false;
|
||||
}
|
||||
if (ord($der[0]) !== 0x30) {
|
||||
die('Invalid DER passed to getSignatureHash()');
|
||||
}
|
||||
// strip out the container sequence
|
||||
$der = substr($der, 2);
|
||||
if (ord($der[0]) !== 0x30) {
|
||||
die('Invalid DER passed to getSignatureHash()');
|
||||
}
|
||||
// Get the length of the first sequence so we can strip it out.
|
||||
$len = ord($der[1]);
|
||||
$bytes = 0;
|
||||
if ($len & 0x80) {
|
||||
$bytes = $len & 0x0f;
|
||||
$len = 0;
|
||||
for ($i = 0; $i < $bytes; $i++) {
|
||||
$len = ($len << 8) | ord($der[$i + 2]);
|
||||
}
|
||||
}
|
||||
$der = substr($der, 2 + $bytes + $len);
|
||||
// Now we should have an octet string
|
||||
if (ord($der[0]) !== 0x04) {
|
||||
die('Invalid DER passed to getSignatureHash()');
|
||||
}
|
||||
$len = ord($der[1]);
|
||||
$bytes = 0;
|
||||
if ($len & 0x80) {
|
||||
$bytes = $len & 0x0f;
|
||||
$len = 0;
|
||||
for ($i = 0; $i < $bytes; $i++) {
|
||||
$len = ($len << 8) | ord($der[$i + 2]);
|
||||
}
|
||||
}
|
||||
return bin2hex(substr($der, 2 + $bytes, $len));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if one cert was used to sign another
|
||||
* Note that more than one CA cert can give a positive result, some certs
|
||||
* re-issue signing certs after having only changed the expiration dates.
|
||||
* @param string $cert - PEM encoded cert
|
||||
* @param string $caCert - PEM encoded cert that possibly signed $cert
|
||||
* @return bool
|
||||
*/
|
||||
function isCertSigner($certPem = null, $caCertPem = null)
|
||||
{
|
||||
if (!function_exists('openssl_pkey_get_public')) {
|
||||
die('Need the openssl_pkey_get_public() function.');
|
||||
}
|
||||
if (!function_exists('openssl_public_decrypt')) {
|
||||
die('Need the openssl_public_decrypt() function.');
|
||||
}
|
||||
if (!function_exists('hash')) {
|
||||
die('Need the php hash() function.');
|
||||
}
|
||||
if (empty($certPem) or empty($caCertPem)) {
|
||||
return false;
|
||||
}
|
||||
// Convert the cert to der for feeding to extractSignature.
|
||||
$certDer = pemToDer($certPem);
|
||||
if (!is_string($certDer)) {
|
||||
die('invalid certPem');
|
||||
}
|
||||
// Grab the encrypted signature from the der encoded cert.
|
||||
$encryptedSig = extractSignature($certDer);
|
||||
if (!is_string($encryptedSig)) {
|
||||
die('Failed to extract encrypted signature from certPem.');
|
||||
}
|
||||
// Extract the public key from the ca cert, which is what has
|
||||
// been used to encrypt the signature in the cert.
|
||||
$pubKey = openssl_pkey_get_public($caCertPem);
|
||||
if ($pubKey === false) {
|
||||
die('Failed to extract the public key from the ca cert.');
|
||||
}
|
||||
// Attempt to decrypt the encrypted signature using the CA's public
|
||||
// key, returning the decrypted signature in $decryptedSig. If
|
||||
// it can't be decrypted, this ca was not used to sign it for sure...
|
||||
$rc = openssl_public_decrypt($encryptedSig, $decryptedSig, $pubKey);
|
||||
if ($rc === false) {
|
||||
return false;
|
||||
}
|
||||
// We now have the decrypted signature, which is der encoded
|
||||
// asn1 data containing the signature algorithm and signature hash.
|
||||
// Now we need what was originally hashed by the issuer, which is
|
||||
// the original DER encoded certificate without the issuer and
|
||||
// signature information.
|
||||
$origCert = stripSignerAsn($certDer);
|
||||
if ($origCert === false) {
|
||||
die('Failed to extract unsigned cert.');
|
||||
}
|
||||
// Get the oid of the signature hash algorithm, which is required
|
||||
// to generate our own hash of the original cert. This hash is
|
||||
// what will be compared to the issuers hash.
|
||||
$oid = getSignatureAlgorithmOid($decryptedSig);
|
||||
if ($oid === false) {
|
||||
die('Failed to determine the signature algorithm.');
|
||||
}
|
||||
switch ($oid) {
|
||||
case '1.2.840.113549.2.2':
|
||||
$algo = 'md2';
|
||||
break;
|
||||
case '1.2.840.113549.2.4':
|
||||
$algo = 'md4';
|
||||
break;
|
||||
case '1.2.840.113549.2.5':
|
||||
$algo = 'md5';
|
||||
break;
|
||||
case '1.3.14.3.2.18':
|
||||
$algo = 'sha';
|
||||
break;
|
||||
case '1.3.14.3.2.26':
|
||||
$algo = 'sha1';
|
||||
break;
|
||||
case '2.16.840.1.101.3.4.2.1':
|
||||
$algo = 'sha256';
|
||||
break;
|
||||
case '2.16.840.1.101.3.4.2.2':
|
||||
$algo = 'sha384';
|
||||
break;
|
||||
case '2.16.840.1.101.3.4.2.3':
|
||||
$algo = 'sha512';
|
||||
break;
|
||||
default:
|
||||
die('Unknown signature hash algorithm oid: ' . $oid);
|
||||
break;
|
||||
}
|
||||
// Get the issuer generated hash from the decrypted signature.
|
||||
$decryptedHash = getSignatureHash($decryptedSig);
|
||||
// Ok, hash the original unsigned cert with the same algorithm
|
||||
// and if it matches $decryptedHash we have a winner.
|
||||
$certHash = hash($algo, $origCert);
|
||||
return ($decryptedHash === $certHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert pem encoded certificate to DER encoding
|
||||
* @return string $derEncoded on success
|
||||
* @return bool false on failures
|
||||
*/
|
||||
function pemToDer($pem = null)
|
||||
{
|
||||
if (!is_string($pem)) {
|
||||
return false;
|
||||
}
|
||||
$cert_split = preg_split('/(-----((BEGIN)|(END)) CERTIFICATE-----)/', $pem);
|
||||
if (!isset($cert_split[1])) {
|
||||
return false;
|
||||
}
|
||||
return base64_decode($cert_split[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain der cert with issuer and signature sections stripped.
|
||||
* @param string $der - der encoded certificate
|
||||
* @return string $der on success
|
||||
* @return bool false on failures.
|
||||
*/
|
||||
function stripSignerAsn($der = null)
|
||||
{
|
||||
if (!is_string($der) or strlen($der) < 8) {
|
||||
return false;
|
||||
}
|
||||
$bit = 4;
|
||||
$len = ord($der[($bit + 1)]);
|
||||
$bytes = 0;
|
||||
if ($len & 0x80) {
|
||||
$bytes = $len & 0x0f;
|
||||
$len = 0;
|
||||
for ($i = 0; $i < $bytes; $i++) {
|
||||
$len = ($len << 8) | ord($der[$bit + $i + 2]);
|
||||
}
|
||||
}
|
||||
return substr($der, 4, $len + 4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Util;
|
||||
|
||||
|
||||
use AlibabaCloud\Tea\Model;
|
||||
|
||||
class JsonUtil
|
||||
{
|
||||
public function toJsonString(array $input)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($input as $k => $v) {
|
||||
if ($v instanceof Model) {
|
||||
$result[$k] = $this->getTeaModelMap($v);
|
||||
} else {
|
||||
$result[$k] = $v;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getTeaModelMap(Model $teaModel)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($teaModel as $k => $v) {
|
||||
if ($v instanceof Model) {
|
||||
$k = $this->toUnderScore($k);
|
||||
$result[$k] = $this->getTeaModelMap($v);
|
||||
} else {
|
||||
if (empty($result)) {
|
||||
$k = $this->toUnderScore($k);
|
||||
$result[$k] = $v;
|
||||
} else {
|
||||
$k = $this->toUnderScore($k);
|
||||
$result[$k] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰命名转下划线命名
|
||||
* @param $str
|
||||
* @return string
|
||||
*/
|
||||
private function toUnderScore($str)
|
||||
{
|
||||
$dstr = preg_replace_callback('/([A-Z]+)/', function ($matchs) {
|
||||
return '_' . strtolower($matchs[0]);
|
||||
}, $str);
|
||||
return trim(preg_replace('/_{2,}/', '_', $dstr), '_');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Util;
|
||||
|
||||
|
||||
use Alipay\EasySDK\Kernel\AlipayConstants;
|
||||
|
||||
class PageUtil
|
||||
{
|
||||
public function buildForm($actionUrl,$parameters){
|
||||
$sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='" . $actionUrl . "?charset=" . trim(AlipayConstants::DEFAULT_CHARSET) . "' method='POST'>";
|
||||
while (list ($key, $val) = $this->fun_adm_each($parameters)) {
|
||||
if (false === $this->checkEmpty($val)) {
|
||||
$val = str_replace("'", "'", $val);
|
||||
//$val = str_replace("\"",""",$val);
|
||||
$sHtml .= "<input type='hidden' name='" . $key . "' value='" . $val . "'/>";
|
||||
}
|
||||
}
|
||||
|
||||
//submit按钮控件请不要含有name属性
|
||||
$sHtml = $sHtml . "<input type='submit' value='ok' style='display:none;'></form>";
|
||||
|
||||
$sHtml = $sHtml . "<script>document.forms['alipaysubmit'].submit();</script>";
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function fun_adm_each(&$array)
|
||||
{
|
||||
$res = array();
|
||||
$key = key($array);
|
||||
if ($key !== null) {
|
||||
next($array);
|
||||
$res[1] = $res['value'] = $array[$key];
|
||||
$res[0] = $res['key'] = $key;
|
||||
} else {
|
||||
$res = false;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验$value是否非空
|
||||
* if not set ,return true;
|
||||
* if is null , return true;
|
||||
**/
|
||||
protected function checkEmpty($value)
|
||||
{
|
||||
if (!isset($value))
|
||||
return true;
|
||||
if ($value === null)
|
||||
return true;
|
||||
if (trim($value) === "")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Util;
|
||||
|
||||
|
||||
class ResponseChecker
|
||||
{
|
||||
public function success($response)
|
||||
{
|
||||
if (!empty($response->code) && $response->code == 10000) {
|
||||
return true;
|
||||
}
|
||||
if (empty($response->code) && empty($response->subCode)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Util;
|
||||
|
||||
use Alipay\EasySDK\Kernel\AlipayConstants;
|
||||
|
||||
class SignContentExtractor
|
||||
{
|
||||
private $RESPONSE_SUFFIX = "_response";
|
||||
private $ERROR_RESPONSE = "error_response";
|
||||
|
||||
/**
|
||||
* @param $body string 网关的整体响应字符串
|
||||
* @param $method string 本次调用的OpenAPI接口名称
|
||||
* @return false|string|null 待验签的原文
|
||||
*/
|
||||
public function getSignSourceData($body, $method)
|
||||
{
|
||||
$rootNodeName = str_replace(".", "_", $method) . $this->RESPONSE_SUFFIX;
|
||||
$rootIndex = strpos($body, $rootNodeName);
|
||||
if ($rootIndex !== strrpos($body, $rootNodeName)) {
|
||||
throw new \Exception('检测到响应报文中有重复的' . $rootNodeName . ',验签失败。');
|
||||
}
|
||||
$errorIndex = strpos($body, $this->ERROR_RESPONSE);
|
||||
if ($rootIndex > 0) {
|
||||
return $this->parserJSONSource($body, $rootNodeName, $rootIndex);
|
||||
} else if ($errorIndex > 0) {
|
||||
return $this->parserJSONSource($body, $this->ERROR_RESPONSE, $errorIndex);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $responseContent
|
||||
* @param $nodeName
|
||||
* @param $nodeIndex
|
||||
* @return false|string|null
|
||||
*/
|
||||
function parserJSONSource($responseContent, $nodeName, $nodeIndex)
|
||||
{
|
||||
$signDataStartIndex = $nodeIndex + strlen($nodeName) + 2;
|
||||
if (strrpos($responseContent, AlipayConstants::ALIPAY_CERT_SN_FIELD)) {
|
||||
$signIndex = strrpos($responseContent, "\"" . AlipayConstants::ALIPAY_CERT_SN_FIELD . "\"");
|
||||
} else {
|
||||
$signIndex = strrpos($responseContent, "\"" . AlipayConstants::SIGN_FIELD . "\"");
|
||||
}
|
||||
// 签名前-逗号
|
||||
$signDataEndIndex = $signIndex - 1;
|
||||
$indexLen = $signDataEndIndex - $signDataStartIndex;
|
||||
if ($indexLen < 0) {
|
||||
return null;
|
||||
}
|
||||
return substr($responseContent, $signDataStartIndex, $indexLen);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Alipay\EasySDK\Kernel\Util;
|
||||
|
||||
class Signer
|
||||
{
|
||||
/**
|
||||
* @param $content string 待签名的内容
|
||||
* @param $privateKeyPem string 私钥
|
||||
* @return string 签名值的Base64串
|
||||
*/
|
||||
public function sign($content, $privateKeyPem)
|
||||
{
|
||||
$priKey = $privateKeyPem;
|
||||
$res = "-----BEGIN RSA PRIVATE KEY-----\n" .
|
||||
wordwrap($priKey, 64, "\n", true) .
|
||||
"\n-----END RSA PRIVATE KEY-----";
|
||||
($res) or die('您使用的私钥格式错误,请检查RSA私钥配置');
|
||||
|
||||
openssl_sign($content, $sign, $res, OPENSSL_ALGO_SHA256);
|
||||
$sign = base64_encode($sign);
|
||||
return $sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $content string 待验签的内容
|
||||
* @param $sign string 签名值的Base64串
|
||||
* @param $publicKeyPem string 支付宝公钥
|
||||
* @return bool true:验证成功;false:验证失败
|
||||
*/
|
||||
public function verify($content, $sign, $publicKeyPem)
|
||||
{
|
||||
$pubKey = $publicKeyPem;
|
||||
$res = "-----BEGIN PUBLIC KEY-----\n" .
|
||||
wordwrap($pubKey, 64, "\n", true) .
|
||||
"\n-----END PUBLIC KEY-----";
|
||||
($res) or die('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
|
||||
|
||||
//调用openssl内置方法验签,返回bool值
|
||||
$result = FALSE;
|
||||
$result = (openssl_verify($content, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function verifyParams($parameters, $publicKey)
|
||||
{
|
||||
$sign = $parameters['sign'];
|
||||
$content = $this->getSignContent($parameters);
|
||||
return $this->verify($content, $sign, $publicKey);
|
||||
}
|
||||
|
||||
public function getSignContent($params)
|
||||
{
|
||||
ksort($params);
|
||||
unset($params['sign']);
|
||||
unset($params['sign_type']);
|
||||
$stringToBeSigned = "";
|
||||
$i = 0;
|
||||
foreach ($params as $k => $v) {
|
||||
if ("@" != substr($v, 0, 1)) {
|
||||
if ($i == 0) {
|
||||
$stringToBeSigned .= "$k" . "=" . "$v";
|
||||
} else {
|
||||
$stringToBeSigned .= "&" . "$k" . "=" . "$v";
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
unset ($k, $v);
|
||||
return $stringToBeSigned;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user