存客宝应用接口初始化
This commit is contained in:
79
application/common/util/AliyunOSS.php
Normal file
79
application/common/util/AliyunOSS.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace app\common\util;
|
||||
|
||||
use OSS\OssClient;
|
||||
use OSS\Core\OssException;
|
||||
use think\facade\Env;
|
||||
|
||||
class AliyunOSS
|
||||
{
|
||||
// OSS配置信息
|
||||
const ACCESS_KEY_ID = 'LTAIxvJUmlt2gLiY';
|
||||
const ACCESS_KEY_SECRET = '0WUo8r6BT4I8ZVUQxflmD8rLHrFNHO';
|
||||
const ENDPOINT = 'oss-cn-shenzhen.aliyuncs.com';
|
||||
const BUCKET = 'karuosiyujzk';
|
||||
const ossUrl = 'https://res.quwanzhi.com';
|
||||
|
||||
/**
|
||||
* 获取OSS客户端实例
|
||||
* @return OssClient
|
||||
* @throws OssException
|
||||
*/
|
||||
public static function getClient()
|
||||
{
|
||||
try {
|
||||
return new OssClient(
|
||||
self::ACCESS_KEY_ID,
|
||||
self::ACCESS_KEY_SECRET,
|
||||
self::ENDPOINT
|
||||
);
|
||||
} catch (OssException $e) {
|
||||
throw new OssException('创建OSS客户端失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到OSS
|
||||
* @param string $filePath 本地文件路径
|
||||
* @param string $objectName OSS对象名称
|
||||
* @return array
|
||||
* @throws OssException
|
||||
*/
|
||||
public static function uploadFile($filePath, $objectName)
|
||||
{
|
||||
try {
|
||||
$client = self::getClient();
|
||||
|
||||
// 上传文件
|
||||
$result = $client->uploadFile(self::BUCKET, $objectName, $filePath);
|
||||
|
||||
// 获取文件访问URL
|
||||
$url = !empty($result['oss-request-url']) ? $result['oss-request-url'] : $client->signUrl(self::BUCKET, $objectName, 3600);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'url' => $url,
|
||||
'object_name' => $objectName,
|
||||
'size' => filesize($filePath),
|
||||
'mime_type' => mime_content_type($filePath)
|
||||
];
|
||||
} catch (OssException $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成OSS对象名称
|
||||
* @param string $originalName 原始文件名
|
||||
* @return string
|
||||
*/
|
||||
public static function generateObjectName($originalName)
|
||||
{
|
||||
$ext = pathinfo($originalName, PATHINFO_EXTENSION);
|
||||
$name = md5(uniqid(mt_rand(), true));
|
||||
return date('Y/m/d/') . $name . '.' . $ext;
|
||||
}
|
||||
}
|
||||
65
application/common/util/AliyunSMS.php
Normal file
65
application/common/util/AliyunSMS.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\util;
|
||||
|
||||
use Darabonba\OpenApi\Models\Config;
|
||||
use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi;
|
||||
use AlibabaCloud\SDK\Dysmsapi\V20170525\Models\SendSmsRequest;
|
||||
use AlibabaCloud\Tea\Utils\Utils\RuntimeOptions;
|
||||
|
||||
class AliyunSMS {
|
||||
|
||||
const ACCESS_KEY_ID = 'LTAI5tFjVRYAFmo6fayvv2Te';
|
||||
const ACCESS_KEY_SECRET = 'mdc7KETPGVon8PiA2kfM47rAOpJne8';
|
||||
const SIGN_NAME = '广州宏科网络';
|
||||
|
||||
const TC_VCODE = 'SMS_464445466';
|
||||
|
||||
static public function createClient() {
|
||||
$config = new Config([
|
||||
// AccessKey ID
|
||||
'accessKeyId' => static::ACCESS_KEY_ID,
|
||||
// AccessKey Secret
|
||||
'accessKeySecret' => static::ACCESS_KEY_SECRET
|
||||
]);
|
||||
|
||||
// 访问的域名
|
||||
$config->endpoint = 'dysmsapi.aliyuncs.com';
|
||||
|
||||
return new Dysmsapi($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
*
|
||||
* @param $phoneNumbers
|
||||
* @param $templateCode
|
||||
* @param array $templateParam
|
||||
* @return bool
|
||||
*/
|
||||
static public function send($phoneNumbers, $templateCode, array $templateParam = array()) {
|
||||
$client = static::createClient();
|
||||
$sendSmsRequest = new SendSmsRequest([
|
||||
'phoneNumbers' => $phoneNumbers,
|
||||
'signName' => static::SIGN_NAME,
|
||||
'templateCode' => $templateCode,
|
||||
'templateParam' => json_encode($templateParam)
|
||||
]);
|
||||
$runtime = new RuntimeOptions([]);
|
||||
$logFile = ROOT_PATH . DS . 'aliyun-sms.txt';
|
||||
try {
|
||||
$data = $client->sendSmsWithOptions($sendSmsRequest, $runtime);
|
||||
if ($data->body->code === 'OK') {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
$logData = print_r($data, TRUE);
|
||||
} catch (\Exception $ex) {
|
||||
$logData = print_r($ex, TRUE);
|
||||
}
|
||||
|
||||
file_put_contents($logFile, '[' . date('Y-m-d H:i:s') . ']' . PHP_EOL . $logData . PHP_EOL . PHP_EOL, FILE_APPEND);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
137
application/common/util/JwtUtil.php
Normal file
137
application/common/util/JwtUtil.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
namespace app\common\util;
|
||||
|
||||
use think\facade\Config;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* JWT工具类
|
||||
* 用于生成和验证JWT令牌
|
||||
*/
|
||||
class JwtUtil
|
||||
{
|
||||
/**
|
||||
* 密钥
|
||||
* @var string
|
||||
*/
|
||||
protected static $secret = 'YiShi@2023#JWT';
|
||||
|
||||
/**
|
||||
* 头部
|
||||
* @var array
|
||||
*/
|
||||
protected static $header = [
|
||||
'alg' => 'HS256', // 加密算法
|
||||
'typ' => 'JWT' // 类型
|
||||
];
|
||||
|
||||
/**
|
||||
* 创建JWT令牌
|
||||
* @param array $payload 载荷信息
|
||||
* @param int $expire 过期时间(秒),默认2小时
|
||||
* @return string
|
||||
*/
|
||||
public static function createToken($payload, $expire = 7200)
|
||||
{
|
||||
$header = self::base64UrlEncode(json_encode(self::$header, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 附加过期时间
|
||||
$payload['exp'] = time() + $expire;
|
||||
$payload['iat'] = time(); // 签发时间
|
||||
|
||||
unset($payload['passwordMd5']);
|
||||
|
||||
$payload = self::base64UrlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE));
|
||||
$signature = self::signature($header . '.' . $payload, self::$secret);
|
||||
|
||||
return $header . '.' . $payload . '.' . $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证令牌
|
||||
* @param string $token 令牌
|
||||
* @return array|bool 验证通过返回载荷信息,失败返回false
|
||||
*/
|
||||
public static function verifyToken($token)
|
||||
{
|
||||
if (empty($token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tokenArray = explode('.', $token);
|
||||
if (count($tokenArray) != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list($header, $payload, $signature) = $tokenArray;
|
||||
|
||||
// 验证签名
|
||||
if (self::signature($header . '.' . $payload, self::$secret) !== $signature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解码载荷
|
||||
$payload = json_decode(self::base64UrlDecode($payload), true);
|
||||
|
||||
// 验证是否过期
|
||||
if (isset($payload['exp']) && $payload['exp'] < time()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* @param string $input 输入
|
||||
* @param string $key 密钥
|
||||
* @return string
|
||||
*/
|
||||
private static function signature($input, $key)
|
||||
{
|
||||
return self::base64UrlEncode(hash_hmac('sha256', $input, $key, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL安全的Base64编码
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private static function base64UrlEncode($input)
|
||||
{
|
||||
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($input));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL安全的Base64解码
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private static function base64UrlDecode($input)
|
||||
{
|
||||
$remainder = strlen($input) % 4;
|
||||
if ($remainder) {
|
||||
$input .= str_repeat('=', 4 - $remainder);
|
||||
}
|
||||
return base64_decode(str_replace(['-', '_'], ['+', '/'], $input));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头中获取Token
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getRequestToken()
|
||||
{
|
||||
$authorization = Request::header('Authorization');
|
||||
if (!$authorization) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查Bearer前缀
|
||||
if (strpos($authorization, 'Bearer ') !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return substr($authorization, 7);
|
||||
}
|
||||
}
|
||||
255
application/common/util/PaymentUtil.php
Normal file
255
application/common/util/PaymentUtil.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\util;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 支付工具类
|
||||
* 用于处理第三方支付相关功能
|
||||
* 仅限内部调用
|
||||
*/
|
||||
class PaymentUtil
|
||||
{
|
||||
/**
|
||||
* 签名算法类型
|
||||
*/
|
||||
const SIGN_TYPE_MD5 = 'MD5';
|
||||
const SIGN_TYPE_RSA_1_256 = 'RSA_1_256';
|
||||
const SIGN_TYPE_RSA_1_1 = 'RSA_1_1';
|
||||
|
||||
/**
|
||||
* 生成支付签名
|
||||
*
|
||||
* @param array $params 待签名参数
|
||||
* @param string $secretKey 签名密钥
|
||||
* @param string $signType 签名类型 MD5/RSA_1_256/RSA_1_1
|
||||
* @return string 签名结果
|
||||
*/
|
||||
public static function generateSign(array $params, string $secretKey, string $signType = self::SIGN_TYPE_MD5): string
|
||||
{
|
||||
// 1. 移除sign字段
|
||||
unset($params['sign']);
|
||||
|
||||
// 2. 过滤空值
|
||||
$params = array_filter($params, function($value) {
|
||||
return $value !== '' && $value !== null;
|
||||
});
|
||||
|
||||
// 3. 按字段名ASCII码从小到大排序
|
||||
ksort($params);
|
||||
|
||||
// 4. 拼接成QueryString格式
|
||||
$queryString = self::buildQueryString($params);
|
||||
|
||||
// 5. 根据签名类型生成签名
|
||||
switch (strtoupper($signType)) {
|
||||
case self::SIGN_TYPE_MD5:
|
||||
return self::generateMd5Sign($queryString, $secretKey);
|
||||
case self::SIGN_TYPE_RSA_1_256:
|
||||
return self::generateRsa256Sign($queryString, $secretKey);
|
||||
case self::SIGN_TYPE_RSA_1_1:
|
||||
return self::generateRsa1Sign($queryString, $secretKey);
|
||||
default:
|
||||
throw new \InvalidArgumentException('不支持的签名类型: ' . $signType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证支付签名
|
||||
*
|
||||
* @param array $params 待验证参数(包含sign字段)
|
||||
* @param string $secretKey 签名密钥
|
||||
* @param string $signType 签名类型
|
||||
* @return bool 验证结果
|
||||
*/
|
||||
public static function verifySign(array $params, string $secretKey, string $signType = self::SIGN_TYPE_MD5): bool
|
||||
{
|
||||
if (!isset($params['sign'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$receivedSign = $params['sign'];
|
||||
$generatedSign = self::generateSign($params, $secretKey, $signType);
|
||||
|
||||
return $receivedSign === $generatedSign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建QueryString
|
||||
*
|
||||
* @param array $params 参数数组
|
||||
* @return string QueryString
|
||||
*/
|
||||
private static function buildQueryString(array $params): string
|
||||
{
|
||||
$pairs = [];
|
||||
foreach ($params as $key => $value) {
|
||||
$pairs[] = $key . '=' . $value;
|
||||
}
|
||||
return implode('&', $pairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成MD5签名
|
||||
*
|
||||
* @param string $queryString 待签名字符串
|
||||
* @param string $secretKey 密钥
|
||||
* @return string MD5签名
|
||||
*/
|
||||
private static function generateMd5Sign(string $queryString, string $secretKey): string
|
||||
{
|
||||
$signString = $queryString . '&key=' . $secretKey;
|
||||
return strtoupper(md5($signString));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成RSA256签名
|
||||
*
|
||||
* @param string $queryString 待签名字符串
|
||||
* @param string $privateKey 私钥
|
||||
* @return string RSA256签名
|
||||
*/
|
||||
private static function generateRsa256Sign(string $queryString, string $privateKey): string
|
||||
{
|
||||
$privateKey = self::formatPrivateKey($privateKey);
|
||||
$key = openssl_pkey_get_private($privateKey);
|
||||
if (!$key) {
|
||||
throw new \Exception('RSA私钥格式错误');
|
||||
}
|
||||
|
||||
$signature = '';
|
||||
$result = openssl_sign($queryString, $signature, $key, OPENSSL_ALGO_SHA256);
|
||||
openssl_pkey_free($key);
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception('RSA256签名失败');
|
||||
}
|
||||
|
||||
return base64_encode($signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成RSA1签名
|
||||
*
|
||||
* @param string $queryString 待签名字符串
|
||||
* @param string $privateKey 私钥
|
||||
* @return string RSA1签名
|
||||
*/
|
||||
private static function generateRsa1Sign(string $queryString, string $privateKey): string
|
||||
{
|
||||
$privateKey = self::formatPrivateKey($privateKey);
|
||||
$key = openssl_pkey_get_private($privateKey);
|
||||
if (!$key) {
|
||||
throw new \Exception('RSA私钥格式错误');
|
||||
}
|
||||
|
||||
$signature = '';
|
||||
$result = openssl_sign($queryString, $signature, $key, OPENSSL_ALGO_SHA1);
|
||||
openssl_pkey_free($key);
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception('RSA1签名失败');
|
||||
}
|
||||
|
||||
return base64_encode($signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化私钥
|
||||
*
|
||||
* @param string $privateKey 原始私钥
|
||||
* @return string 格式化后的私钥
|
||||
*/
|
||||
private static function formatPrivateKey(string $privateKey): string
|
||||
{
|
||||
$privateKey = str_replace(['-----BEGIN PRIVATE KEY-----', '-----END PRIVATE KEY-----', "\n", "\r"], '', $privateKey);
|
||||
$privateKey = chunk_split($privateKey, 64, "\n");
|
||||
return "-----BEGIN PRIVATE KEY-----\n" . $privateKey . "-----END PRIVATE KEY-----";
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化公钥
|
||||
*
|
||||
* @param string $publicKey 原始公钥
|
||||
* @return string 格式化后的公钥
|
||||
*/
|
||||
private static function formatPublicKey(string $publicKey): string
|
||||
{
|
||||
$publicKey = str_replace(['-----BEGIN PUBLIC KEY-----', '-----END PUBLIC KEY-----', "\n", "\r"], '', $publicKey);
|
||||
$publicKey = chunk_split($publicKey, 64, "\n");
|
||||
return "-----BEGIN PUBLIC KEY-----\n" . $publicKey . "-----END PUBLIC KEY-----";
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证RSA签名
|
||||
*
|
||||
* @param string $queryString 原始字符串
|
||||
* @param string $signature 签名
|
||||
* @param string $publicKey 公钥
|
||||
* @param string $signType 签名类型
|
||||
* @return bool 验证结果
|
||||
*/
|
||||
public static function verifyRsaSign(string $queryString, string $signature, string $publicKey, string $signType = self::SIGN_TYPE_RSA_1_256): bool
|
||||
{
|
||||
$publicKey = self::formatPublicKey($publicKey);
|
||||
$key = openssl_pkey_get_public($publicKey);
|
||||
if (!$key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$algorithm = $signType === self::SIGN_TYPE_RSA_1_1 ? OPENSSL_ALGO_SHA1 : OPENSSL_ALGO_SHA256;
|
||||
$result = openssl_verify($queryString, base64_decode($signature), $key, $algorithm);
|
||||
openssl_pkey_free($key);
|
||||
|
||||
return $result === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
*
|
||||
* @param int $length 长度
|
||||
* @return string 随机字符串
|
||||
*/
|
||||
public static function generateNonceStr(int $length = 32): string
|
||||
{
|
||||
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
$str = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$str .= $chars[mt_rand(0, strlen($chars) - 1)];
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成时间戳
|
||||
*
|
||||
* @return int 时间戳
|
||||
*/
|
||||
public static function generateTimestamp(): int
|
||||
{
|
||||
return time();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化金额(分转元)
|
||||
*
|
||||
* @param int $amount 金额(分)
|
||||
* @return string 格式化后的金额(元)
|
||||
*/
|
||||
public static function formatAmount(int $amount): string
|
||||
{
|
||||
return number_format($amount / 100, 2, '.', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析金额(元转分)
|
||||
*
|
||||
* @param string $amount 金额(元)
|
||||
* @return int 金额(分)
|
||||
*/
|
||||
public static function parseAmount(string $amount): int
|
||||
{
|
||||
return (int) round(floatval($amount) * 100);
|
||||
}
|
||||
}
|
||||
135
application/common/util/Signer.php
Normal file
135
application/common/util/Signer.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\util;
|
||||
|
||||
/**
|
||||
* 第三方支付签名工具(仅内部调用)
|
||||
* 规则:
|
||||
* 1. 除 sign 外的所有非空参数,按字段名 ASCII 升序,使用 QueryString 形式拼接(key1=value1&key2=value2)
|
||||
* 2. 参与签名的字段名与值均为原始值,不做 URL Encode
|
||||
* 3. 支持算法:MD5(默认)/ RSA_1_256 / RSA_1_1
|
||||
*/
|
||||
class Signer
|
||||
{
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param array $params 参与签名的参数(会自动剔除 sign 及空值)
|
||||
* @param string $algorithm 签名算法:md5 | RSA_1_256 | RSA_1_1
|
||||
* @param array $options 额外选项:
|
||||
* - secret: string MD5 签名时可选的密钥,若提供则会在原串末尾以 &key=SECRET 追加
|
||||
* - private_key: string RSA 签名所需私钥(PEM 字符串,支持带头尾)
|
||||
* - passphrase: string 可选,RSA 私钥口令
|
||||
* @return string 返回签名串(MD5 为32位小写;RSA为base64编码)
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function sign(array $params, $algorithm = 'md5', array $options = [])
|
||||
{
|
||||
$signString = self::buildSignString($params);
|
||||
|
||||
$algo = strtolower($algorithm);
|
||||
switch ($algo) {
|
||||
case 'md5':
|
||||
return self::signMd5($signString, isset($options['secret']) ? (string)$options['secret'] : null);
|
||||
case 'rsa_1_256':
|
||||
return self::signRsa($signString, $options, 'sha256');
|
||||
case 'rsa_1_1':
|
||||
return self::signRsa($signString, $options, 'sha1');
|
||||
default:
|
||||
throw new \InvalidArgumentException('Unsupported algorithm: ' . $algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建签名原始串
|
||||
* - 剔除 sign 字段
|
||||
* - 过滤空值(null、'')
|
||||
* - 按键名 ASCII 升序
|
||||
* - 使用原始值拼接为 key1=value1&key2=value2
|
||||
*
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
public static function buildSignString(array $params)
|
||||
{
|
||||
$filtered = [];
|
||||
foreach ($params as $key => $value) {
|
||||
if ($key === 'sign') {
|
||||
continue;
|
||||
}
|
||||
if ($value === '' || $value === null) {
|
||||
continue;
|
||||
}
|
||||
$filtered[$key] = $value;
|
||||
}
|
||||
|
||||
ksort($filtered, SORT_STRING);
|
||||
|
||||
$pairs = [];
|
||||
foreach ($filtered as $key => $value) {
|
||||
// 原始值拼接,不做 urlencode
|
||||
$pairs[] = $key . '=' . (is_bool($value) ? ($value ? '1' : '0') : (string)$value);
|
||||
}
|
||||
|
||||
return implode('&', $pairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* MD5 签名
|
||||
* - 若提供 secret,则原串末尾追加 &key=SECRET
|
||||
* - 返回 32 位小写
|
||||
*
|
||||
* @param string $signString
|
||||
* @param string|null $secret
|
||||
* @return string
|
||||
*/
|
||||
protected static function signMd5($signString, $secret = null)
|
||||
{
|
||||
if ($secret !== null && $secret !== '') {
|
||||
$signString .= '&key=' . $secret;
|
||||
}
|
||||
return strtolower(md5($signString));
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA 签名
|
||||
*
|
||||
* @param string $signString
|
||||
* @param array $options 必填:private_key,可选:passphrase
|
||||
* @param string $hashAlgo sha256|sha1
|
||||
* @return string base64 签名
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected static function signRsa($signString, array $options, $hashAlgo = 'sha256')
|
||||
{
|
||||
if (empty($options['private_key'])) {
|
||||
throw new \InvalidArgumentException('RSA signing requires private_key.');
|
||||
}
|
||||
|
||||
$privateKey = $options['private_key'];
|
||||
$passphrase = isset($options['passphrase']) ? (string)$options['passphrase'] : '';
|
||||
|
||||
// 兼容无头尾私钥,自动包裹为 PEM
|
||||
if (strpos($privateKey, 'BEGIN') === false) {
|
||||
$privateKey = "-----BEGIN PRIVATE KEY-----\n" . trim(chunk_split(str_replace(["\r", "\n"], '', $privateKey), 64, "\n")) . "\n-----END PRIVATE KEY-----";
|
||||
}
|
||||
|
||||
$pkeyId = openssl_pkey_get_private($privateKey, $passphrase);
|
||||
if ($pkeyId === false) {
|
||||
throw new \InvalidArgumentException('Invalid RSA private key or passphrase.');
|
||||
}
|
||||
|
||||
$signature = '';
|
||||
$algoConst = $hashAlgo === 'sha1' ? OPENSSL_ALGO_SHA1 : OPENSSL_ALGO_SHA256;
|
||||
$ok = openssl_sign($signString, $signature, $pkeyId, $algoConst);
|
||||
openssl_free_key($pkeyId);
|
||||
|
||||
if (!$ok) {
|
||||
throw new \InvalidArgumentException('OpenSSL sign failed.');
|
||||
}
|
||||
|
||||
return base64_encode($signature);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user