预览
ClientArea截图
后台截图
用户购买字样填写
配置
将插件放入WHMCS的Server模块中
whmcs/
modules/
servers/
lhlappGen/
lhlappGen.php
...添加产品后模块设置内选择LHL's App Gen +,然后按照说明配置即可。
方案支持
内置了三种 License 加密方案:
| 方案 | 长度 | 安全性 | 依赖 |
|---|---|---|---|
| Lite | 27 字符 | 🟡 密钥泄露可伪造 | 无 |
| HMAC | 35 字符 | 🟡 盐值泄露可伪造 | 无 |
| ECC | 88 字符 | 🟢 公钥泄露无法伪造 | ext-openssl |
| Custom | 自定义 | 自定义 | 自定义 |
1. Lite 轻量方案(最短 License)
纯 base64 + XOR/加法/位旋转,无 hex 计算,零外部依赖。
# 使用内置密钥
python keygen.py lite MyImageBot
# 使用自定义密钥
python keygen.py lite MyImageBot --secret "my-secret-key"输出:
License Key: n1HH5F-7Cej0r-1Nh0L1-YDDBa9
长度: 27 字符2. HMAC-SHA256 方案
# 使用内置盐值
python keygen.py hmac MyImageBot
# 使用自定义盐值
python keygen.py hmac MyImageBot --salt "my-custom-salt"输出:
License Key: 10B0811B-453B7652-BCB42C11-755D58033. ECC P-256 方案(非对称签名)
# 第一步:生成密钥对(仅需一次)
python keygen.py ecc-gen # 输出到当前目录
python keygen.py ecc-gen -o ./keys # 指定输出目录
# 第二步:用私钥签名生成 License
python keygen.py ecc-sign MyImageBot --key private.pem
# 第三步:用公钥验证 License(可选)
python keygen.py ecc-verify MyImageBot --key public.pem --license "base64..."输出:
License Key: EeAz1+aOE6kzWeTALqx38SWwwO6KU1JUiBGHtiL4xJRRJXs8HbRloXa+NQk/MWlvwHgGa9yUSlxLxT0EvgTP9w==
长度: 88 字符ECC 密钥分发
| 文件 | 放在哪 | 说明 |
|---|---|---|
private.pem | WHMCS 服务器 / 开发者本地 | ⚠️ 绝对保密,用于签发 License |
public.pem | 客户端应用(bot / 程序)内 | 可公开,仅用于验证签名 |
自定义 License 加密算法开发指南
快速修改
步骤 1: 复制示例
cd /path/to/whmcs/modules/servers/lhlappGen/algorithms/
cp example_sha512_base62.php my_algo.php步骤 2: 修改算法
编辑 my_algo.php,把类名改为你自己的,修改 generate() 方法:
<?php
require_once __DIR__ . '/CustomLicenseInterface.php';
class MyAlgorithm implements CustomLicenseInterface
{
public static function generate(string $secretOrKey, string $input): string|false
{
$message = mb_strtolower(trim($input), 'UTF-8');
// 你的算法逻辑
$key = hash_hmac('sha512', $message, $secretOrKey);
$formatted = strtoupper(substr($key, 0, 32));
return implode('-', str_split($formatted, 8));
}
public static function getDisplayConfig(): array
{
return ['color' => '#e67e22', 'maxWidth' => '600px', 'fontSize' => '1.1em'];
}
public static function getAlgorithmName(): string
{
return 'My Custom Algorithm';
}
}步骤 3: 测试
在 WHMCS 客户区查看该产品,确认 License Key 正常生成。
接口规范
CustomLicenseInterface
文件位置: algorithms/CustomLicenseInterface.php
interface CustomLicenseInterface
{
/**
* [必须] 生成 License Key
*
* @param string $secretOrKey WHMCS 配置的密钥 (configoption5)
* @param string $input 用户变量值 (已 trim, 未 lowercase)
* @return string|false License Key 或 false
*/
public static function generate(string $secretOrKey, string $input): string|false;
/**
* [可选] 前端显示配置
*
* @return array{color?: string, maxWidth?: string, fontSize?: string}
*/
public static function getDisplayConfig(): array;
/**
* [可选] 算法名称 (日志/调试用)
*
* @return string
*/
public static function getAlgorithmName(): string;
}generate() 方法要求
| 要求 | 说明 |
|---|---|
| 必须是静态方法 | public static function generate(...) |
| 确定性输出 | 相同输入 + 相同密钥 = 相同输出 (除非使用随机签名方案) |
| 返回字符串 | 任意可显示字符串,建议只含 A-Z a-z 0-9 - + / = |
| 失败返回 false | 不要抛出异常到外层 |
| 自行处理大小写 | 输入未做 lowercase,建议在内部 mb_strtolower() |
getDisplayConfig() 返回值
[
'color' => '#e67e22', // License 文字颜色 (任何合法 CSS 颜色值)
'maxWidth' => '600px', // 输入框最大宽度
'fontSize' => '1.05em', // 字号大小
]缺失的键会使用默认值 (橙色主题)。默认前端不显示,可以
文件放置位置
whmcs/
modules/
servers/
lhlappGen/
lhlappGen.php ← 核心模块 (不要修改)
algorithms/
CustomLicenseInterface.php ← 接口 (不要修改)
example_aes_cbc.php ← 示例 1
example_sha512_base62.php ← 示例 2
my_algo.php ← 你的算法文件 ★⚠️ 算法文件必须放在 algorithms/ 目录下,不支持其他路径。编写你的算法
最小模板
<?php
require_once __DIR__ . '/CustomLicenseInterface.php';
class MyLicenseAlgo implements CustomLicenseInterface
{
public static function generate(string $secretOrKey, string $input): string|false
{
// 1. 预处理输入
$message = mb_strtolower(trim($input), 'UTF-8');
if (empty($message) || empty($secretOrKey)) {
return false;
}
// 2. 你的核心算法
// ...
// 3. 格式化输出
return $licenseKey;
}
public static function getDisplayConfig(): array
{
return []; // 使用默认橙色主题
}
public static function getAlgorithmName(): string
{
return 'My Algorithm';
}
}完整进阶示例: HMAC-SM3 (国密)
<?php
require_once __DIR__ . '/CustomLicenseInterface.php';
class SM3HmacAlgorithm implements CustomLicenseInterface
{
public static function generate(string $secretOrKey, string $input): string|false
{
$message = mb_strtolower(trim($input), 'UTF-8');
if (empty($message) || empty($secretOrKey)) {
return false;
}
// 需要安装 ext-gmssl 或使用纯 PHP SM3 库
if (!function_exists('openssl_digest') ||
!in_array('sm3', openssl_get_md_methods())) {
// 降级为 SHA-256
$hash = hash_hmac('sha256', $message, $secretOrKey);
} else {
$hash = hash_hmac('sm3', $message, $secretOrKey);
}
$formatted = strtoupper(substr($hash, 0, 32));
return implode('-', str_split($formatted, 8));
}
public static function getDisplayConfig(): array
{
return [
'color' => '#dc3545', // 红色 (国密风格)
'maxWidth' => '600px',
'fontSize' => '1.15em',
];
}
public static function getAlgorithmName(): string
{
return 'HMAC-SM3 国密算法';
}
}不使用接口 (简化模式)
如果你不想 implements CustomLicenseInterface,模块也能工作——只要类中有 generate() 静态方法即可:
<?php
class SimpleAlgo
{
public static function generate(string $secretOrKey, string $input): string|false
{
return strtoupper(substr(md5($secretOrKey . strtolower($input)), 0, 16));
}
}不过强烈建议实现接口,这样 IDE 能提供更好的类型提示和错误检查。
内置示例
example_aes_cbc.php
| 项目 | 值 |
|---|---|
| 算法 | AES-128-CBC 加密 → SHA-256 → hex |
| 输出 | XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX (35 字符) |
| 依赖 | ext-openssl |
| 安全性 | 中 (固定 IV,密钥泄露可伪造) |
example_sha512_base62.php
| 项目 | 值 |
|---|---|
| 算法 | SHA-512 → 截取 → Base62 编码 |
| 输出 | XXXXX-XXXXX-XXXXX-XXXXX (23 字符) |
| 依赖 | 无 (纯 PHP) |
| 安全性 | 中 (密钥泄露可伪造) |
客户端验证对接
自定义算法需要你自行编写客户端验证逻辑。参考 verify/ 目录下的内置方案验证模块:
verify/
python/license_verify.py ← Python 验证
js/license_verify.js ← JavaScript 验证
go/license_verify.go ← Go 验证
php/license_verify.php ← PHP 验证对接建议
- 对称方案 (如 HMAC / AES / Hash): 客户端使用相同密钥 + 算法重新计算,然后比较
- 非对称方案 (如 RSA / ECDSA): 客户端用公钥验证签名
- 在线验证: 客户端调用你的 API 验证(正在开发中,您也可以自行二次开发)
安全注意事项
推荐做法
- 使用
mb_strtolower(trim($input), 'UTF-8')预处理输入 - 使用确定性算法 (相同输入 = 相同输出)
- 客户端验证时使用常量时间比较 (防时序攻击)
- 密钥/盐值长度 ≥ 16 字节
- 如果安全性要求高,使用非对称方案 (公钥不怕泄露)
避免做法
- 不要在算法中使用
rand()/mt_rand()(会导致每次生成不同) - 不要在算法文件中执行副作用 (数据库写入、网络请求等)
- 不要在算法中硬编码密钥 (应通过 configoption5 传入)
- 不要使用 MD5 作为唯一安全层 (碰撞攻击风险)
- 不要在错误时抛出异常 (应返回
false)
文件安全
模块对自定义算法文件做了以下安全限制:
- 路径限制: 只能从
algorithms/目录加载 - 防穿越: 使用
basename()+ 正则过滤,禁止../等 - 字符白名单: 文件名仅允许
a-z A-Z 0-9 _ - . - 异常捕获:
generate()抛出的任何异常都会被捕获并静默处理
常见问题
Q: License Key 没有生成,显示空白?
检查清单:
configoption4是否选择了custom?configoption7文件名是否正确?- 文件是否位于
algorithms/目录下? - 类是否有
generate()静态方法? generate()是否返回了false? (检查密钥和输入是否为空)
Q: 如何调试?
在算法的 generate() 方法开头加日志:
if (function_exists('logActivity')) {
logActivity("Custom Algo Debug: secret=" . substr($secretOrKey, 0, 5) . "..., input={$input}");
}Q: 可以一个文件里放多个类吗?
可以,但模块只会使用第一个实现了 CustomLicenseInterface 的类。建议一个文件一个类。
Q: 支持 namespace 吗?
当前版本不支持带 namespace 的类。类名应在全局命名空间中。
Q: 如何确保跨语言一致性?
如果你的客户端有多种语言 (Python / JS / Go / PHP),建议:
- 先用 PHP 实现
generate() - 写几个固定测试用例:
generate("test-secret", "MyBot")→XXXX-XXXX-... - 在其他语言实现同样的算法,用相同测试用例验证输出一致
- 参考
verify/目录中内置方案的跨语言实现
Q: 能否用 Composer 包?
可以,只要在算法文件中正确 require Composer 的 autoloader:
require_once __DIR__ . '/../../vendor/autoload.php'; // 根据 WHMCS 目录结构调整路径 采用 CC BY-NC-SA 4.0 协议授权,转载请注明来源。