Нативная реализация RSA-шифрования на MQL5
Представьте распространённую ситуацию: вашему советнику нужно отправить на сервер сигналы, ключи, данные для входа или другую важную информацию. Вы можете использовать HTTP или даже HTTPS и считать соединение достаточно безопасным. Но рано или поздно выясняется, что настоящая уязвимость находится вовсе не в соединении, а внутри самого кода.
В MQL5 бывают ситуации, когда у вас просто нет выбора и приходится встраивать чувствительные значения напрямую в EA. Параметры алгоритмов, приватные ключи, пароли — всё это может оказаться скомпилированным в файл. И хотя код скомпилирован и обфусцирован, достаточно квалифицированный человек всё ещё может извлечь эти значения. Это создаёт серьёзную проблему безопасности: транспорт может быть зашифрован, но сам советник остаётся потенциальной точкой утечки, потому что ему приходится хранить секреты внутри себя. При этом использование DLL для шифрования запрещено для продуктов Market. MQL5 не предоставляет RSA «из коробки», а лёгкие приёмы «шифрования» вроде XOR не дают реальной защиты. Многие разработчики упираются именно в это ограничение: им нужен способ защитить коммуникацию и ключи, но доступные инструменты либо ограничены, либо просто недостаточно надёжны.
Эта статья показывает, как обойти эти ограничения. В ней объясняется, как сделать так, чтобы ваш Expert Advisor (EA) больше не хранил внутри себя критически важные данные, а безопасно получал их с доверенного сервера в зашифрованном виде. Вы также узнаете, как реализовать корректное гибридное шифрование — RSA для обмена ключами и AES для быстрой передачи сообщений — полностью на MQL5, без DLL и внешних библиотек.
Введение в RSA
Название «RSA» образовано из первых букв фамилий Рона Ривеста, Ади Шамира и Леонарда Адлемана, которые представили алгоритм в 1977 году во время работы в MIT. Их работа стала первой практической реализацией криптосистемы с открытым ключом на основе однонаправленных математических функций, превратив теоретическую идею в пригодную для использования технологию. Хотя концепция криптографии с открытым ключом была предложена ранее Диффи и Хеллманом в 1976 году, именно конструкция RSA дала конкретный механизм для безопасного шифрования и цифровых подписей.
В основе RSA лежит математическая сложность факторизации больших составных чисел — задача, которая остаётся вычислительно невыполнимой при достаточно больших размерах ключа. Это свойство делает RSA одной из фундаментальных технологий современной криптографии. На протяжении десятилетий RSA использовался для защиты веб-трафика, аутентификации цифровых документов, обмена ключами и защиты чувствительных данных на множестве платформ и в разных протоколах.
Сегодня RSA остаётся одним из самых широко используемых алгоритмов асимметричного шифрования и является важной частью таких стандартов, как SSL/TLS, PGP, SSH, а также многих систем защищённой связи. Несмотря на появление эллиптической криптографии (ECC) и других современных альтернатив, RSA продолжает цениться за простоту, надёжность и давно устоявшуюся модель безопасности.

Математика, лежащая в основе
RSA — простой и элегантный алгоритм:
Работает он так: вы шифруете сообщение открытым ключом, а расшифровать его и восстановить исходный текст может только соответствующий закрытый ключ. Основа RSA опирается на формулу, показанную ниже. Если вам интересно, кто её открыл, заслуга принадлежит выдающемуся математику Леонарду Эйлеру. Теорема Эйлера (также известная как теорема Ферма–Эйлера или теорема Эйлера о функции Эйлера) является фундаментом системы RSA. Подробнее о ней можно прочитать в этой статье.
![]()
Вот что означают эти буквы:
- m: ваше сообщение (просто записанное как число).
- e и n: вместе образуют открытый ключ.
- d: это закрытый ключ.
- n: называется модулем.
Рассмотрим основное уравнение подробнее. Даже при ограниченных знаниях математики можно заметить, что к сообщению (m) применяются определённые математические операции, а результат этих операций восстанавливает исходное сообщение. Это даёт идею разделить уравнение на две асимметричные части, используя распределительное свойство операций по модулю. Сообщение m можно зашифровать, возведя его в степень e по модулю n, в результате чего получается шифртекст, Z (зашифрованное сообщение).
![]()
А зашифрованное сообщение Z можно расшифровать с помощью закрытого ключа d:
![]()
Продемонстрируем эту концепцию на простом числовом примере с m = 2, e = 3, d = 3 и n = 15. Используя формулу шифрования, получаем:
Z = (m ^ e) mod n = 2^3 mod 15 = 8
Теперь зашифрованное значение можно расшифровать с помощью закрытого ключа d = 3:
m = (Z^d) mod n = 8^3 mod 15 = 2
Мы даже можем написать небольшую программу на MQL5, реализующую приведённый выше числовой пример:
//+------------------------------------------------------------------+ //| Simple RSA Numerical Example (for educational purposes only) | //+------------------------------------------------------------------+ // Fast modular exponentiation: computes (base^exp) % mod int ModPow(int base, int exp, int mod) { long result = 1; long b = base % mod; while(exp > 0) { if(exp & 1) result = (result * b) % mod; b = (b * b) % mod; exp >>= 1; } return (int)result; } // RSA "encryption": c = m^e % n int EncryptRSA(int m, int e, int n) { return ModPow(m, e, n); } // RSA "decryption": m = c^d % n int DecryptRSA(int c, int d, int n) { return ModPow(c, d, n); } //+------------------------------------------------------------------+ //| Example based on small numbers (not secure!) | //+------------------------------------------------------------------+ void OnStart() { int m = 2; // message int e = 3; // public exponent int d = 3; // private exponent int n = 15; // modulus Print("Original message: ", m); int encrypted = EncryptRSA(m, e, n); Print("Encrypted: ", encrypted, " // 2^3 % 15 = 8"); int decrypted = DecryptRSA(encrypted, d, n); Print("Decrypted: ", decrypted, " // 8^3 % 15 = 2"); // Verifying the main RSA concept: // (m^e)^d % n = m int check = ModPow(ModPow(m, e, n), d, n); Print("Check (m^e)^d % n = ", check); }
Советы и приёмы:
Сначала нужно преобразовать текст в числа, прежде чем его можно будет зашифровать. Именно здесь используются ASCII или Unicode — каждая буква, пробел или символ превращается в число. Возьмём, например, «Hello world!». Его можно записать как одно огромное число: в десятичном виде → 22405534230753963835153736737 или в шестнадцатеричном → 0x48656c6c6f20776f726c6421. Однако модуль не может быть маленьким числом вроде 15; такое значение слишком мало для реальных сообщений. Модуль RSA должен быть больше числового представления сообщения, иначе математика шифрования не будет работать корректно. RSA-шифрование вычислительно затратно. Шифрование больших чисел с большими показателями степени, например 65537, требует множества умножений. Без оптимизации процессор может быть сильно загружен.
[0x00][0x02][random bytes][0x00][message]
При использовании дополнения повторная отправка одного и того же сообщения каждый раз даёт разные зашифрованные результаты. Это значительно усложняет злоумышленнику взлом шифрования. Без правильного дополнения и корректной работы с ключами RSA становится уязвимым к нескольким классам атак. На следующем шаге мы покажем, как на практике сгенерировать корректную пару ключей.
Генерация ключей с помощью OpenSSL:
Сначала убедитесь, что OpenSSL установлен в вашей операционной системе. RSA-ключи можно сгенерировать следующей командой OpenSSL (среда Windows)://bash openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048Открытые ключи и публичные сертификаты содержат открытый показатель степени и модуль, а закрытые ключи — закрытый показатель степени. Следующая команда выводит компоненты как открытого, так и закрытого ключа:
//bash openssl rsa -in private_key.pem -text -noout openssl rsa -in public_key.pem -pubin -text -nooutПосле выполнения этих команд вы увидите вывод, похожий на изображение ниже.

Именно так браузеры обрабатывают защищённые соединения SSL или TLS. Итак, RSA обеспечивает безопасную связь даже по незащищённым каналам. Для промышленной эксплуатации важно применять правильное дополнение и выбирать достаточно большой ключ — 2048 бит или больше. Такие инструменты, как OpenSSL, упрощают генерацию ключей и управление ими. Для разработки можно найти и другие онлайн-инструменты для RSA-шифрования, расшифрования или даже генерации ключей, но никогда не доверяйте им в рабочей среде. Хотя Windows предоставляет функциональность RSA через CryptoAPI, вызов внешних DLL добавляет сложность и потенциальные риски безопасности, особенно для Expert Advisors (EA). Когда клиенты используют ваш EA и видят, что ему требуется доступ к DLL, они могут насторожиться или даже потерять доверие к вашему продукту.
Проектирование алгоритма (шаг за шагом)
Процесс шифрования состоит из нескольких этапов, и для удобства понимания объяснение кода разделено на три части: Часть 1 охватывает основные концепции и инициализацию требований класса. Часть 2 вводит необходимые арифметические функции для больших целых чисел. А Часть 3 представляет итоговую реализацию процесса RSA-шифрования.
Часть 1
Создание класса RSA:
public class MQL5_RSA { private: uchar modulus[]; //will store the RSA modulus (n) as a big-endian byte array. int exponent; // holds the public exponent (usually 65537). bool debugMode; //toggles console logging. public: MQL5_RSA(bool debug=false) { debugMode = debug; MathSrand((int)TimeLocal()); // seed RNG once } };Инициализация:
Загрузите модуль (открытый ключ) как шестнадцатеричную строку, преобразуйте её в байты, нормализуйте и сохраните открытый показатель степени (обычно 65537).
void Init(string modulusHex, int e) { ArrayResize(modulus, 0); // clear previous values if(debugMode) PrintFormat("Init: modulus hex len=%d, e=%d", StringLen(modulusHex), e); HexToBytes(modulusHex, modulus); // convert hex → bytes Normalize(modulus); // remove leading zeros (important!) exponent = e; if(debugMode) PrintFormat("Init completed: modulus bytes=%d", ArraySize(modulus)); }
Исправление и написание HexToBytes():
Эта функция критически важна, потому что в исходном коде MetaQuotes была ошибка: он добавляет коды символов, а не hex-пары. Мы напишем корректную и требуемую версию:
void HexToBytes(string hex, uchar &out[]) { string clean = ""; int L = StringLen(hex); // keep only valid hex characters for(int i = 0; i < L; i++) { ushort c = StringGetCharacter(hex, i); if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) clean += StringSubstr(hex, i, 1); } // ensure even number of hex chars (pad if necessary) if(StringLen(clean) % 2 == 1) clean = "0" + clean; int n = StringLen(clean) / 2; ArrayResize(out, n); for(int i = 0; i < n; i++) { ushort c1 = StringGetCharacter(clean, i*2); ushort c2 = StringGetCharacter(clean, i*2+1); int high = HexNibble(c1); // convert 1 hex char -> 0..15 int low = HexNibble(c2); out[i] = (uchar)((high << 4) | low); // combine two nibbles → byte } }
Нормализация больших целых чисел:
Числа RSA представлены массивами в формате big-endian. Это означает, что индекс 0 содержит самый значимый байт. Ведущие нулевые байты нам не нужны, поэтому мы их удаляем:
void Normalize(uchar &a[]) { int size = ArraySize(a); int leading = 0; // count leading zero bytes while(leading < size && a[leading] == 0) leading++; if(leading == 0) return; // already normalized int newSize = size - leading; if(newSize <= 0) { ArrayResize(a, 0); return; } uchar temp[]; ArrayResize(temp, newSize); // copy only the non-zero part ArrayCopy(temp, a, 0, leading, newSize); ArrayResize(a, newSize); ArrayCopy(a, temp); }
На этом этапе класс RSA предоставляет базовую функциональность ядра. Класс хранит флаг режима отладки, принимает и нормализует модуль, корректно преобразует шестнадцатеричные значения в байты, удаляет ведущие нули и сохраняет значение показателя степени. Эта основа необходима перед реализацией операций арифметики больших целых чисел, таких как вычитание, сравнение и умножение, а затем — модульная арифметика и схема дополнения PKCS#1. После добавления этих возможностей процесс RSA-шифрования можно выполнять.
Часть 2
Создание движка больших целых чисел: Внутреннее устройство RSA
Далее мы строим систему арифметики больших целых чисел, необходимую для RSA. В MQL5 нет встроенного типа big-integer, поэтому RSA должен имитировать большие целые числа с помощью массивов байтов. В этом разделе разрабатывается математическая основа, поддерживающая схему дополнения PKCS#1 и модульное возведение в степень. Будут реализованы следующие функции:
- Compare();
- SubtractInPlace();
- LeftShiftBytes();
- Multiply();
- Mod();
- MulMod();
В совокупности эти функции имитируют настоящие операции арифметики больших целых чисел.
1. Compare() — определение, какое большое целое число больше:
Перед выполнением вычитания, деления или операций по модулю необходимо уметь сравнивать два числа.a > b or a < b or a == b
int Compare(const uchar &a_in[], const uchar &b_in[]) { // copy to avoid modifying original arrays uchar a[]; ArrayCopy(a, a_in); uchar b[]; ArrayCopy(b, b_in); Normalize(a); Normalize(b); int na = ArraySize(a), nb = ArraySize(b); // first compare lengths if(na > nb) return 1; if(na < nb) return -1; // lengths equal → compare byte by byte for(int i=0; i<na; i++) { if(a[i] > b[i]) return 1; if(a[i] < b[i]) return -1; } return 0; // equal }
2. SubtractInPlace() — вычитание в формате big-endian
Эта функция выполняет a = a − b. Операция выполняется байт за байтом, начиная справа, поскольку последний байт представляет наименее значимую часть числа. Заём приходится обрабатывать вручную. Эта функция широко используется в Mod() на этапе редукции длинным делением, а также встречается в промежуточных шагах умножения.
void SubtractInPlace(uchar &a[], const uchar &b[]) { // assume a >= b (Compare() must ensure this) int na = ArraySize(a); int nb = ArraySize(b); int borrow = 0; for(int i = 0; i < na; i++) { int ai = (int)a[na - 1 - i]; // rightmost byte of a int bi = (i < nb) ? (int)b[nb - 1 - i] : 0; // rightmost byte of b int diff = ai - bi - borrow; if(diff < 0) { diff += 256; // wrap around as unsigned byte borrow = 1; } else { borrow = 0; } a[na - 1 - i] = (uchar)diff; } Normalize(a); // remove leading zeros }
3. LeftShiftBytes() — умножение на 256ⁿ:
Во время операций длинного деления часто требуется сдвигать число влево, что фактически умножает его на степени 256 (то есть выполняет сдвиг на целые байты).void LeftShiftBytes(const uchar &in[], int shiftBytes, uchar &out[]) { int n = ArraySize(in); if(n == 0 || shiftBytes == 0) { ArrayCopy(out, in); return; } ArrayResize(out, n + shiftBytes); // copy original bytes for(int i=0; i<n; i++) out[i] = in[i]; // append zeros on the right (least significant side) for(int i=n; i<n+shiftBytes; i++) out[i] = 0; }При вычислении модуля делитель можно сдвигать до тех пор, пока он не выровняется с делимым.
Это ядро алгоритма длинного деления.
4. Multiply() — умножение «столбиком»:
Стандартная функция умножения реализуется классическим методом «столбиком» следующим образом:void Multiply(const uchar &a[], const uchar &b[], uchar &result[]) { int na = ArraySize(a); int nb = ArraySize(b); if(na==0 || nb==0) { ArrayResize(result, 0); return; } int nRes = na + nb; int temp[]; ArrayResize(temp, nRes); ArrayInitialize(temp, 0); // accumulator array (int for safety) // classic schoolbook multiplication for(int i = na - 1; i >= 0; i--) { int carry = 0; for(int j = nb - 1; j >= 0; j--) { int prod = (int)a[i] * (int)b[j] + temp[i + j + 1] + carry; temp[i + j + 1] = prod % 256; carry = prod / 256; } temp[i] += carry; } ArrayResize(result, nRes); for(int i=0; i<nRes; i++) result[i] = (uchar)temp[i]; Normalize(result); }
Это умножение — самая медленная часть чистой реализации RSA на MQL5, но оно необходимо для функций MulMod() и ModExp(). Эти функции будут подробно рассмотрены далее.
5. Mod(), модуль большого целого числа с использованием длинного деления:
Это самая важная процедура для больших целых чисел. Мы реализуем упрощённое длинное деление. Начнём с пустого массива остатка, затем для каждого байта делимого будем сдвигать остаток влево, добавлять следующий байт и вычитать модуль, пока остаток ≥ модулю. Итоговый остаток и будет нужным результатом.
bool Mod(const uchar &a_in[], const uchar &m_in[], uchar &result[]) { if(ArraySize(m_in) == 0) return false; uchar dividend[]; ArrayCopy(dividend, a_in); Normalize(dividend); uchar modv[]; ArrayCopy(modv, m_in); Normalize(modv); // if dividend < modulus → done if(Compare(dividend, modv) < 0) { ArrayCopy(result, dividend); return true; } int m = ArraySize(dividend); uchar rem[]; ArrayResize(rem, 0); for(int i = 0; i < m; i++) { // shift remainder left by 1 byte int rlen = ArraySize(rem); ArrayResize(rem, rlen + 1); rem[rlen] = dividend[i]; Normalize(rem); // subtract modulus while remainder >= modulus while(Compare(rem, modv) >= 0) { SubtractInPlace(rem, modv); } } Normalize(rem); ArrayCopy(result, rem); return true; }
6. MulMod() — умножение с последующим взятием по модулю:
Это просто соединяет две операции:
Multiply(a, b) → temp
Mod(temp, m) → out
bool MulMod(const uchar &a[], const uchar &b[], const uchar &m[], uchar &out[]) { uchar product[]; Multiply(a, b, product); return Mod(product, m, out); }
Часть 3
Теперь у нас есть все необходимые инструменты для арифметики RSA. Сначала к сообщению нужно применить схему дополнения PKCS#1 v1.5 перед шифрованием. Затем реализуется функция ModExp(), вычисляющая: result = base^exp mod n. Это ключевая операция RSA, формирующая шифртекст. Для удобного представления результата используется вспомогательная функция Base64Encode(), которая позволяет выводить шифртекст в текстовом виде.
Модульное возведение в степень (ModExp) — сердце RSA:
Функция ModExp() эффективно вычисляет result = base^exp mod n для больших показателей степени с помощью повторного возведения в квадрат и модульного умножения. Она опирается на MulMod() (умножить, затем сократить по модулю), благодаря чему все промежуточные значения остаются достаточно малыми для обработки.
bool ModExp(const uchar &base[], int exp, const uchar &modn[], uchar &result[]) { if(debugMode) PrintFormat("ModExp: exp=%d", exp); // Work on a copy to avoid mutating caller arrays uchar baseCopy[]; ArrayCopy(baseCopy, base); Normalize(baseCopy); // Reduce base modulo modn first: base = base % modn if(Compare(baseCopy, modn) >= 0) { if(!Mod(baseCopy, modn, baseCopy)) return false; // mod failed } // Initialize result = 1 (big-int representation) uchar res[]; ArrayResize(res, 1); res[0] = 1; Normalize(res); // basePow holds current power of base (base^(2^i)) uchar basePow[]; ArrayCopy(basePow, baseCopy); int e = exp; // local copy so we can shift it // Square-and-multiply loop while(e > 0) { // If current LSB is 1 → multiply into result if((e & 1) == 1) { uchar tmp[]; if(!MulMod(res, basePow, modn, tmp)) return false; // MulMod failed ArrayCopy(res, tmp); // res = (res * basePow) % modn } // Move to next bit e >>= 1; // If still bits left, square basePow: basePow = (basePow * basePow) % modn if(e > 0) { uchar tmp2[]; if(!MulMod(basePow, basePow, modn, tmp2)) return false; // MulMod failed ArrayCopy(basePow, tmp2); } } Normalize(res); ArrayCopy(result, res); // return result via out param return true; }
Схема дополнения PKCS#1 v1.5 и шифрование (EncryptPKCS1v15):
Сначала нужно сформировать сообщение с дополнением EM = 0x00 || 0x02 || PS || 0x00 || M, где PS — ненулевые случайные байты, и убедиться, что длина сообщения меньше k - 11 (k = длина модуля в байтах), поскольку служебные байты учитываются в итоговой длине сообщения.
bool EncryptPKCS1v15(uchar &plain[], uchar &cipher[]) { int k = ArraySize(modulus); // modulus size (bytes) int mlen = ArraySize(plain); // message length (bytes) if(debugMode) PrintFormat("Encrypt: key bytes=%d, plain=%d, max=%d", k, mlen, k-11); // PKCS#1 v1.5 requires at least 11 bytes of overhead if(mlen > k - 11) { if(debugMode) Print("Error: data too large for key"); return false; } // Build the encoded message EM (k bytes) uchar em[]; ArrayResize(em, k); ArrayInitialize(em, 0); // default everything to 0 em[0] = 0x00; // leading zero by spec em[1] = 0x02; // block type 2 (encryption) int psLen = k - mlen - 3; // length of padding string PS // Fill PS with non-zero random bytes for(int i=0; i<psLen; i++) { uchar b = 0; // loop until rand byte is non-zero (spec requirement) do { b = (uchar)(MathRand() % 256); } while(b == 0); em[2 + i] = b; } // Separator before message em[2 + psLen] = 0x00; // Copy message into EM at the right position ArrayCopy(em, plain, 3 + psLen, 0, mlen); if(debugMode) Print("EM constructed"); // Modular exponentiation: C = EM^e mod n uchar cBig[]; if(!ModExp(em, exponent, modulus, cBig)) { if(debugMode) Print("ModExp failed"); return false; } // Ensure ciphertext byte array has exact length k int clen = ArraySize(cBig); ArrayResize(cipher, k); if(clen < k) { // left-pad with zeros ArrayInitialize(cipher, 0); ArrayCopy(cipher, cBig, k - clen, 0, clen); } else { ArrayCopy(cipher, cBig); // already k bytes or longer (shouldn't be longer normally) } if(debugMode) Print("Encryption finished"); return true; }
Строка заполнения (PS) должна состоять из ненулевых случайных байтов. В этой реализации используется MathRand(), инициализированный в конструкторе. Хотя это лучше, чем неинициализированный генератор случайных чисел, он не является криптографически стойким. В промышленных системах следует использовать криптографически стойкий ГСЧ. Схема дополнения PKCS#1 v1.5 широко поддерживается, но имеет известные уязвимости, например атаки с оракулом дополнения. Для новых систем предпочтителен RSAES-OAEP. Функция дополнения возвращает false, если сообщение слишком длинное для выбранного ключа.
Вспомогательная функция вывода Base64 (Base64Encode):
Шифртексты — это бинарные данные. Для журналирования, передачи по HTTP или хранения в текстовом виде Base64 предоставляет удобное и стандартизованное представление.
string Base64Encode(uchar &data[]) { string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int len = ArraySize(data); string out = ""; // process 3 bytes => 4 base64 chars for(int i = 0; i < len; i += 3) { int b0 = data[i]; int b1 = (i + 1 < len) ? data[i + 1] : 0; int b2 = (i + 2 < len) ? data[i + 2] : 0; int val = (b0 << 16) | (b1 << 8) | b2; // append 4 chars, using '=' padding for missing bytes out += StringSubstr(chars, (val >> 18) & 0x3F, 1); out += StringSubstr(chars, (val >> 12) & 0x3F, 1); out += (i + 1 < len) ? StringSubstr(chars, (val >> 6) & 0x3F, 1) : "="; out += (i + 2 < len) ? StringSubstr(chars, val & 0x3F, 1) : "="; } return out; }
Собираем всё вместе
Чтобы сохранить читаемость, полный исходный код класса RSA здесь не приводится. Полная реализация предоставлена в виде библиотеки RSA.mqh доступной для скачивания внизу страницы. Библиотека содержит все компоненты, рассмотренные в статье, а также пример с комментариями, демонстрирующий использование класса. Её можно использовать для быстрой интеграции либо расширять и изменять после понимания внутреннего устройства. Если возникнут ошибки или потребуется уточнение по какой-либо части реализации, читателям предлагается оставить комментарий ниже.
Примеры использования
Базовая реализация RSA:
Этот простой пример показывает, как может быть реализовано RSA-шифрование. Сначала создайте экземпляр класса RSA, затем подготовьте сообщение (данные, которые нужно зашифровать) как uchar[], где каждый элемент — один байт. Наконец, вызовите EncryptPKCS1v15(plain, cipher), чтобы выполнить шифрование. Результат представляет собой массив char и сохраняется в cipherData[]:
void OnStart() { MQL5_RSA rsa; string modulusHex = "A1B2C3D4E5F6..."; // Your modulus in hex int exponent = 65537; // exponent value rsa.Init(modulusHex, exponent); string plainText = "Hello RSA!"; //Data that you want to be encrypted. uchar plainData[]; StringToCharArray(plainText, plainData, 0, StringLen(plainText)); uchar cipherData[]; if(rsa.EncryptPKCS1v15(plainData, cipherData)) { string base64Cipher = rsa.Base64Encode(cipherData); Print("Encrypted: ", base64Cipher); } else { Print("Encryption failed!"); } }
Использование RSA + AES в реальном проекте:

Рабочий процесс гибридного шифрования обычно состоит из следующих шагов:
- Сгенерировать случайный сеансовый ключ AES (например, 128 или 256 бит).
- Зашифровать этот AES-ключ с помощью открытого ключа RSA. Позже расшифровать его сможет только владелец соответствующего закрытого ключа.
- Зашифровать фактическую полезную нагрузку данных с помощью AES — быстрого симметричного шифра, идеально подходящего для больших сообщений или файлов.
- Передать оба компонента вместе:
- Данные, зашифрованные AES (быстро и компактно).
- AES-ключ, зашифрованный RSA (маленький, но защищённый).
В контексте MQL5 эта стратегия позволяет разработчикам защищать коммуникацию между Expert Advisors, индикаторами и внешними серверами даже при использовании обычных HTTP- или сокет-соединений. Реализованный в этой статье класс RSA можно использовать для шифрования AES-ключа, а встроенные функции MQL5 CryptEncode() и CryptDecode() — для AES-шифрования и расшифрования фактического сообщения.
Это позволяет создать полностью самодостаточный слой безопасности внутри MetaTrader 5, фактически формируя облегчённую версию «HTTPS поверх HTTP». Его можно использовать для защиты чувствительных данных, таких как торговые команды, учётные данные аутентификации или конфигурационные сообщения, без внешних библиотек шифрования или DLL.
Шаг 1— сервер генерирует открытый ключ RSA:
Используйте Python, Java, OpenSSL или любую серверную среду, чтобы сгенерировать модуль (шестнадцатеричную строку), показатель степени (обычно 65537) и закрытый ключ (хранится на стороне вашего сервера).
EA будет использовать только открытые значения. Не раскрывайте закрытый ключ на стороне клиента.
modulusHex = "A1B2C3…"; exponent = 65537;
Шаг 2— EA создаёт экземпляр RSA и загружает ключ:
#include <RSA.mqh>
MQL5_RSA rsa;
rsa.Init(modulusHex, exponent); Шаг 3 — EA подготавливает сообщение запроса:
Пример полезной нагрузки может быть запросом входа, содержащим: ID EA, номер счёта и timestamp. Сформируйте корректную JSON-строку для своего запроса. Для создания JSON-строки можно использовать любую стороннюю библиотеку. Фрагмент ниже показывает, как должна выглядеть ожидаемая JSON-строка:
string json = "{\"cmd\":\"login\"," "\"account\":" + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) + "," "\"ts\":" + IntegerToString((int)TimeCurrent()) + "," "}"; uchar plain[]; StringToCharArray(json, plain, 0, StringLen(json));
Шаг 4 — EA шифрует сеансовый AES-ключ с помощью RSA:
AES-ключ, используемый для остальной части сессии:
uchar aesKey[]; GenerateAESKey(aesKey); // 16 random bytes (AES-128) uchar encryptedAes[]; rsa.EncryptPKCS1v15(aesKey, encryptedAes); string encryptedAesBase64 = rsa.Base64Encode(encryptedAes);Шаг 5 — EA шифрует фактические данные с помощью AES:
uchar encryptedPayload[]; CryptEncode(CRYPT_AES128, plain, aesKey, encryptedPayload); string payloadBase64 = CryptBase64Encode(encryptedPayload);
Шаг 6 — EA отправляет всё на сервер:
string body = "{\"key\":\"" + encryptedAesBase64 + "\"," + "\"data\":\"" + payloadBase64 + "\"}"; string result; char headers[]; int status = WebRequest("POST", url, headers, 5000, body, result);
Шаг 7 — расшифрование на стороне сервера (концептуально):
На сервере сначала выполните base64-декодирование RSA-зашифрованного AES-ключа, затем расшифруйте AES-ключ с помощью закрытого RSA-ключа. После восстановления AES-ключа его можно использовать для расшифрования полезной нагрузки данных. Поскольку AES-ключ случайно генерируется для каждой сессии, каждое сообщение остаётся уникальным и защищённым, даже если злоумышленник перехватит трафик. Этот механизм фактически устанавливает защищённую сессию, аналогичную упрощённому HTTPS-туннелю.
Шаг 8 — клиентская сторона расшифровывает ответ сервера:
uchar responseCipher[]; CryptBase64Decode(result, responseCipher); uchar responsePlain[]; CryptDecode(CRYPT_AES128, responseCipher, aesKey, responsePlain); string serverReply = CharArrayToString(responsePlain); Print("Server replied: ", serverReply);Следуя этим шагам, реальную криптографию можно реализовать напрямую в среде MetaTrader 5, получив надёжное и полностью переносимое решение для конечных пользователей.
Заключение
В этой статье была представлена полная и функциональная библиотека RSA-шифрования, написанная целиком на MQL5, показывающая, что даже сложные математические алгоритмы можно реализовать напрямую в среде MetaTrader 5 без внешних зависимостей. В этой работе мы показали, что MQL5 не ограничивается автоматизацией торговли — он также способен выполнять продвинутые вычислительные задачи, такие как модульная арифметика и криптография с открытым ключом.
На этой основе разработчики теперь получают практический набор инструментов для создания защищённых систем обмена сообщениями, защиты конфигурационных файлов, поддержки безопасного обмена ключами и зашифрованной коммуникации непосредственно внутри MetaTrader 5. Это позволяет Expert Advisors и индикаторам безопасно взаимодействовать с удалёнными сервисами, аутентифицировать источники данных и обмениваться зашифрованными торговыми командами, не раскрывая чувствительную информацию в сети.
Реализация также подчёркивает важную мысль: криптография не обязана опираться на внешние DLL или сторонние библиотеки, чтобы быть эффективной. Когда алгоритмы реализованы нативно в MQL5, код остаётся прозрачным, переносимым и полностью контролируемым разработчиком. Это обеспечивает как проверяемость, так и соответствие политикам безопасности MetaQuotes, что особенно важно при разработке коммерческих торговых инструментов, распространяемых через Market.
Для разработчиков, заинтересованных в развитии этой работы, возможны несколько направлений. Библиотеку RSA можно объединить с цифровыми подписями для проверки подлинности и целостности сообщений или расширить современными схемами заполнения, такими как OAEP, для повышения устойчивости к криптоанализу. Аналогично, интеграция AES в аутентифицированных режимах, например GCM или CBC-HMAC, может обеспечить и шифрование, и обнаружение подмены за один шаг. Такие улучшения приблизят реализацию к современным стандартам вроде TLS и PGP — полностью внутри среды MQL5.
С практической точки зрения эта статья призывает разработчиков воспринимать MQL5 не просто как язык торговых скриптов, а как полноценную программную среду, где сосуществуют безопасность, сетевое взаимодействие и вычисления. Сочетая математическую точность с аккуратным проектированием, разработчики MQL5 могут создавать защищённые самодостаточные системы, способные уверенно взаимодействовать с внешними API, брокерами или облачными сервисами.
Итак, криптография в MQL5 одновременно возможна и мощна. Теперь у разработчиков есть инструменты, чтобы экспериментировать, расширять и напрямую интегрировать механизмы защищённой коммуникации в свои проекты. По мере того как торговая экосистема развивается в сторону более связных и ориентированных на данные архитектур, способность обрабатывать шифрование и аутентификацию внутри терминала будет становиться всё более ценной. Имея эту основу, каждый разработчик может сделать значимые шаги к созданию более безопасных, умных и устойчивых торговых систем.
Авторы и программисты
- Vahid Irak
- Siavash Nourmohammadi
Ссылки и дополнительное чтение
Для читателей, интересующихся математическими основами и современными применениями RSA- и AES-шифрования, следующие источники дают надёжные и подробные объяснения:
- Wikipedia – PKCS #1: стандарт криптографии RSA
Объясняет официальный стандарт RSA, включая схемы заполнения, такие как PKCS#1 v1.5 и OAEP, используемые для шифрования и подписей.
Официальная документация MQL5 по CryptEncode(), CryptDecode() и другим встроенным функциям для AES и хеширования. - Paar, C. & Pelzl, J. (2024). Understanding Cryptography – учебник для студентов и практиков, глава 7: криптосистема RSA. Springer. ссылка
- Mollin, R. A. (2023). RSA и криптография с открытым ключом. Routledge. ссылка
- National Institute of Standards and Technology. FIPS 197: Advanced Encryption Standard (AES). 2001 (обновлено). ссылка
- Tuo, Z. (2023). «Сравнительный анализ алгоритмов AES и RSA и их интегрированного применения». Theoretical and Natural Science, Vol 25, pp. 28-35. ссылка
- «Гибридное шифрование RSA-AES: объединение преимуществ симметричных и асимметричных алгоритмов». IJRAR, 2023. ссылка
- Ganesh, R., Khan, A. R. и др. (2025). «Панорамный обзор Advanced Encryption Standard (AES)». International Journal of Information Security. ссылка
- Упрощённое объяснение того, как работает шифрование/расшифрование сообщений RSA
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/20273
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Торговые инструменты MQL5 (Часть 23): Трёхмерные графики с управляемой камерой и поддержкой DirectX для анализа распределений
Осваиваем графики Kagi в MQL5 (Часть I): Создание движка графика Kagi
Моделирование рынка: Первые шаги на SQL в MQL5 (V)
От начального до среднего уровня: События в объектах (II)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования