300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 股票量化交易软件:利用外部应用程序进行加密

股票量化交易软件:利用外部应用程序进行加密

时间:2019-04-04 12:22:24

相关推荐

股票量化交易软件:利用外部应用程序进行加密

加密在 赫兹量化程序中很少使用。 在日常交易中,使用密码术的机会并不多。 一个例外就是偏执的信号跟单机希望保护发送的数据免于监听,仅此而已。 若数据不会离开终端,很难想象为什么需要加密/解密数据。 甚至,这可能代表开发人员能力低下,因其造成了终端的额外负载。

也许无需在交易中使用加密? 实际上,其实有。 例如,考虑许可。 可能会有一家小型公司,甚或一位广受欢迎产品的独立开发者。 这种情况与许可问题相关,因此需要许可证加密/解密。

可在许可证中指定用户数据,和产品的可编辑列表。 指标或智能交易系统开始操作,检查许可证的可用性,及给定产品的期限。 程序向服务器发送请求,更新许可证,如有必要,或可接收新的许可证。 这可能不是最有效和最安全的方式,但出于演示目的,我们将在本文中运用它。 显然,在这种情况下,许可证将通过不同的软件工具进行读取/写入 — 终端,远程服务器,控制模块和日志记录模块。 它们可以由不同的人,在不同的时间,以不同的语言编写。

本文的目的是研究加密/解密模式,在这种模式下,赫兹量化 终端可以解密由 C# 或 C++ 程序加密的对象,反之亦然。

本文适用于中等技能的程序员和初学者。

设定任务

概述中已经提到了这一点。 我们将尝试模拟一个实际问题的解决方案,要求为若干种产品(指标和智能交易系统)的许可证创建、加密和解密。 对于我们而言,用哪个程序来加密和解密许可证并不重要。 例如,可以先在开发者的计算机上创建许可证,然后在销售部门对其进行纠正,然后在交易者的计算机上解密。 该过程必须针对性能低廉的算法有很强的容错性。赫兹股票量化软件

除了解决主要任务外,我们还将研究许可的复杂问题。 这不是一切就绪立即可用的许可证,而只是可能的变体之一,应对其进一步进行编辑和开发。

源数据

我们参考终端文档来获取操作的源数据。 有两个标准函数负责加密/解密过程:

intCryptEncode(ENUM_CRYPT_METHOD method,// conversion methodconst uchar&data[],// source arrayconst uchar&key[], // encryption keyuchar&result[] // destination array);intCryptDecode(ENUM_CRYPT_METHOD method,// conversion methodconst uchar&data[],// source arrayconst uchar&key[], // encryption keyuchar&result[] // destination array);

加密/解密的具体方法由赫兹股票量化软件参数确定。 在这种情况下,我们对method参数可以具有的三个值很感兴趣:CRYPT_AES128,CRYPT_AES256,CRYPT_DES。 这三个值代表具有不同密钥长度的对称加密算法。

在本文中,我们仅用其中之一CRYPT_AES128。 这是一种具有 128 位(16 字节)密钥的对称分组密码算法。 其他算法的用法类似。赫兹股票量化软件

AES 算法(不仅是选定的算法,还有其他具有密钥长度的算法)拥有一些上述函数中没有提供的一些重要设置。 其中包括加密模式和填充。 我们不会深入介绍这些术语。 因此,终端使用电子密码簿(ECB)加密模式,且Padding (填充模板)等于零。 我要感谢交易伙伴,因为他们在赫兹股票量化软件上给与了解释。 现在,我们的任务将更容易解决。

开发操作对象

鉴于我们研究如何将加密/解密应用于许可,故我们的操作对象是许可。 这应该是一些包含可适用在许可证的各种产品信息的结构。 这里需要以下数据:

该产品的许可期限。产品名称。

我们用最简单的方法创建相应的结构:

#define PRODMAXLENGTH 255struct ea_user {ea_user() {expired = -1;}datetime expired;//License expiration (-1 - unlimited)intnamelength; //Product name lengthcharuname[PRODMAXLENGTH]; //Product namevoid SetEAname(string name) {namelength = StringToCharArray(name, uname);}string GetEAname() {return CharArrayToString(uname, 0, namelength);}bool IsExpired() {if (expired == -1) return false; // NOT expiredreturn expired <= TimeLocal();}};//struct ea_user

此处是一些解释:

产品名称存储在固定长度的数组。产品名称最大长度限制为PRODMAXLENGTH。可以很轻易地将此策略封包到字节数组中 — 这是我们在加密整个对象之前要做的。

然而,仅有此结构是不够的。 显然,许可证必须包含用户详细信息。 该信息能被包括在已讲述过的结构中,但用户可能拥有多个产品许可证,因此效率低下。 一个更合理的解决方案是为用户创建一个单独的结构,并往其中添加所需数量的产品许可证。 因此,用户将只拥有一个许可证,但其中包含所有许可产品的权限。赫兹股票量化软件

可包含在用户描述结构中的信息:

独有的用户 ID。 还可以保存名称,但每次都要发送个人数据(即使是加密形式)似乎并不可取。用户帐户上可用产品的有关信息。用户的许可证到期日期。 该字段可限定所有现有产品的用法,甚至是无限制产品,作为用户服务时间。赫兹股票量化软件用户终端中许可产品的数量:

#define COUNTACC 5struct user_lic {user_lic() {uid = -1;log_count =0;ea_count=0;expired = -1;ArrayFill(logins, 0, COUNTACC, 0);}long uid; //User IDdatetime expired;//End of user service (-1 - unlimited)intlog_count; //The number of the user's accountslong logins[COUNTACC]; //User's accountsintea_count; //The number of licensed productsbool AddLogin(long lg){if (log_count >= COUNTACC) return false;logins[log_count++] = lg;return true;}long GetLogin(int num) {if (num >= log_count) return -1;return logins[num];}bool IsExpired() {if (expired == -1) return false; // NOT expiredreturn expired <= TimeLocal();} };//struct user_lic

以下是一些澄清:

用户帐户存储在固定长度的数组中。 它们由帐号表示。 尽管,若有必要,可按服务器或经纪商名称,以及特定帐户的激活次数轻松补充信息。赫兹股票量化软件

到目前为止,此结构包含有关用户和相关产品的足够信息。 它们每个都是一种类型,可以通过StructToCharArray函数对其实例进行处理。赫兹股票量化软件

现在,我们需要将结构数据序列化为字节数组,以便进一步进行加密。 实现如下:

创建并初始化 user_lic 结构的实例。赫兹股票量化软件将其序列化为字节数组。创建并初始化 ea_user 结构的一个或多个实例。将它们序列化为相同的字节数组,增加其大小,并调整 ea_count 字段。

创建一个类来执行以下操作:

class CLic {public:static int iSizeEauser;static int iSizeUserlic;CLic() {}~CLic() {}int SetUser(const user_lic& header){Reset();if (!StructToCharArray(header, dest) ) return 0;return ArraySize(dest);}//int SetUser(user_lic& header)int AddEA(const ea_user& ea) {int c = ArraySize(dest);if (c == 0) return 0;uchar tmp[];if (!StructToCharArray(ea, tmp) ) return 0;ArrayInsert(dest, tmp, c);return ArraySize(dest);}//int AddEA(ea_user& ea)bool GetUser(user_lic& header) const {if (ArraySize(dest) < iSizeUserlic) return false;return CharArrayToStruct(header, dest);}//bool GetUser(user_lic& header)//num - 0 basedbool GetEA(int num, ea_user& ea) const {int index = iSizeUserlic + num * iSizeEauser;if (ArraySize(dest) < index + iSizeEauser) return false;return CharArrayToStruct(ea, dest, index);}//bool GetEA(int num, ea_user& ea)int Encode(ENUM_CRYPT_METHOD method, string key, uchar&buffer[]) const {if (ArraySize(dest) < iSizeUserlic) return 0;if(!IsKeyCorrect(method, key) ) return 0;uchar k[];StringToCharArray(key, k);return CryptEncode(method, dest, k, buffer); }int Decode(ENUM_CRYPT_METHOD method, string key, uchar&buffer[]) {Reset();if(!IsKeyCorrect(method, key) ) return 0;uchar k[];StringToCharArray(key, k);return CryptDecode(method, buffer, k, dest); } protected:void Reset() {ArrayResize(dest, 0);}bool IsKeyCorrect(ENUM_CRYPT_METHOD method, string key) const {int len = StringLen(key);switch (method) {case CRYPT_AES128:if (len == 16) return true;break;case CRYPT_AES256:if (len == 32) return true;break;case CRYPT_DES:if (len == 7) return true;break;}#ifdef __DEBUG_USERMQH__Print("Key length is incorrect: ",len);#endif return false;}//bool IsKeyCorrect(ENUM_CRYPT_METHOD method, string key)private:uchar dest[];};//class CLicstatic int CLic::iSizeEauser= sizeof(ea_user);static int CLic::iSizeUserlic = sizeof(user_lic);

该类中添加了两个函数,可以进行加密和解密,还有一个受保护函数用于检查密钥长度。 从其代码中可以看到,例如,CRYPT_AES128方法的密钥长度必须等于 16 个字节。 实际上,它不能少于 16 个字节。 大概,以后会将其常规化,令其对开发人员隐藏。 我们不会依赖于此,而是会严格设置所需的密钥长度。赫兹股票量化软件

最后,已生成的字节数组能够进行加密,并将其保存到二进制文件当中。 根据一般规则,此文件应存储在终端的File文件夹中。 若有必要,可对其进行读取和解密:

bool CreateLic(ENUM_CRYPT_METHOD method, string key, CLic& li, string licname) {uchar cd[];if (li.Encode(method, key, cd) == 0) return false;int h = FileOpen(licname, FILE_WRITE | FILE_BIN);if (h == INVALID_HANDLE) {#ifdef __DEBUG_USERMQH__Print("File create failed: ",licname);#endifreturn false;}FileWriteArray(h, cd);FileClose(h);#ifdef __DEBUG_USERMQH__li.SaveArray();#endifreturn true;}// bool CreateLic(ENUM_CRYPT_METHOD method, string key, const CLic& li, string licname)bool ReadLic(ENUM_CRYPT_METHOD method, string key, CLic& li, string licname) {int h = FileOpen(licname, FILE_READ | FILE_BIN);if (h == INVALID_HANDLE) {#ifdef __DEBUG_USERMQH__Print("File open failed: ",licname);#endifreturn false;}uchar cd[];FileReadArray(h,cd);if (ArraySize(cd) < CLic::iSizeUserlic) {#ifdef __DEBUG_USERMQH__Print("File too small: ",licname);#endifreturn false;}li.Decode(method, key, cd);FileClose(h);return true;}// bool ReadLic(ENUM_CRYPT_METHOD method, string key, CLic& li, string licname)

两个函数都很清楚,不需要其他说明。 随附的 CryptoMQL.zip 文件包含两个脚本和一个函数库文件,它们实现了加密/解密,还有加密的许可证文件 lic.txt。赫兹股票量化软件

C# 项目

我们创建一个简单的 C# 项目,从而模拟另一个程序的解密和编辑过程。 使用 Visual Studio 并为 .NET Framework 平台创建一个控制台应用程序。 检查 System.Security 和 System.Security.Cryptography 的空间连接。

代码中出现以下问题:MQL 和 C# 具有不同的时间格式。已经解决了该问题。 作者已完成了一个壮举,我们可以在项目中借用他的MtConverter类。

创建两个类,分别为EaUser 和 UserLic,其字段类似于 ea_user 和 user_lic 结构。 目的是解密由终端创建的许可证(lic.txt 文件),解析接收的数据,修改对象,并重新加密,以便创建新文件。 如果您仔细设置加密/解密模式,此任务必将易于实现。 此处是相应代码的样子:

using (Aes aesAlg = Aes.Create()){aesAlg.Key = Key;aesAlg.IV = IV;aesAlg.Mode = CipherMode.ECB;aesAlg.Padding = PaddingMode.Zeros;..................................

请注意最后两行,其中设置了加密模式(ECB)和填充。 我们为这些模式采用有关的设置信息。 模块中与密钥安装有关的第一行应该予以澄清。 它采用与终端加密时相同的密钥,但这次将其转换为字节数组:

string skey = "qwertyuiopasdfgh";byte[] Key= Encoding.ASCII.GetBytes(s: skey);

注意设置“ IV”参数的那一行。 这就是所谓的“初始化向量”,即,除 ECB 模式外参与所有加密模式的随机数字。 因此,我们此时简单地创建所需长度的数组:

byte[] iv = new byte[16];

另外,请注意,C# 中的密钥情况与 MQL 中的情况不同。 如果密钥长度(在本例中为 “qwertyuiopasdfgh” 行)大于 16,则将引发异常。 这就是为什么严格控制 MQL 代码中的密钥长度是一个不错决定的原因。

其余的非常简单。 读取二进制文件 -> 解密流 -> 利用BinaryReader填充创建的 UserLic 类实例。 也许,若序列化相应的类,可能会获得相似的结果。 您可自行测试这种可能性。

我们来修改某些字段,在这种情况下,我们将更改用户 ID。 然后,以相同的方式加密数据,并创建一个新文件 “lic_C#.txt”。 以上操作由项目中的两个静态函数EncryptToFile_AesDecryptFromFile_Aes执行。 为了进行调试,我添加了两个类似的函数,它们不操纵文件,而是针对字节数组起作用:EncryptToArray_AesDecryptFromArray_Aes

以下附件,含有 CryptoC#.zip 项目的所有必需文件。

任何人都可能注意到以下项目缺陷:

它没有必要检查所调用函数的参数。没有异常处理。单流操作模式。

我没有实现上述功能,因为本文目标并非创建功能齐全的应用程序。 如果我们实现了所有必需的部分,那么多余的代码部分会很庞大,并且会分散基本问题的注意力。赫兹股票量化软件

C++ 项目

下一个项目是以 C++ 创建的。 我们在 Visual Studio 环境中创建控制台应用程序。 在箱体之外我们不支持任何加密/解密。 因此,我们必须通过下载并安装 OpenSSL 安装包来连接著名的 OpenSSL 函数库。 结果就是,我们可以使用所有 Open SSL 函数库和包含项,它们应该连接到所创建项目。 有关如何将函数库连接到项目的详细信息,请阅读赫兹股票量化软件。 不幸的是,OpenSSL 文档远未完善,然而没有更好的使用方法。

连接函数库后,继续编写代码。 首先要做的事情是再次讲述已知的两个结构:

constexpr size_t PRODMAXLENGTH = 255;#pragma pack(1)typedef struct EA_USER {EA_USER();EA_USER(std::string name);EA_USER(EA_USER& eauser);std::time_texpired;long namelength;char eaname[PRODMAXLENGTH];std::string GetName();void SetName(std::string newName);std::string GetTimeExpired();std::string ToString();size_t ToArray(byte* pbyte);constexpr size_t GetSize() noexcept;friend std::ostream& operator<< (std::ostream& out, EA_USER& eauser);friend std::istream& operator>> (std::istream& in, EA_USER& eauser);}EAUSER, *PEAUSER;#pragma pack()constexpr size_t COUNTACC = 5;#pragma pack(1)typedef struct USER_LIC{using PEAUNIC = std::unique_ptr<EAUSER>;USER_LIC();USER_LIC(USER_LIC&& ul);USER_LIC(const byte* pbyte);int64_t uid;std::time_t expired; long log_count; int64_t logins[COUNTACC]; long ea_count; std::vector<PEAUNIC> pEa;std::string GetTimeExpired();std::string ToString();size_t ToArray(byte* pbyte);void AddEA(EA_USER eau);bool AddAcc(long newAcc);size_t GetSize();friend std::ostream& operator<< (std::ostream& out, USER_LIC& ul);friend std::istream& operator>> (std::istream& in, USER_LIC& ul);USER_LIC& operator = (const USER_LIC&) = delete;USER_LIC(const USER_LIC&) = delete;} USERLIC, *PUSERLIC;#pragma pack()

该代码比 C# 的要复杂一些。 某些字段类型有所不同。 例如,在此项目中,携带帐户数组的字段具有int64_t数组类型,而 MQL 包含文件具有long类型。 这与相应类型的大小有关。 如果您不控制此类功能,则可能导致难以捕获的错误。 有些部分则比较容易:此处我们不需要转换时间。

另外,在这个项目中,我们可能会遇到密钥长度不正确的问题。 若要解决此问题,请在项目中包括以下函数:赫兹股票量化软件

std::string AES_NormalizeKey(const void *const apBuffer, size_t aSize)

此函数会将appBuffer数组“修剪”为所需的aSize长度。 另外,我们编写以下辅助函数:

void handleErrors(void){ERR_print_errors_fp(stderr);}

此函数将提供 OpenSSL 函数库中错误代码的说明。 以下两个函数实现了主要操作:

int aes_decrypt(const byte* key, const byte* iv, const byte* pCtext, byte* pRtext, int iTextsz)int aes_encrypt(const byte* key, const byte* iv, const byte* pPtext, byte* pCtext, int iTextsz)

附件中提供了方法的实现。 我只会提及一些基本要点:

这里不使用初始化向量。 我们创建所需大小的数组,并在调用点将其传递。该函数库不提供任何有关填充的好处。 通过调用设置此模式:

EVP_CIPHER_CTX_set_padding(ctx.get(), 0);

确保传递“零”,而不是这样:

EVP_CIPHER_CTX_set_padding(ctx.get(), EVP_PADDING_ZERO);

也许看似与此相应。 更多问题是与添加的连接有关。 事实上,如果填充值为零(如同我们的项目中一样),则开发者必须注意确保加密对象的长度是BLOCK_SIZE = AES_BLOCK_SIZE的倍数,即 16 个字节。 这就是为什么在调用 aes_encrypt(......) 之前,必须要为加密数组提供相应的对齐方式。赫兹股票量化软件

按顺序执行与上一个项目相同的操作:

解密生成的文件,对其进行编辑,然后再次对其进行加密。 在这种情况下,将另一个用户帐户的有关信息添加到许可证之中。现在,我们得到一个加密文件 lic_С++.txt 。 这次的文件大小不同。 这是时钟大小(16 字节),它是在对齐过程中加进来的。赫兹股票量化软件

该项目的所有源文件都位于下面随附的 CryptoС++.zip 存档中。

最终检查和结果

现在,我们进入最后的操作步骤。 将最近加密的 lic_С++.txt 文件移至 MetaTrader 数据目录的 File 文件夹当中,并用先前编写的脚本 deleteuser.mq5 对其解密。 我们得到了预期的结果:尽管长度有所变化,但文件已成功解密。

那么,我们会得到什么结果呢? 最重要的是,我们检测到加密/解密参数,该参数允许将加密文件从一个程序传输到另一个程序。 显然,我们可以稍后假定,如果加密/解密失败,则该问题可能是由应用程序中的错误引起的。

哈希值

如您一样的许多人可能都知道,加密不仅限于加密/解密。 我们来研究一个加密原语 - 哈希散列。 此过程意味着将某个任意数组转换为固定长度的数组。 这样的数组称为哈希值,转换函数称为 hash 函数。 两个初始数组至少在某一数位上彼此不同,将产生完全不同的哈希值,这些哈希值可用于识别和比较。赫兹股票量化软件

此为一个示例。 用户在站点上注册,并输入他的识别数据。 数据保存在一个非常机密的数据库当中。 现在,同一用户尝试在主页上输入登录名和密码,从而登录该站点。 网站应该怎么做? 它可以简单地从数据库中检索用户的密码,并将其与输入的密码进行比较。 但这并不安全。 安全的方式是取所存储密码的哈希值并和输入密码的哈希值进行比较。 即使存储的哈希值被盗,密码本身依然保持安全。 通过比较哈希值,我们可以确定输入的密码是否正确。

哈希是一个单向过程。 即使拿到哈希值,也不可能将其还原为数据数组。 因此,哈希对于密码学非常重要。 我们研究不同环境中的哈希计算。

我们的目的相同:找出在终端程序和其它第三方程序中进行计算时,如何确保相同初始数据的哈希值相同。

在 MQL 中,用我们之前所用的库函数CryptEncode计算哈希值。 为计算哈希值,应设置method函数的参数值。 我们采用CRYPT_HASH_SHA256值。 文档提供了其他值和其他哈希类型,因此您可以进一步阅读本主题。 采用现有密码行:“qwertyuiopasdfgh”作为源数组。 计算其哈希值,并将哈希值写入文件。 所得代码非常简单;因此,我们简单地将其包含在附加的脚本文件decryptuser.mq5当中,而无需创建单独的类和函数:

string k = "qwertyuiopasdfgh";uchar key[], result[], enc[];StringToCharArray(k, enc);int sha = CryptEncode(CRYPT_HASH_SHA256,enc,key,result); string sha256;for(int i = 0; i < sha; i++) sha256 += StringFormat("%X ",result[i]);Print("SHA256 len: ",sha," Value:",sha256);int h = FileOpen("sha256.bin", FILE_WRITE | FILE_BIN);if (h == INVALID_HANDLE) {Print("File create failed: sha256.bin");}else {FileWriteArray(h, result);FileClose(h);}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。