first commit

This commit is contained in:
2024-04-01 09:54:43 +08:00
commit 899d816bc3
795 changed files with 130040 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
class AuthorityReqRegistryImpl implements AuthorizationReqRegistry
{
private $authorizationReqs = [];
/**
* @param $operationId string
* @param $securityReq AuthorizationReq
* @return $this
*/
public function register($operationId, $securityReq)
{
$this->authorizationReqs[$operationId] = $securityReq;
return $this;
}
public function getAuthorizationReq($operationId)
{
return $this->authorizationReqs[$operationId];
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
class AuthorizationReq
{
/**
* @var string
*/
private $signerType;
/**
* @var string
*/
private $credentialType;
/**
* @var string
*/
private $signatureAlg;
/**
* @var string
*/
private $digestAlg;
/**
* @var string
*/
private $protocolPrefix;
/**
* AuthorizationReq constructor.
* @param string $signerType
* @param string $credentialType
* @param string $signatureAlg
* @param string $digestAlg
* @param string $protocolPrefix
*/
public function __construct($signerType, $credentialType, $signatureAlg, $digestAlg, $protocolPrefix)
{
$this->signerType = $signerType;
$this->credentialType = $credentialType;
$this->signatureAlg = $signatureAlg;
$this->digestAlg = $digestAlg;
$this->protocolPrefix = $protocolPrefix;
}
/**
* @return string
*/
public function getSignerType()
{
return $this->signerType;
}
/**
* @param string $signerType
* @return AuthorizationReq
*/
public function setSignerType($signerType)
{
$this->signerType = $signerType;
return $this;
}
/**
* @return string
*/
public function getCredentialType()
{
return $this->credentialType;
}
/**
* @param string $credentialType
* @return AuthorizationReq
*/
public function setCredentialType($credentialType)
{
$this->credentialType = $credentialType;
return $this;
}
/**
* @return string
*/
public function getSignatureAlg()
{
return $this->signatureAlg;
}
/**
* @param string $signatureAlg
* @return AuthorizationReq
*/
public function setSignatureAlg($signatureAlg)
{
$this->signatureAlg = $signatureAlg;
return $this;
}
/**
* @return string
*/
public function getDigestAlg()
{
return $this->digestAlg;
}
/**
* @param string $digestAlg
* @return AuthorizationReq
*/
public function setDigestAlg($digestAlg)
{
$this->digestAlg = $digestAlg;
return $this;
}
/**
* @return string
*/
public function getProtocolPrefix()
{
return $this->protocolPrefix;
}
/**
* @param string $protocolPrefix
* @return AuthorizationReq
*/
public function setProtocolPrefix($protocolPrefix)
{
$this->protocolPrefix = $protocolPrefix;
return $this;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
interface AuthorizationReqRegistry
{
/**
* @param $operationId string
* @return AuthorizationReq
*/
public function getAuthorizationReq($operationId);
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
use Yeepay\Yop\Sdk\Security\DigestAlg;
class AuthorizationReqSupport
{
/**
* @var array
*/
private static $supportedAuthorizationReqs;
static function init()
{
self::$supportedAuthorizationReqs =
[
"YOP-RSA2048-SHA256" => new AuthorizationReq("RSA", "RSA2048", "SHA256withRSA", DigestAlg::SHA256,
"YOP-RSA2048-SHA256"),
"YOP-RSA2048-SHA512" => new AuthorizationReq("RSA", "RSA2048", "SHA512withRSA", DigestAlg::SHA512,
"YOP-RSA2048-SHA512"),
];
}
/**
* @param string $securityReqName
* @return AuthorizationReq
*/
static function getAuthorizationReq($securityReqName)
{
return self::$supportedAuthorizationReqs[$securityReqName];
}
}
AuthorizationReqSupport::init();

View File

@@ -0,0 +1,75 @@
<?php
namespace Yeepay\Yop\Sdk\Auth\Cipher;
use Yeepay\Yop\Sdk\Auth\Encryptor;
use Yeepay\Yop\Sdk\Http\ContentType;
use Yeepay\Yop\Sdk\Http\Headers;
use Yeepay\Yop\Sdk\Internal\Request;
use Yeepay\Yop\Sdk\Security\Aes\AesEncryptor;
class DefaultEncryptor implements Encryptor
{
/**
* @var string
*/
private $key;
/**
* @var int
*/
private $keyLength;
/**
* DefaultEncryptor constructor.
* @param $base64EncodedKey string
*/
public function __construct($base64EncodedKey)
{
$this->key = base64_decode($base64EncodedKey);
$this->keyLength = strlen($this->key);
}
/**
* @param $request
* @return mixed
*/
public function encrypt(Request $request)
{
if ($request->getContentType() == ContentType::APPLICATION_OCTET_STREAM) {
return;
}
$request->addHeader(Headers::YOP_ENCRYPT_TYPE, "aes".$this->keyLength);
if (!empty($request->getParameters()) > 0) {
$encryptedParameters = [];
foreach ($request->getParameters() as $paramName => $paramValues) {
$encryptedParamValues = [];
foreach ($paramValues as $paramValue) {
$encryptedParamValues[] = AesEncryptor::encryptAndEncodeBase64($paramValue, $this->key);
}
$encryptedParameters[$paramName] = $encryptedParamValues;
}
$request->setParameters($encryptedParameters);
}
if (empty($request->getContent())) {
if (is_string($request->getContent())) {
$request->setContent(AesEncryptor::decodeBase64AndDecrypt($request->getContent(), $this->key));
} else {
if (is_resource($request->getContent())) {
//todo 忽略对resource的签名
}
}
}
}
/**
* @param $content string
* @return string
*/
public function decrypt($content)
{
return AesEncryptor::decodeBase64AndDecrypt($content, $this->key);
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Yeepay\Yop\Sdk\Auth\Credential;
use Yeepay\Yop\Sdk\Auth\YopCredentialProvider;
use Yeepay\Yop\Sdk\Auth\YopRsaCredentials;
use Yeepay\Yop\Sdk\Config\AppSdkConfig;
use Yeepay\Yop\Sdk\Config\AppSdkConfigProvider;
use Yeepay\Yop\Sdk\Config\Support\ConfigUtils;
use Yeepay\Yop\Sdk\Exception\YopClientException;
class DefaultCredentialProvider implements YopCredentialProvider
{
/**
* @var array
*/
private $credentials;
/**
* @var array
*/
private $defaultAppCredentials;
/**
* @var array
*/
private $publicKeys;
/**
* DefaultCredentialProvider constructor.
* @param AppSdkConfigProvider $appSdkConfigProvider
* @throws YopClientException
*/
public function __construct(AppSdkConfigProvider $appSdkConfigProvider)
{
$this->credentials = [];
foreach ($appSdkConfigProvider->getAllConfig() as $appKey => $appKeySdkConfig) {
/* @var $appKeySdkConfig AppSdkConfig */
$appKeyCredentials = [];
if (!empty($appKeySdkConfig->getIsvPrivateKeys())) {
foreach ($appKeySdkConfig->getIsvPrivateKeys() as $credentialType => $isvPrivateKey) {
/* @var $isvPrivateKey string|resource */
if (is_string($isvPrivateKey)) {
$appKeyCredentials[$credentialType] = new YopRsaCredentials($appKey,
ConfigUtils::getPrivateKey($isvPrivateKey),
$appKeySdkConfig->getEncryptKey());
} else {
if (is_resource($isvPrivateKey)) {
$appKeyCredentials[$credentialType] = new YopRsaCredentials($appKey, $isvPrivateKey,
$appKeySdkConfig->getEncryptKey());
}
}
}
}
if (!empty($appKeyCredentials)) {
$this->credentials[$appKey] = $appKeyCredentials;
}
}
$this->defaultAppCredentials = $this->credentials[$appSdkConfigProvider->getDefaultConfig()->getAppKey()];
$this->publicKeys = $appSdkConfigProvider->getDefaultConfig()->getYopPublicKeys();
}
/**
* @param $appKey string
* @param $credentialType string
* @return YopRsaCredentials
*/
public function getCredential($appKey, $credentialType)
{
if (isset($this->credentials[$appKey])) {
return $this->credentials[$appKey][$credentialType];
}
}
/**
* @param $credentialType string
* @return YopRsaCredentials
*/
public function getDefaultAppCredential($credentialType)
{
return $this->defaultAppCredentials[$credentialType];
}
/**
* @param $credentialType string
* @return resource
*/
public function getYopPublicKey($credentialType)
{
return $this->publicKeys[$credentialType];
}
}

22
lib/Auth/Encryptor.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
use Yeepay\Yop\Sdk\Internal\Request;
interface Encryptor
{
/**
* @param $request
* @return mixed
*/
public function encrypt(Request $request);
/**
* @param $content
* @return mixed
*/
public function decrypt($content);
}

97
lib/Auth/SignOptions.php Normal file
View File

@@ -0,0 +1,97 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
class SignOptions
{
const DEFAULT_EXPIRATION_IN_SECONDS = 1800;
/**
* @var string
*/
private $digestAlg;
/**
* @var string
*/
private $protocolPrefix;
/**
* @var int
*/
private $expirationInSeconds = self::DEFAULT_EXPIRATION_IN_SECONDS;
/**
* SignOptions constructor.
* @param string $digestAlg
* @param string $protocolPrefix
* @param int $expirationInSeconds
*/
public function __construct($digestAlg, $protocolPrefix, $expirationInSeconds = null)
{
$this->digestAlg = $digestAlg;
$this->protocolPrefix = $protocolPrefix;
if (isset($expirationInSeconds)) {
$this->expirationInSeconds = $expirationInSeconds;
}
}
/**
* @return string
*/
public function getDigestAlg()
{
return $this->digestAlg;
}
/**
* @param string $digestAlg
* @return SignOptions
*/
public function setDigestAlg($digestAlg)
{
$this->digestAlg = $digestAlg;
return $this;
}
/**
* @return string
*/
public function getProtocolPrefix()
{
return $this->protocolPrefix;
}
/**
* @param string $protocolPrefix
* @return SignOptions
*/
public function setProtocolPrefix($protocolPrefix)
{
$this->protocolPrefix = $protocolPrefix;
return $this;
}
/**
* @return int
*/
public function getExpirationInSeconds()
{
return $this->expirationInSeconds;
}
/**
* @param int $expirationInSeconds
* @return SignOptions
*/
public function setExpirationInSeconds($expirationInSeconds)
{
$this->expirationInSeconds = $expirationInSeconds;
return $this;
}
}

26
lib/Auth/Signer.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
use Yeepay\Yop\Sdk\Http\YopHttpResponse;
use Yeepay\Yop\Sdk\Internal\Request;
interface Signer
{
/**
* @param Request $request
* @param YopRsaCredentials|null $credentials
* @param SignOptions|null $options
*/
public function sign(Request $request, YopRsaCredentials $credentials = null, SignOptions $options = null);
/**
* @param YopHttpResponse $httpResponse
* @param string $signature
* @param resource $publicKey
* @param SignOptions $options
*/
public function checkSignature(YopHttpResponse $httpResponse, $signature, $publicKey, SignOptions $options);
}

View File

@@ -0,0 +1,199 @@
<?php
namespace Yeepay\Yop\Sdk\Auth\Signer;
use DateTime;
use Yeepay\Yop\Sdk\Auth\Signer;
use Yeepay\Yop\Sdk\Auth\SignOptions;
use Yeepay\Yop\Sdk\Auth\YopRsaCredentials;
use Yeepay\Yop\Sdk\Exception\VerifySignFailedException;
use Yeepay\Yop\Sdk\Exception\YopClientException;
use Yeepay\Yop\Sdk\Http\Headers;
use Yeepay\Yop\Sdk\Http\YopHttpResponse;
use Yeepay\Yop\Sdk\Internal\Request;
use Yeepay\Yop\Sdk\Security\Encodes;
use Yeepay\Yop\Sdk\Utils\DateUtils;
use Yeepay\Yop\Sdk\Utils\Http\HttpUtils;
class RsaSigner implements Signer
{
private static $yopAuthVersion = 'yop-auth-v3';
private static $defaultHeadersToSign;
private static $headerJoiner = "\n";
private static $signedHeaderStringJoiner = ';';
private $logger;
public static function __init()
{
self::$defaultHeadersToSign = [
strtolower(Headers::CONTENT_LENGTH),
strtolower(Headers::CONTENT_TYPE),
strtolower(Headers::CONTENT_DISPOSITION),
strtolower(Headers::CONTENT_MD5),
strtolower(Headers::YOP_REQUEST_ID),
strtolower(Headers::YOP_DATE),
strtolower(Headers::YOP_APPKEY),
strtolower(Headers::YOP_CONTENT_SHA256),
strtolower(Headers::YOP_HASH_CRC64ECMA),
];
}
public function sign(Request $request, YopRsaCredentials $credentials = null, SignOptions $options = null)
{
if ($credentials == null) {
return;
}
$accessKeyId = $credentials->getAppKey();
$request->addHeader(Headers::HOST, HttpUtils:: generateHostHeader($request->getEndpoint()));
$timestamp = new DateTime();
$timestamp->setTimezone(DateUtils::$UTC_TIMEZONE);
$contentSha256 = $this->calculateContentHash($request);
if (isset($contentSha256)) {
$request->addHeader(Headers::YOP_CONTENT_SHA256, $contentSha256);
}
$canonicalQueryString = $this->getCanonicalQueryString($request);
$headersToSign = $this->getHeadersToSign($request->getHeaders(), self::$defaultHeadersToSign);
$canonicalHeader = $this->getCanonicalHeaders($headersToSign);
$signedHeaders = strtolower(trim(implode(self::$signedHeaderStringJoiner, array_keys($headersToSign))));
$authString = self::$yopAuthVersion.'/'.$accessKeyId.'/'.DateUtils::formatAlternateIso8601Date($timestamp).'/'
.$options->getExpirationInSeconds();
$canonicalURI = $this->getCanonicalURIPath($request->getResourcePath());
$canonicalRequest = $authString.self::$headerJoiner.$request->getHttpMethod().self::$headerJoiner.$canonicalURI
.self::$headerJoiner.$canonicalQueryString.self::$headerJoiner.$canonicalHeader;
$signature = $this->computeSignature($canonicalRequest, $credentials->getPrivateKey(),
$options->getDigestAlg());
$authorizationHeader = $options->getProtocolPrefix().' '.$authString.'/'.$signedHeaders.'/'.$signature;
$request->addHeader(Headers::AUTHORIZATION, $authorizationHeader);
}
private function calculateContentHash(Request $request)
{
$content = null;
if (HttpUtils::usePayloadForQueryParameters($request)) {
$content = HttpUtils::getCanonicalQueryString($request->getParameters(), true);
} else {
if (is_string($request->getContent())) {
$content = $request->getContent();
}
}
if (isset($content)) {
return hash('sha256', $content);
} else {
return null;
}
}
private function getCanonicalQueryString(Request $request)
{
if (HttpUtils::usePayloadForQueryParameters($request)) {
return '';
}
return HttpUtils::getCanonicalQueryString($request->getParameters(), true);
}
/**
* @param $path string
* @return string
*/
private function getCanonicalURIPath($path)
{
if (empty($path)) {
return '/';
} else {
if ($path[0] == '/') {
return HttpUtils::urlEncodeExceptSlash($path);
} else {
return '/'.HttpUtils::urlEncodeExceptSlash($path);
}
}
}
/**
* @param $headers array
* @return string
*/
private function getCanonicalHeaders($headers)
{
if (count($headers) == 0) {
return '';
}
$headerStrings = [];
foreach ($headers as $k => $v) {
if ($k === null) {
continue;
}
if ($v === null) {
$v = '';
}
$headerStrings[] = rawurlencode(
strtolower(trim($k))
).':'.rawurlencode(trim($v));
}
sort($headerStrings);
return implode(self::$headerJoiner, $headerStrings);
}
/**
* @param $headers array
* @param $headersToSign array
* @return array
*/
private function getHeadersToSign($headers, $headersToSign)
{
$ret = [];
if ($headersToSign !== null) {
$tmp = [];
foreach ($headersToSign as $header) {
$tmp[] = strtolower(trim($header));
}
$headersToSign = $tmp;
}
foreach ($headers as $k => $v) {
if (trim((string) $v) !== '') {
if ($headersToSign !== null) {
$k = strtolower(trim($k));
if (in_array($k, $headersToSign) && $k != trim(Headers::AUTHORIZATION)) {
$ret[$k] = $v;
}
}
}
}
return $ret;
}
private function computeSignature($canonicalRequest, $privateKey, $digestAlg)
{
$signature = null;
if (openssl_sign($canonicalRequest, $signature, $privateKey, $digestAlg)) {
return Encodes::base64url_encode($signature).'$'.$digestAlg;
}
throw new YopClientException('compute signature failed.');
}
public function checkSignature(YopHttpResponse $httpResponse, $signature, $publicKey, SignOptions $options)
{
$content = $httpResponse->readContent();
$content = str_replace([" ", "\n", "\t"], "", $content);
if (openssl_verify($content, base64_decode($signature), $publicKey, $options->getDigestAlg()) == 1) {
return;
}
throw new VerifySignFailedException("response sign verify failure");
}
}
RsaSigner::__init();

View File

@@ -0,0 +1,29 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
use Yeepay\Yop\Sdk\Auth\Signer\RsaSigner;
class SignerFactory
{
private static $signers = [];
public static function init()
{
self::$signers['RSA'] = new RsaSigner();
}
/**
* @param $signerType string
* @return Signer
*/
public static function getSigner($signerType)
{
return self::$signers[$signerType];
}
}
SignerFactory::init();

View File

@@ -0,0 +1,27 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
interface YopCredentialProvider
{
/**
* @param $appKey string
* @param $credentialType string
* @return YopRsaCredentials
*/
public function getCredential($appKey, $credentialType);
/**
* @param $credentialType string
* @return YopRsaCredentials
*/
public function getDefaultAppCredential($credentialType);
/**
* @param $credentialType string
* @return resource
*/
public function getYopPublicKey($credentialType);
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Yeepay\Yop\Sdk\Auth;
class YopRsaCredentials
{
/**
* @var string
*/
private $appKey;
/**
* @var resource
*/
private $privateKey;
/**
* @var string
*/
private $encryptKey;
/**
* YopRsaCredentials constructor.
* @param string $appKey
* @param resource $privateKey
* @param string $encryptKey
*/
public function __construct($appKey, $privateKey, $encryptKey)
{
$this->appKey = $appKey;
$this->privateKey = $privateKey;
$this->encryptKey = $encryptKey;
}
/**
* @return string
*/
public function getAppKey()
{
return $this->appKey;
}
/**
* @param string $appKey
* @return YopRsaCredentials
*/
public function setAppKey($appKey)
{
$this->appKey = $appKey;
return $this;
}
/**
* @return resource
*/
public function getPrivateKey()
{
return $this->privateKey;
}
/**
* @param resource $privateKey
* @return YopRsaCredentials
*/
public function setPrivateKey($privateKey)
{
$this->privateKey = $privateKey;
return $this;
}
/**
* @return string
*/
public function getEncryptKey()
{
return $this->encryptKey;
}
/**
* @param string $encryptKey
* @return YopRsaCredentials
*/
public function setEncryptKey($encryptKey)
{
$this->encryptKey = $encryptKey;
return $this;
}
}