探索 MQL5 中的密码学:深入浅出的方法阐述·综合运用
📘

探索 MQL5 中的密码学:深入浅出的方法阐述·综合运用

第 3/3 篇

◍ 把信号加密后塞进邮件发出去

这段逻辑承接密钥加载失败的处理:若前面没拿到有效 AES256 密钥,直接 Print 报错并 return,后续加密全不做。 拿到密钥后,先用 StringToCharArray 把 signal 字符串按 CP_UTF8 转成字节数组 data,注意长度用 StringLen(signal) 显式指定,避免尾部补零污染明文。 接着 CryptEncode(CRYPT_AES256, data, key, encrypted) 做 256 位 AES 加密,返回值 result<=0 代表失败,此时用 GetLastError 打印具体错误码再 return。加密成功后再对 encrypted 跑一次 CRYPT_HASH_SHA256 得到 hash,用于接收端校验完整性。 为了走邮件传输,encrypted 和 hash 都要过 CRYPT_BASE64 编码,再用 CharArrayToString(..., WHOLE_ARRAY, CP_UTF8) 转回字符串。邮件正文把两段 Base64 拼起来,主题写 "Encrypted Trading Signal",最后 SendMail 发出;返回 false 时同样打印错误码。 在 MT5 里跑这段前,必须先在 工具→选项→邮件 里配好 SMTP 账号,否则 SendMail 永远返回 false,错误码通常是 4014 或 4015。外汇与贵金属信号传输涉及账户隐私,走加密邮件可降低泄露概率,但邮件通道本身不保证时效,高风险品种信号延迟可能直接影响成交。

MQL5 / C++
  else
  {
      Print("Failed to load the encryption key. Error: ", GetLastError());
      class="kw">return;
  }
  class=class="str">"cmt">// Convert the signal to a byte array
  StringToCharArray(signal, data, class="num">0, StringLen(signal), CP_UTF8);
  class=class="str">"cmt">// Encrypt the signal
  class="type">int result = CryptEncode(CRYPT_AES256, data, key, encrypted);
  if(result <= class="num">0)
  {
      Print("Encryption failed. Error code: ", GetLastError());
      class="kw">return;
  }
  class=class="str">"cmt">// Compute the hash of the encrypted signal
  result = CryptEncode(CRYPT_HASH_SHA256, encrypted, nullKey, hash);
  if(result <= class="num">0)
  {
      Print("Hashing failed. Error code: ", GetLastError());
      class="kw">return;
  }
  class=class="str">"cmt">// Convert encrypted data and hash to Base64 for email transmission
  class="type">uchar base64Encrypted[], base64Hash[];
  CryptEncode(CRYPT_BASE64, encrypted, nullKey, base64Encrypted);
  CryptEncode(CRYPT_BASE64, hash, nullKey, base64Hash);
  class="type">class="kw">string base64EncryptedStr = CharArrayToString(base64Encrypted, class="num">0, WHOLE_ARRAY, CP_UTF8);
  class="type">class="kw">string base64HashStr = CharArrayToString(base64Hash, class="num">0, WHOLE_ARRAY, CP_UTF8);
  class=class="str">"cmt">// Prepare the email content
  class="type">class="kw">string emailSubject = "Encrypted Trading Signal";
  class="type">class="kw">string emailBody = "Encrypted Signal(Base64):\n" + base64EncryptedStr + "\n\nHash(SHA256, Base64):\n" + base64HashStr;
  class=class="str">"cmt">// Send the email(Assuming email settings are configured in MetaTrader)
  class="type">bool emailSent = SendMail(emailSubject, emailBody);
  if(emailSent)
  {
      Print("Email sent successfully.");
  }
  else
  {
      Print("Failed to send email. Error code: ", GetLastError());
  }
}

邮件信号的完整性校验与解密链路

从邮件拿到 Base64 编码的加密信号和哈希后,第一步不是急着解密,而是先验证数据没被篡改。代码里用 32 字节的本地密钥文件 encryption_key.bin 做 AES-256 解密准备,但在这之前必须先对加密字节流算一次 SHA-256,和邮件附带的哈希比对。 CryptEncode(CRYPT_HASH_SHA256, ...) 返回的字节数若 ≤0 说明哈希失败,此时直接退出;若 ArrayCompare 不相等则打印「Signal integrity compromised!」,这步能挡掉大部分传输中被中间人动过手脚的包。外汇与贵金属信号经公网邮件流转,这类校验不是可选项,而是降低误单概率的硬前置。 校验通过后才有资格 CryptDecode(CRYPT_AES256, encrypted, key, decrypted),解密结果用 CharArrayToString 转回 UTF-8 字符串打印。整段逻辑跑通后,你能在 MT5 专家日志里直接看到明文交易信号,后续接下单逻辑即可。建议把密钥文件权限设为仅 MT5 终端可读,避免明文落盘风险。

MQL5 / C++
  class=class="str">"cmt">// Received Base64-encoded encrypted signal and hash from email
  class="type">class="kw">string base64EncryptedStr = "Received encrypted signal in Base64";
  class="type">class="kw">string base64HashStr = "Received hash in Base64";
  class="type">uchar key[class="num">32];
  class="type">uchar encrypted[];
  class="type">uchar hash[];
  class="type">uchar computedHash[];
  class="type">uchar decrypted[];
  class="type">uchar nullKey[];
  class=class="str">"cmt">// Load the encryption key
  class="type">int fileHandle = FileOpen("encryption_key.bin", FILE_BIN|FILE_READ);
  if(fileHandle != INVALID_HANDLE)
  {
      FileReadArray(fileHandle, key, class="num">0, class="num">32);
      FileClose(fileHandle);
  }
  else
  {
      Print("Failed to load the encryption key. Error: ", GetLastError());
      class="kw">return;
  }
  class=class="str">"cmt">// Convert Base64 strings back to byte arrays
  class="type">uchar base64Encrypted[], base64Hash[];
  StringToCharArray(base64EncryptedStr, base64Encrypted, class="num">0, WHOLE_ARRAY, CP_UTF8);
  StringToCharArray(base64HashStr, base64Hash, class="num">0, WHOLE_ARRAY, CP_UTF8);
  class=class="str">"cmt">// Decode Base64 to get encrypted data and hash
  CryptDecode(CRYPT_BASE64, base64Encrypted, nullKey, encrypted);
  CryptDecode(CRYPT_BASE64, base64Hash, nullKey, hash);
  class=class="str">"cmt">// Compute hash of the encrypted data
  class="type">int result = CryptEncode(CRYPT_HASH_SHA256, encrypted, nullKey, computedHash);
  if(result <= class="num">0)
  {
      Print("Hashing failed. Error code: ", GetLastError());
      class="kw">return;
  }
  class=class="str">"cmt">// Compare the computed hash with the received hash
  if(ArrayCompare(hash, computedHash) != class="num">0)
  {
      Print("Signal integrity compromised!");
      class="kw">return;
  }
  else
  {
      Print("Signal integrity verified.");
  }
  class=class="str">"cmt">// Decrypt the signal
  result = CryptDecode(CRYPT_AES256, encrypted, key, decrypted);
  if(result <= class="num">0)
  {
      Print("Decryption failed. Error code: ", GetLastError());
      class="kw">return;
  }
  class=class="str">"cmt">// Convert decrypted data back to class="type">class="kw">string
  class="type">class="kw">string signal = CharArrayToString(decrypted, class="num">0, result, CP_UTF8);
  Print("Decrypted Trading Signal:\n", signal);
  class=class="str">"cmt">// Now you can act upon the trading signal
}

「MQL5 密码学落地的几条硬规矩」

密钥别写死在代码里。硬编码字符串一进 exe 就能被逆向提取,正确做法是放加密文件或运行时从安全源拉取;用 CSecureZeroMemory 清残留也只是辅助,核心还是减少明文驻留时间。 动态密钥要在运行时由 CryptGenRandom 一类安全随机源生成,并设定更换周期。实测长期不换密钥的 EA,一旦分发版本泄露,历史通信全部可解密,更换频率建议按会话或按天。 组合拳比单加密更稳:先压缩再加密能缩小体积并增加混淆层;加密完再对密文算一次哈希,解密侧比对哈希可发现传输中被篡改。下面这段演示了密文哈希校验的基本顺序。

MQL5 / C++
  class="type">int enc_len = EncryptData(plain, key, cipher);
  class="type">uchar hash[class="num">32];
  CryptSHA256(cipher, enc_len, hash);  class=class="str">"cmt">// 对密文做摘要
  class=class="str">"cmt">// 传输 cipher + hash
  class=class="str">"cmt">// 接收端重新 CryptSHA256 比对
错误处理别靠猜。密钥长度错、数组未初始化会直接返回 -1,大数组还可能内存不足;每次调用后接 GetLastError() 拿具体码,比打印『失败』有用得多。 性能上 AES128 比 AES256 少约 40% 轮运算,贵金属和外汇 EA 跑在 VPS 上也要算账——只加密账户敏感字段,别把整根 K 线缓冲全加密,否则 tick 处理延迟会肉眼可见地掉速。外汇和贵金属杠杆高,这类保护只防数据泄露,不降低交易本身风险。

MQL5 / C++
  class="type">int enc_len = EncryptData(plain, key, cipher);
  class="type">uchar hash[class="num">32];
  CryptSHA256(cipher, enc_len, hash);  class=class="str">"cmt">// 对密文做摘要
  class=class="str">"cmt">// 传输 cipher + hash
  class=class="str">"cmt">// 接收端重新 CryptSHA256 比对

◍ 把这条线请下神坛

把密码学接进 MQL5 程序,本质只是给交易算法加了一道数据转换与访问控制的工序,不是点金术。用 CryptEncode/CryptDecode 做敏感参数加密、用独立 KeyGenerator 管密钥,能降低策略被扒和被篡改的概率,但外汇与贵金属本身高杠杆、高波动,加密层挡不住行情风险。 文中给的 Example1-Encrypt.mq5 到 SignalSender.mq5 共 8 个文件,体积都在 1.2–2.66 KB,说明这套实现很轻量,开 MT5 直接挂上就能跑通收发与解密链路。 真要把它用起来,别神话「安全即优势」——密钥泄露或编码误用,照样让保护形同虚设。开发者更该盯的是每次部署后密钥轮换和本地存储边界。

常见问题

发送前用哈希函数对信号内容算一个摘要随邮件一起发,接收方收到后重算摘要比对,不一致就说明被篡改过。
不能照搬。两者函数库和字符串处理差异较大,需按 MT5 的 CryptEncode/CryptDecode 重写并重新测试加解密链路。
可以。小布盯盘的 AIGC 能按你设定的密钥对信号做哈希与加密,并调用邮件接口定时发出,你只需配置一次接收地址。
多半是编码不一致或密钥填错。确认发送端和接收端都用同款字符集,且 Base64 传输时没被邮件客户端换行截断。
把密钥硬编码进公开脚本、忽略邮件长度限制导致密文被截,以及用弱哈希当完整性校验,这三条最致命。