231 lines
6.6 KiB
PHP
231 lines
6.6 KiB
PHP
|
|
<?php
|
|||
|
|
namespace support;
|
|||
|
|
class Encrypt
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 类似QQ的非连续数字ID加密
|
|||
|
|
* 使用可逆的位运算混淆算法
|
|||
|
|
* 特点:
|
|||
|
|
* 1. 连续输入产生非连续、看起来随机的输出
|
|||
|
|
* 2. 加密解密完全可逆
|
|||
|
|
* 3. 输出为8位纯数字(10000000 - 99999999),类似QQ号
|
|||
|
|
* 4. 支持约9000万个ID (0 - 89999999)
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// 最大ID值(支持9000万个ID)
|
|||
|
|
private static $maxId = 90000000;
|
|||
|
|
// 加密结果基数(确保8位数)
|
|||
|
|
private static $baseValue = 10000000;
|
|||
|
|
/**
|
|||
|
|
* 加密连续数字ID为非连续数字ID
|
|||
|
|
* 使用基于哈希的可逆映射
|
|||
|
|
* @param int $plainId 原始连续ID (0 - 89999999)
|
|||
|
|
* @param int $userKey 可选的额外密钥
|
|||
|
|
* @return int 加密后的非连续ID(8位数)
|
|||
|
|
*/
|
|||
|
|
static function userIDencode($userID,$key=0):string{
|
|||
|
|
if(!is_numeric(($userID))){
|
|||
|
|
return $userID;
|
|||
|
|
}
|
|||
|
|
$userID = intval($userID);
|
|||
|
|
if($userID<=100234){
|
|||
|
|
return $userID.'';
|
|||
|
|
}
|
|||
|
|
if($userID<=102028){
|
|||
|
|
return id_encode($userID);
|
|||
|
|
}
|
|||
|
|
$key = intval($key);
|
|||
|
|
|
|||
|
|
$plainId = abs(intval($userID)) % self::$maxId;
|
|||
|
|
|
|||
|
|
// 使用哈希函数生成伪随机序列,然后建立双射映射
|
|||
|
|
// 方法:使用线性同余生成器 (LCG) 的变体
|
|||
|
|
// 公式:encrypted = (a * plainId + b) mod m
|
|||
|
|
// 其中 a 和 m 互质,保证可逆
|
|||
|
|
|
|||
|
|
// 选择一个大质数作为乘数(与9000万互质)
|
|||
|
|
$a = 15485863; // 大质数,与90000000互质
|
|||
|
|
$b = 2038074743; // 大常数
|
|||
|
|
$m = self::$maxId;
|
|||
|
|
|
|||
|
|
// 加密
|
|||
|
|
$encrypted = ($a * $plainId + $b + $key) % $m;
|
|||
|
|
|
|||
|
|
// 确保结果是8位数(10000000 - 99999999)
|
|||
|
|
return (self::$baseValue + $encrypted).'';
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* 解密非连续数字ID为原始连续ID
|
|||
|
|
* @param int $encryptedId 加密后的非连续ID(8位数)
|
|||
|
|
* @param int $userKey 可选的额外密钥
|
|||
|
|
* @return int 原始连续ID
|
|||
|
|
*/
|
|||
|
|
static function userIDDecode($userID,$key=0):string{
|
|||
|
|
if(!is_numeric(($userID))){
|
|||
|
|
return id_decode($userID.'');
|
|||
|
|
}
|
|||
|
|
if($userID<=100234){
|
|||
|
|
return $userID.'';
|
|||
|
|
}
|
|||
|
|
$key = intval($key);
|
|||
|
|
// 去除基数,得到原始混淆值
|
|||
|
|
$cipherId = ($userID - self::$baseValue + self::$maxId) % self::$maxId;
|
|||
|
|
|
|||
|
|
// LCG解密:plain = (encrypted - b) * a^-1 mod m
|
|||
|
|
$a = 15485863;
|
|||
|
|
$b = 2038074743;
|
|||
|
|
$m = self::$maxId;
|
|||
|
|
|
|||
|
|
// 计算 a 的模逆元 (使用扩展欧几里得算法)
|
|||
|
|
$aInv = self::modInverse($a, $m);
|
|||
|
|
|
|||
|
|
// 解密
|
|||
|
|
$temp = ($cipherId - $b - $key) % $m;
|
|||
|
|
if ($temp < 0) $temp += $m;
|
|||
|
|
|
|||
|
|
$plainId = ($aInv * $temp) % $m;
|
|||
|
|
|
|||
|
|
return $plainId.'';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使用扩展欧几里得算法计算模逆元
|
|||
|
|
* 返回 x 使得 (a * x) % m = 1
|
|||
|
|
*/
|
|||
|
|
private static function modInverse($a, $m) {
|
|||
|
|
$m0 = $m;
|
|||
|
|
$x0 = 0;
|
|||
|
|
$x1 = 1;
|
|||
|
|
|
|||
|
|
if ($m == 1) return 0;
|
|||
|
|
|
|||
|
|
while ($a > 1) {
|
|||
|
|
// q 是商
|
|||
|
|
$q = intval($a / $m);
|
|||
|
|
$t = $m;
|
|||
|
|
|
|||
|
|
// m 是余数,处理完后 a 和 m 互换
|
|||
|
|
$m = $a % $m;
|
|||
|
|
$a = $t;
|
|||
|
|
|
|||
|
|
// 更新 x0 和 x1
|
|||
|
|
$t = $x0;
|
|||
|
|
$x0 = $x1 - $q * $x0;
|
|||
|
|
$x1 = $t;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保 x1 是正数
|
|||
|
|
if ($x1 < 0) $x1 += $m0;
|
|||
|
|
|
|||
|
|
return $x1;
|
|||
|
|
}
|
|||
|
|
static function generateShortUniqueID($length = 8)
|
|||
|
|
{
|
|||
|
|
// 生成指定长度的随机字节,转为 Base64 编码并去除不必要字符
|
|||
|
|
return substr(bin2hex(random_bytes($length / 2)), 0, $length);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static function idEncode($id = '')
|
|||
|
|
{
|
|||
|
|
if ($id <= 100234) {
|
|||
|
|
return $id . '';
|
|||
|
|
}
|
|||
|
|
return id_encode($id);
|
|||
|
|
}
|
|||
|
|
static function idDecode($id = '')
|
|||
|
|
{
|
|||
|
|
$_id = intval($id);
|
|||
|
|
if ($_id == $id) {
|
|||
|
|
return $id;
|
|||
|
|
}
|
|||
|
|
return id_decode($id);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* 生成可逆的邀请码(8位,含校验位)
|
|||
|
|
* @param int $id 用户ID(需≥1000)
|
|||
|
|
* @return string 大写字母+数字组合
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
static function base62Encode(int $id, $secret = 'your_secret_salt'): string
|
|||
|
|
{
|
|||
|
|
// 添加校验位(防止篡改)
|
|||
|
|
$hash = crc32($id . $secret) % 1000;
|
|||
|
|
$code_num = $id * 1000 + $hash;
|
|||
|
|
|
|||
|
|
// Base62 编码(0-9A-Za-z)
|
|||
|
|
$base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|||
|
|
$code = '';
|
|||
|
|
while ($code_num > 0) {
|
|||
|
|
$code = $base62[$code_num % 62] . $code;
|
|||
|
|
$code_num = (int) ($code_num / 62);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 补全到8位
|
|||
|
|
return str_pad($code, 8, '0', STR_PAD_LEFT);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* 从邀请码解析用户ID
|
|||
|
|
* @return int|false 成功返回id,失败返回false
|
|||
|
|
*/
|
|||
|
|
static function base62Decode(string $code, $secret = 'your_secret_salt'): int|false
|
|||
|
|
{
|
|||
|
|
$base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|||
|
|
$code_num = 0;
|
|||
|
|
|
|||
|
|
// Base62 解码
|
|||
|
|
for ($i = 0; $i < strlen($code); $i++) {
|
|||
|
|
$pos = strpos($base62, $code[$i]);
|
|||
|
|
if ($pos === false)
|
|||
|
|
return false;
|
|||
|
|
$code_num = $code_num * 62 + $pos;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 分离校验位
|
|||
|
|
$id = (int) ($code_num / 1000);
|
|||
|
|
$hash = $code_num % 1000;
|
|||
|
|
|
|||
|
|
// 校验
|
|||
|
|
if (crc32($id . $secret) % 1000 != $hash) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $id;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Aes加密
|
|||
|
|
* @param mixed $str
|
|||
|
|
* @param mixed $key
|
|||
|
|
* @return string
|
|||
|
|
*/
|
|||
|
|
static function aesencode($str, $key = '')
|
|||
|
|
{
|
|||
|
|
if (!$key) {
|
|||
|
|
$key = Config('pay.api_token');
|
|||
|
|
}
|
|||
|
|
if (is_array($str) || is_object($str)) {
|
|||
|
|
$str = json_encode($str, JSON_UNESCAPED_UNICODE);
|
|||
|
|
}
|
|||
|
|
$key = hash('sha256', $key, true);
|
|||
|
|
$iv = substr($key, 0, 16);
|
|||
|
|
$encrypted = openssl_encrypt($str, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
|
|||
|
|
return base64_encode($encrypted);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Aes解密
|
|||
|
|
* @param mixed $str
|
|||
|
|
* @param mixed $key
|
|||
|
|
* @return string
|
|||
|
|
*/
|
|||
|
|
static function aesdecode($str, $key = '')
|
|||
|
|
{
|
|||
|
|
if (!$key) {
|
|||
|
|
$key = Config('pay.api_token');
|
|||
|
|
}
|
|||
|
|
$key = hash('sha256', $key, true);
|
|||
|
|
$iv = substr($key, 0, 16);
|
|||
|
|
$encrypted = base64_decode($str);
|
|||
|
|
$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
|
|||
|
|
return $decrypted;
|
|||
|
|
}
|
|||
|
|
}
|