MENU

WHMCS 产品密钥分发插件 (3种方案+支持自定义方案)

2026 年 03 月 05 日 • 阅读: 5 • 技术,开发,文档

预览

ClientArea截图
ClientArea截图
后台截图
后台截图
用户购买字样填写
用户购买字样填写

配置

将插件放入WHMCS的Server模块中

whmcs/
  modules/
    servers/
      lhlappGen/
        lhlappGen.php
        ...

添加产品后模块设置内选择LHL's App Gen +,然后按照说明配置即可。

方案支持

内置了三种 License 加密方案:

方案长度安全性依赖
Lite27 字符🟡 密钥泄露可伪造
HMAC35 字符🟡 盐值泄露可伪造
ECC88 字符🟢 公钥泄露无法伪造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-755D5803

3. 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.pemWHMCS 服务器 / 开发者本地⚠️ 绝对保密,用于签发 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 验证

对接建议

  1. 对称方案 (如 HMAC / AES / Hash): 客户端使用相同密钥 + 算法重新计算,然后比较
  2. 非对称方案 (如 RSA / ECDSA): 客户端用公钥验证签名
  3. 在线验证: 客户端调用你的 API 验证(正在开发中,您也可以自行二次开发)

安全注意事项

推荐做法
  • 使用 mb_strtolower(trim($input), 'UTF-8') 预处理输入
  • 使用确定性算法 (相同输入 = 相同输出)
  • 客户端验证时使用常量时间比较 (防时序攻击)
  • 密钥/盐值长度 ≥ 16 字节
  • 如果安全性要求高,使用非对称方案 (公钥不怕泄露)
避免做法
  • 不要在算法中使用 rand() / mt_rand() (会导致每次生成不同)
  • 不要在算法文件中执行副作用 (数据库写入、网络请求等)
  • 不要在算法中硬编码密钥 (应通过 configoption5 传入)
  • 不要使用 MD5 作为唯一安全层 (碰撞攻击风险)
  • 不要在错误时抛出异常 (应返回 false)

文件安全

模块对自定义算法文件做了以下安全限制:

  1. 路径限制: 只能从 algorithms/ 目录加载
  2. 防穿越: 使用 basename() + 正则过滤,禁止 ../
  3. 字符白名单: 文件名仅允许 a-z A-Z 0-9 _ - .
  4. 异常捕获: generate() 抛出的任何异常都会被捕获并静默处理

常见问题

Q: License Key 没有生成,显示空白?

检查清单:

  1. configoption4 是否选择了 custom?
  2. configoption7 文件名是否正确?
  3. 文件是否位于 algorithms/ 目录下?
  4. 类是否有 generate() 静态方法?
  5. 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),建议:

  1. 先用 PHP 实现 generate()
  2. 写几个固定测试用例: generate("test-secret", "MyBot")XXXX-XXXX-...
  3. 在其他语言实现同样的算法,用相同测试用例验证输出一致
  4. 参考 verify/ 目录中内置方案的跨语言实现

Q: 能否用 Composer 包?

可以,只要在算法文件中正确 require Composer 的 autoloader:

require_once __DIR__ . '/../../vendor/autoload.php'; // 根据 WHMCS 目录结构调整路径
返回文章列表 打赏
本页链接的二维码
打赏二维码