利用外部应用程序进行加密(基础篇)
📘

利用外部应用程序进行加密(基础篇)

第 1/3 篇

◍ 用外部程序给 MT5 信号做加密

MT5 终端本身不提供针对自定义信号流的强加密接口,若想在不暴露策略逻辑的前提下把信号推给外部接收端,常见做法是交给独立外部程序先加密再传输。 实际可行的一条路是用 Windows 的 CryptoAPI 或 OpenSSL 这类现成库,在 EA 通过 WinAPI 调用外部 DLL 完成 AES 或 RSA 运算,而非在 MQL5 里手写密码学。 需要注意,外汇与贵金属杠杆交易本身高风险,任何信号外传方案都应先在小资金账户验证链路稳定性,避免加密握手失败导致漏单。

为什么交易程序里还会碰上加密

很多人写 MQL 程序时觉得加密纯属多余:数据不离开终端,加解密只徒增负载,甚至像是开发者炫技。日常喊单、画线的指标里确实几乎用不到密码学。 但有一类场景绕不开——卖许可。独立开发者或小团队把用户身份、产品可编辑清单写进许可证,EA 启动时先校验有效期,再向服务器请求续期或拉新许可。 这里麻烦的是:终端、远程服务、控制与日志模块常由不同人用不同语言(MQL5 / C# / C++)在不同时段写好。本文要打通的,正是 MetaTrader 终端能解密 C# 或 C++ 侧加密对象、反向亦可的模式。 外汇与贵金属杠杆高、账户风险大,许可校验失败可能直接让 EA 停摆,但这套加解密演示本身不涉及行情收益断言,仅作工程参考。

「先框定要解决的许可证问题」

我们要模拟一个实际场景:为若干指标和智能交易系统(EA)做许可证的创建、加密与解密。具体用哪个程序执行加解密并不关键,典型链路是开发者本机生成许可证,销售端做字段修正,交易者终端再解密还原。 这条链路必须容忍性能很低的算法,也就是在老旧或弱算力设备上也能跑通,不能依赖重运算。原文给出的并非开箱即用的成品许可方案,而是一种可继续改写的变体思路。 外汇与贵金属交易工具涉及高风险,这类许可证逻辑只解决分发与绑定,不保证任何策略收益,实盘前请自行在 MT5 策略测试器与离线环境验证容错。

◍ MT5 里拿得到的两组加解密接口

要在 MT5 里做本地数据混淆或传输前加密,直接调终端内置的两个标准函数就够了:CryptEncode 负责加密,CryptDecode 负责解密。它们靠 method 参数切换算法,可选值里有 CRYPT_AES128、CRYPT_AES256、CRYPT_DES,三者都是不同密钥长度的对称加密,本文只挑了 CRYPT_AES128 来用。 CRYPT_AES128 是 128 位(16 字节)密钥的分组密码,其余两种用法结构一致,换参数即可。要注意的是,终端文档没暴露 AES 的全部细节设置,实际跑的时候固定走电子密码簿(ECB)模式,Padding 填充为零,这点在社区交流里被确认过,省了我们自己配模式的麻烦。 下面把两个函数的签名逐行拆一下,方便直接抄进 EA 或脚本:

MQL5 / C++
class="type">int  CryptEncode(
   ENUM_CRYPT_METHOD   method,         class=class="str">"cmt">// 转换方法:选 CRYPT_AES128 等
   class="kw">const class="type">uchar&        data[],         class=class="str">"cmt">// 源数组:待加密的原始字节
   class="kw">const class="type">uchar&        key[],          class=class="str">"cmt">// 加密密钥:长度需匹配算法
   class="type">uchar&              result[]        class=class="str">"cmt">// 目标数组:输出密文
   );
class="type">int  CryptDecode(
   ENUM_CRYPT_METHOD   method,         class=class="str">"cmt">// 转换方法:需与加密时一致
   class="kw">const class="type">uchar&        data[],         class=class="str">"cmt">// 源数组:待解密的密文字节
   class="kw">const class="type">uchar&        key[],          class=class="str">"cmt">// 加密密钥:必须与加密同值
   class="type">uchar&              result[]        class=class="str">"cmt">// 目标数组:输出明文
   );

把许可证拆成用户与产品两层结构

做许可系统别一上来就把用户和产品揉进同一个结构体。原文思路是先定义产品许可 ea_user:固定 255 字符存产品名(PRODMAXLENGTH 宏),expired 设 -1 表示不限期,IsExpired() 拿本地时间比;再单独定义 user_lic 管用户,uid、服务截止日、最多 5 个账户(COUNTACC=5)、以及 ea_count 产品数。 这样用户只持一份许可证,里面挂多产品权限,比每个产品重复塞用户资料省空间。user_lic 构造函数里 ArrayFill 把 logins 清 0,AddLogin 在未满 5 个时写入并返回 true,超了直接 false——实盘部署前你该在 MT5 里改 COUNTACC 试下边界,看第 6 个账户会不会静默丢弃。 序列化靠 StructToCharArray:先 user_lic 实例转字节数组,再把若干 ea_user 追加进去并自增 ea_count。CLic 类包了加解密和密钥长度检查,例如 CRYPT_AES128 密钥必须正好 16 字节,代码里写死校验不依赖未来平台改版。 生成的字节数组加密后存终端 File 文件夹,读取时反向解密。附带的 CryptoMQL 包里有两个脚本加函数库,能直接跑通 lic.txt 的写读闭环,外汇贵金属 EA 分发前建议先拿模拟账户验一遍这套授权流,注意券商环境对文件目录权限限制可能带来高风险。

MQL5 / C++
class="macro">#define PRODMAXLENGTH class="num">255
class="kw">struct ea_user
{
  ea_user() {expired = -class="num">1;}
  class="type">class="kw">datetime expired;                class=class="str">"cmt">//License expiration(-class="num">1 - unlimited)
  class="type">int      namelength;             class=class="str">"cmt">//Product name length
  class="type">char     uname[PRODMAXLENGTH];    class=class="str">"cmt">//Product name
  class="type">void SetEAname(class="type">class="kw">string name)
  {
    namelength = StringToCharArray(name, uname);
  }
  class="type">class="kw">string GetEAname()
  {
    class="kw">return CharArrayToString(uname, class="num">0, namelength);
  }
  class="type">bool IsExpired()
  {
    if (expired == -class="num">1)
      class="kw">return class="kw">false; class=class="str">"cmt">// NOT expired
    class="kw">return expired <= TimeLocal();
  }
};class=class="str">"cmt">//class="kw">struct ea_user
class="macro">#define COUNTACC class="num">5
class="kw">struct user_lic {
  user_lic() {
    uid      = -class="num">1;
    log_count = class="num">0;
    ea_count  = class="num">0;
    expired   = -class="num">1;
    ArrayFill(logins, class="num">0, COUNTACC, class="num">0);
  }
  class="type">long uid;                       class=class="str">"cmt">//User ID
  class="type">class="kw">datetime expired;               class=class="str">"cmt">//End of user service(-class="num">1 - unlimited)
  class="type">int  log_count;                 class=class="str">"cmt">//The number of the user&class="macro">#x27;s accounts
  class="type">long logins[COUNTACC];          class=class="str">"cmt">//User&class="macro">#x27;s accounts
  class="type">int  ea_count;                  class=class="str">"cmt">//The number of licensed products
  class="type">bool AddLogin(class="type">long lg){
    if (log_count >= COUNTACC) class="kw">return class="kw">false;
    logins[log_count++] = lg;
    class="kw">return true;
  }
  class="type">long GetLogin(class="type">int num) {
    if (num >= log_count) class="kw">return -class="num">1;
    class="kw">return logins[num];
  }
  class="type">bool IsExpired() {
    if (expired == -class="num">1) class="kw">return class="kw">false; class=class="str">"cmt">// NOT expired
    class="kw">return expired <= TimeLocal();
  }  
};class=class="str">"cmt">//class="kw">struct user_lic
class CLic {
class="kw">public:
  class="kw">static class="type">int iSizeEauser;
  class="kw">static class="type">int iSizeUserlic;

  CLic() {}
  ~CLic() {}

  class="type">int SetUser(class="kw">const user_lic& header){
    Reset();
    if (!StructToCharArray(header, dest) ) class="kw">return class="num">0;
    class="kw">return ArraySize(dest);
  }class=class="str">"cmt">//class="type">int SetUser(user_lic& header)

常见问题

先把许可证拆成用户层和产品层,用外部应用生成带到期时间的令牌,指标启动时校验令牌有效性即可限制试用。
说明没有做许可证校验。用外部加密程序绑定用户机器码和产品ID,启动时验证签名可防止随意移植篡改。
可以。把你的许可证结构和校验逻辑描述给小布,它能指出用户层与产品层拆分是否清晰、令牌是否易被绕过。
个人开发者用外部轻量程序生成令牌更灵活,平台接口适合深度集成;先框定要解决的许可证问题再选方案。
将授权与对方机器特征绑定,通过私信道发送,且设置单次激活或短有效期,降低转发复用风险。