Reine Implementierung der RSA-Verschlüsselung in MQL5
Stellen Sie sich eine häufige Situation vor: Ihr Expert Advisor muss Signale, Schlüssel, Anmeldedaten oder andere wichtige Daten an einen Server senden. Sie können HTTP oder sogar HTTPS verwenden und die Verbindung als einigermaßen sicher betrachten. Aber früher oder später stellt man fest, dass die eigentliche Schwachstelle gar nicht in der Verbindung liegt, sondern im Code selbst.
In MQL5 gibt es Momente, in denen Sie einfach keine andere Wahl haben, als sensible Werte direkt in den EA einzubetten. Algorithmus-Parameter, private Schlüssel, Passwörter – all das kann in die Datei kompiliert werden. Und auch wenn der Code kompiliert und verschleiert ist, kann jemand mit genügend Geschick diese Werte extrahieren. Dies führt zu einem erheblichen Sicherheitsproblem: Ihr Transport mag verschlüsselt sein, doch der Expert Advisor selbst bleibt ein potenzielles Leck, da er intern Geheimnisse speichern muss. Gleichzeitig ist die Verwendung von DLLs für die Verschlüsselung für Market-Produkte verboten. MQL5 bietet standardmäßig kein RSA, und leichtgewichtige Tricks einer „Verschlüsselung“ wie XOR bieten keinen echten Schutz. Viele Entwickler stecken an diesem Punkt fest: Sie benötigen eine Möglichkeit, die Kommunikation zu sichern und ihre Schlüssel zu schützen, aber die verfügbaren Tools sind entweder eingeschränkt oder einfach nicht stark genug.
Der vorliegende Artikel zielt darauf ab, diese Einschränkungen zu beseitigen. Es wird erklärt, wie Sie sicherstellen, dass Ihr Expert Advisor (EA) keine kritischen Daten mehr intern speichert, sondern sie stattdessen sicher von einem vertrauenswürdigen Server in verschlüsselter Form abruft. Sie werden auch lernen, wie man eine geeignete hybride Verschlüsselung implementiert – unter Verwendung von RSA für den Austausch von Schlüsseln und AES für schnelle Nachrichtenübermittlung – vollständig in MQL5, ohne auf DLLs oder externe Bibliotheken angewiesen zu sein.
Einführung in RSA
Der Name „RSA“ stammt von den Initialen von Ron Rivest, Adi Shamir und Leonard Adleman, die den Algorithmus 1977 während ihrer Arbeit am MIT vorstellten. Ihre Arbeit war die erste praktische Umsetzung eines Kryptosystems mit öffentlichem Schlüssel, das auf mathematischen Einwegfunktionen basierte und aus einer theoretischen Idee eine verwendbare Technologie machte. Obwohl das Konzept der Public-Key-Kryptografie bereits 1976 von Diffie und Hellman vorgeschlagen wurde, war es die RSA-Konstruktion, die einen konkreten Mechanismus für sichere Verschlüsselung und digitale Signaturen lieferte.
Im Kern beruht RSA auf der mathematischen Schwierigkeit, große zusammengesetzte Zahlen zu faktorisieren – ein Problem, das für ausreichend große Schlüsselgrößen rechnerisch unlösbar bleibt. Diese Eigenschaft macht RSA zu einer grundlegenden Technologie in der modernen Kryptografie. Seit Jahrzehnten wird es zur Sicherung des Internetverkehrs, zur Authentifizierung digitaler Dokumente, zum Austausch von Schlüsseln und zum Schutz sensibler Daten über unzählige Plattformen und Protokolle hinweg eingesetzt.
Heute ist RSA nach wie vor einer der am weitesten verbreiteten asymmetrischen Verschlüsselungsalgorithmen und bildet einen wichtigen Bestandteil von Standards wie SSL/TLS, PGP, SSH und vielen sicheren Kommunikationssystemen. Trotz des Aufkommens der Kryptographie mit elliptischen Kurven (ECC) und anderer moderner Alternativen wird RSA weiterhin wegen seiner Einfachheit, Robustheit und seines seit Langem etablierten Sicherheitsmodells geschätzt.

Die Mathematik dahinter
RSA ist ein einfacher und eleganter Algorithmus:
So funktioniert er: Sie verschlüsseln Ihre Nachricht mit einem öffentlichen Schlüssel, und nur der passende private Schlüssel kann sie entschlüsseln und die ursprüngliche Nachricht wiederherstellen. Der Kern von RSA basiert auf der nachstehenden Formel. Wenn Sie sich fragen, wer sie entdeckt hat, so geht der Verdienst an den brillanten Mathematiker Leonhard Euler. Das Euler'sche Theorem (auch bekannt als Fermat-Euler-Theorem oder Euler'sche Phi-Funktion) ist die Grundlage des RSA-Systems. Mehr darüber können Sie in diesem Artikel lesen.
![]()
Die Bedeutung dieser Buchstaben ist folgende:
- m: ist Ihre Nachricht (nur als Zahl geschrieben).
- e und n: bilden zusammen den öffentlichen Schlüssel.
- d: ist der private Schlüssel.
- n: wird als Modulo bezeichnet.
Schauen wir uns die Hauptgleichung genauer an. Selbst mit begrenztem mathematischem Hintergrundwissen ist es möglich zu erkennen, dass bestimmte mathematische Operationen auf die Nachricht (m) angewendet werden und dass das Ergebnis der Operation die ursprüngliche Nachricht wiederherstellt. Dies ermöglicht die Aufteilung der Gleichung in zwei asymmetrische Teile unter Verwendung der distributiven Eigenschaft von Modulo-Operationen. Die Nachricht m kann verschlüsselt werden, indem sie mit der Potenz von e modulo n verschlüsselt wird, was einen Geheimtext ergibt, Z (die verschlüsselte Nachricht).
![]()
Und die verschlüsselte Nachricht Z kann mit dem privaten Schlüssel entschlüsselt werden d:
![]()
Wir wollen dieses Konzept anhand eines einfachen Zahlenbeispiels mit m = 2, e = 3, d = 3 und n = 15 veranschaulichen. Nach der Verschlüsselungsformel ergibt sich:
Z = (m ^ e) mod n = 2^3 mod 15 = 8
Der verschlüsselte Wert kann nun mit dem privaten Schlüssel d = 3 entschlüsselt werden:
m = (Z^d) mod n = 8^3 mod 15 = 2
Wir können sogar ein kleines MQL5-Programm erstellen, um das obige numerische Beispiel zu implementieren:
//+------------------------------------------------------------------+ //| 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); }
Tipps und Tricks:
Zunächst müssen wir den Text in Zahlen umwandeln, bevor wir ihn verschlüsseln können. Hier kommen ASCII oder Unicode ins Spiel – jeder Buchstabe, jedes Leerzeichen oder Symbol wird zu einer Zahl. Nehmen wir zum Beispiel „Hello world!“. Man kann sie als eine große Zahl ausschreiben: dezimal → 22405534230753963835153736737 oder in hex → 0x48656c6c6f20776f726c6421. Der Modulo kann jedoch keine kleine Zahl wie 15 sein; ein solcher Wert ist für echte Nachrichten viel zu klein. Der RSA-Modulo muss größer sein als die numerische Darstellung der Nachricht, sonst funktioniert die Verschlüsselungsmathematik nicht richtig. Die RSA-Verschlüsselung ist rechenintensiv. Die Verschlüsselung großer Zahlen mit großen Exponenten, wie z. B. 65537, erfordert viele Multiplikationen. Ohne Optimierung kann die CPU stark belastet werden.
[0x00][0x02][random bytes][0x00][message]
Durch das Padding werden beim mehrmaligen Senden derselben Nachricht unterschiedliche verschlüsselte Ergebnisse erzielt. Dadurch wird es für einen Angreifer erheblich schwieriger, die Verschlüsselung zu kompromittieren. Ohne ordnungsgemäßes Padding und korrekte Schlüsselverwaltung ist die RSA-Verschlüsselung für mehrere Angriffsklassen anfällig. Im folgenden Schritt demonstrieren wir, wie man in der Praxis ein geeignetes Schlüsselpaar erzeugt.
Schlüsselerzeugung mit OpenSSL:
Vergewissern Sie sich zunächst, dass OpenSSL auf Ihrem Betriebssystem installiert ist. RSA-Schlüssel können mit dem folgenden OpenSSL-Befehl erzeugt werden (Windows-Umgebung)://bash openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048Öffentliche Zertifikate enthalten den öffentlichen Exponenten und den Modulo, während private Schlüssel den privaten Exponenten enthalten. Der folgende Befehl zeigt sowohl die Komponenten des öffentlichen als auch die des privaten Schlüssels an:
//bash openssl rsa -in private_key.pem -text -noout openssl rsa -in public_key.pem -pubin -text -nooutNachdem Sie diese Befehle ausgeführt haben, erhalten Sie eine Ausgabe ähnlich der folgenden Abbildung.

So handhaben die Browser sichere Verbindungen mit SSL oder TLS. Zusammenfassend kann gesagt werden, dass RSA eine sichere Kommunikation auch über unsichere Kanäle ermöglicht. Bei der Verwendung ist es wichtig, dass Sie einen ausreichend großen Schlüssel wählen – 2048 Bit oder mehr. Tools wie OpenSSL vereinfachen die Schlüsselerzeugung und -verwaltung. Sie können auch andere Online-Tools für die RSA-Verschlüsselung und -Entschlüsselung oder sogar für die Schlüsselgenerierung finden und diese für die Entwicklung verwenden, ihnen aber niemals für die Produktion vertrauen. Obwohl Windows die RSA-Funktionalität über die CryptoAPI bereitstellt, führt der Aufruf externer DLLs zu zusätzlicher Komplexität und potenziellen Sicherheitsrisiken, insbesondere für Expert Advisors (EAs). Wenn Kunden Ihren EA verwenden und sehen, dass er DLL-Zugriff erfordert, zögern sie möglicherweise oder verlieren sogar das Vertrauen in Ihr Produkt.
Algorithmusentwurf (Schritt für Schritt)
Der Verschlüsselungsprozess besteht aus mehreren Schritten, und um ihn leichter nachvollziehen zu können, wurde die Erklärung des Codes in drei Teile unterteilt: Teil 1 behandelt die Kernkonzepte und die Initialisierung der Klassenanforderungen. In Teil 2 werden die notwendigen arithmetischen Funktionen für große ganze Zahlen vorgestellt. In Teil 3 wird die endgültige Implementierung des RSA-Verschlüsselungsverfahrens vorgestellt.
Teil 1
Erstellen der RSA-Klasse:
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 } };Initialisierung:
Wir laden den Modulo (öffentlicher Schlüssel) als Hex-String, konvertieren ihn in Bytes, normalisieren ihn und speichern den öffentlichen Exponenten (normalerweise 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)); }
Korrigieren und Schreiben von HexToBytes():
Diese Funktion ist von entscheidender Bedeutung, da der ursprüngliche MetaQuotes-Code einen Fehler aufwies; er fügt Zeichencodes an, keine Hexpaare. Wir schreiben eine korrekte und geforderte Version:
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 } }
Normalisierung großer Ganzzahlen:
RSA-Zahlen sind Big-Endian-Arrays. Das bedeutet, dass Index 0 das höchstwertige Byte ist. Wir wollen niemals führende Null-Bytes, also schneiden wir sie ab:
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); }
An dieser Stelle bietet die RSA-Klasse die wesentlichen Kernfunktionen. Die Klasse kann den Debug-Status speichern, Modulo akzeptieren und normalisieren, hexadezimale Werte korrekt in Bytes umwandeln, führende Nullen entfernen und den Exponentenwert speichern. Diese Grundlage ist unerlässlich, bevor Sie arithmetische Operationen mit großen Zahlen (Big-Integer) wie Subtraktion, Vergleich und Multiplikation implementieren, gefolgt von modularer Arithmetik und dem Padding durch PKCS#1. Mit diesen Funktionen kann der RSA-Verschlüsselungsprozess ausgeführt werden.
Teil 2
Aufbau der Big-Integer-Maschine: Das Einmaleins der RSA
Als Nächstes bauen wir das für RSA erforderliche Big-Integer-Arithmetiksystem auf. MQL5 bietet keinen nativen Big-Integer-Typ, sodass RSA große Ganzzahlen mit Arrays von Bytes simulieren muss. In diesem Abschnitt wird das mathematische Grundgerüst entwickelt, das das Padding von PKCS#1 und modulare Potenzierung unterstützt. Die folgenden Funktionen werden implementiert:
- Compare();
- SubtractInPlace();
- LeftShiftBytes();
- Multiplizieren();
- Mod();
- MulMod();
Zusammen emulieren diese Funktionen echte Arithmetikoperationen mit großen Ganzzahlen, den „big-integer“
1.Compare() – Bestimmt, welche große ganze Zahl größer ist:
Bevor man Subtraktions-, Divisions- oder Modulo-Operationen durchführen kann, muss man in der Lage sein, zwei Zahlen zu vergleichen.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-Subtraktion
Diese Funktion führt a = a – b aus. Die Operation wird byteweise durchgeführt, beginnend von rechts, da das letzte Byte den niederwertigsten Teil der Zahl darstellt. Diese Funktion wird in Mod() während der Reduktionsphase der „langen“ Division ausgiebig verwendet und erscheint auch in den Zwischenschritten der Multiplikation.
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() – Multiplizieren mit 256ⁿ:
Bei „langen“ Divisionsoperationen ist es oft notwendig, eine Zahl nach links zu verschieben, um sie mit Potenzen von 256 zu multiplizieren (d. h. Verschiebung um ganze Bytes).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; }Bei der Berechnung des Moduls kann der Divisor verschoben werden, bis er mit dem Dividend übereinstimmt.
Dies ist der Kern des Algorithmus der „lange“ Division.
4. Multiply() – Schulbuch Multiplikation:
Die Standard-Multiplikationsfunktion wird nun mit der klassischen „Schulbuch-Methode“ im Folgenden implementiert: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); }
Diese Multiplikation ist der langsamste Teil der reinen MQL5-RSA-Implementierung, aber sie ist für die Funktionen MulMod() und ModExp() notwendig. Diese Funktionen werden später im Detail besprochen.
5. Mod(), Big-Integer Modulo mit „langer“ Division:
Dies ist die wichtigste ganzzahlige Routine, die wir in einer vereinfachten „langen“ Division implementieren. Wir beginnen m it dem Rest als leeres Array und verschieben dann für jedes Byte der Dividende den Rest nach links, hängen das nächste Byte an und subtrahieren den Modulo, wobei der Rest ≥ Modulo ist. Der endgültige Rest wäre das gewünschte Ergebnis.
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() – Multiplikation, gefolgt von Mod:
Das einfach zusammengefügt:
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); }
Teil 3
Wir haben nun alle notwendigen Werkzeuge für die RSA-Arithmetik. Zunächst muss die Sicherheit der Nachricht durch das Padding mit PKCS#1 v1.5 vor der Verschlüsselung erhöht werden. Anschließend wird die Funktion ModExp() implementiert, um Folgendes zu berechnen: Ergebnis = base^exp mod n. Dies ist die Kernoperation von RSA, die den Chiffretext erzeugt. Schließlich kann die Ausgabe mit der Hilfsfunktion Base64Encode() standardisiert werden, sodass der Chiffretext bequem angezeigt werden kann.
Modulare Potenzierung (ModExp) – Das Herzstück von RSA:
Die Funktion ModExp() berechnet effizient das Ergebnis = base^exp mod n für große Exponenten durch wiederholtes Quadrieren und modulare Multiplikation. Es stützt sich auf MulMod() (multiplizieren und dann reduzieren), damit alle Zwischenwerte klein genug bleiben, um sie zu verarbeiten.
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; }
Padding (PKCS#1 v1.5) und Verschlüsselung (EncryptPKCS1v15 ):
Zuerst sollten wir die „aufgepolsterte“ Nachricht EM = 0x00 || 0x02 || PS || 0x00 || M erstellen, wobei PS ein Zufallsbyte ungleich Null ist, und sicherstellen, dass die Nachrichtenlänge kleiner als k – 11 ist (k = Modulo-Länge in Bytes), da Overhead-Bytes für die endgültige Nachrichtenlänge mitgezählt werden.
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; }
Die Paddings-Zeichenfolge (PS) muss aus Zufallsbytes bestehen, die nicht Null sind. In dieser Implementierung wird MathRand() verwendet, das im Konstruktor eingesetzt wird. Dies ist zwar eine Verbesserung gegenüber einem unbestückten Zufallszahlengenerator, aber kryptografisch nicht sicher. Produktionssysteme sollten einen kryptographisch sicheren RNG verwenden. PKCS#1 v1.5 wird weitgehend unterstützt, hat aber bekannte Schwachstellen, wie z. B. Padding-Orakel-Angriffe. Für neue Systeme wird RSAES-OAEP bevorzugt. Die Polsterfunktion gibt false zurück, wenn die Nachricht zu lang für den gewählten Schlüssel ist.
Base64 Output Helper (Base64Encode):
Chiffretexte sind binäre Daten. Für die Protokollierung, den HTTP-Transport oder die textbasierte Speicherung bietet Base64 eine praktische und standardisierte Darstellung.
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; }
Alles zusammenfügen
Um die Lesbarkeit zu erhalten, ist der vollständige Quellcode der RSA-Klasse hier nicht enthalten. Die vollständige Implementierung befindet sich in der Bibliothek RSA.mqh, die am Ende der Seite zum Download bereitsteht. Die Bibliothek enthält alle in diesem Artikel besprochenen Komponenten sowie ein kommentiertes Beispiel, das die Verwendung der Klasse demonstriert. Es kann für eine schnelle Integration verwendet oder erweitert und verändert werden, sobald die interne Funktionsweise verstanden wurde. Falls Fehler auftreten oder ein Teil der Umsetzung geklärt werden muss, sind die Leser aufgefordert, unten einen Kommentar zu hinterlassen.
Verwendungsbeispiele
Rohe RSA-Implementierung:
Dieses einfache Beispiel zeigt, wie die RSA-Verschlüsselung implementiert werden könnte. Erstellen wir zunächst eine Instanz der RSA-Klasse und bereiten dann unsere Nachricht (die Daten, die wir verschlüsseln möchten) als uchar[] vor, wobei jedes Element ein Byte ist. Schließlich rufen wir EncryptPKCS1v15 (plain, cipher) auf, um die Verschlüsselung durchzuführen. Das Ergebnis ist ein char-Array und wird in cipherData[] gespeichert:
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!"); } }
Verwendung von RSA + AES in einem echten Projekt:

Der Arbeitsablauf bei der hybriden Verschlüsselung folgt im Allgemeinen diesen Schritten:
- Wir erzeugen einen zufälligen AES-Sitzungsschlüssel (z. B. 128 oder 256 Bit),
- verschlüsseln diesen AES-Schlüssel mit dem öffentlichen RSA-Schlüssel – nur der Besitzer des entsprechenden privaten Schlüssels kann sie später entschlüsseln –,
- verschlüsseln die eigentlichen Nutzdaten mit AES – einer schnellen, symmetrischen Verschlüsselung, die sich ideal für große Nachrichten oder Dateien eignet – und
- übertragen beide Komponenten gemeinsam:
- die AES-verschlüsselten Daten (schnell und kompakt) und
- den RSA-verschlüsselte AES-Schlüssel (klein, aber sicher).
Im Zusammenhang mit MQL5 ermöglicht diese Strategie Entwicklern, die Kommunikation zwischen Expert Advisors, Indikatoren und externen Servern zu sichern, auch wenn sie Standard-HTTP- oder Socket-Verbindungen verwenden. Die in diesem Artikel implementierte RSA-Klasse kann zur Verschlüsselung des AES-Schlüssels verwendet werden, während die integrierten MQL5-Funktionen CryptEncode() und CryptDecode() die AES-Verschlüsselung und -Entschlüsselung der eigentlichen Nachricht übernehmen.
Dies ermöglicht eine vollständig eigenständige Sicherheitsschicht innerhalb von MetaTrader 5, wodurch eine leichtgewichtige Version von „HTTPS über HTTP“ entsteht. Sie kann zum Schutz sensibler Daten wie Handelsbefehle, Authentifizierungsdaten oder Konfigurationsnachrichten verwendet werden, ohne auf externe Verschlüsselungsbibliotheken oder DLLs angewiesen zu sein.
Schritt 1 – der Server generiert den öffentlichen RSA-Schlüssel:
Wir verwenden Python, Java, OpenSSL oder eine beliebige Backend-Umgebung, um den Modulo (Hexadezimalzeichenfolge), den Exponenten (normalerweise 65537) und den privaten Schlüssel (der auf Ihrer Serverseite gespeichert wird) zu erzeugen.
Die EA wird nur die öffentlichen Werte verwenden. Geben Sie den privaten Schlüssel nicht an die Client-Seite weiter.
modulusHex = "A1B2C3…"; exponent = 65537;
Schritt 2 – der EA erstellt eine RSA-Instanz und lädt den Schlüssel:
#include <RSA.mqh>
MQL5_RSA rsa;
rsa.Init(modulusHex, exponent); Schritt 3 – der EA bereitet eine Anforderungsnachricht vor:
Ein Beispiel für eine Nutzlast kann eine Login-Anfrage sein, die Folgendes enthält: EA-ID, Kontonummer und Zeitstempel. Erstellen Sie einen geeigneten JSON-String für Ihre Anfrage. Sie können eine beliebige Bibliothek eines Drittanbieters verwenden, um den JSON-String zu erstellen. Der folgende Ausschnitt zeigt, wie die erwartete JSON-Zeichenfolge aussehen sollte:
string json = "{\"cmd\":\"login\"," "\"account\":" + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) + "," "\"ts\":" + IntegerToString((int)TimeCurrent()) + "," "}"; uchar plain[]; StringToCharArray(json, plain, 0, StringLen(json));
Schritt 4 – der EA verschlüsselt den AES-Sitzungsschlüssel mit RSA:
Der AES-Schlüssel, der für den Rest der Sitzung verwendet wird:
uchar aesKey[]; GenerateAESKey(aesKey); // 16 random bytes (AES-128) uchar encryptedAes[]; rsa.EncryptPKCS1v15(aesKey, encryptedAes); string encryptedAesBase64 = rsa.Base64Encode(encryptedAes);Schritt 5 – der EA verschlüsselt die eigentlichen Daten mit AES:
uchar encryptedPayload[]; CryptEncode(CRYPT_AES128, plain, aesKey, encryptedPayload); string payloadBase64 = CryptBase64Encode(encryptedPayload);
Schritt 6 – der EA sendet alles an den Server:
string body = "{\"key\":\"" + encryptedAesBase64 + "\"," + "\"data\":\"" + payloadBase64 + "\"}"; string result; char headers[]; int status = WebRequest("POST", url, headers, 5000, body, result);
Schritt 7 – serverseitige Entschlüsselung (konzeptionell):
Führen Sie auf dem Server zunächst eine base64-Dekodierung für den RSA-verschlüsselten AES-Schlüssel durch und entschlüsseln Sie dann den AES-Schlüssel mit dem privaten RSA-Schlüssel. Sobald der AES-Schlüssel wiederhergestellt ist, kann er zur Entschlüsselung der Nutzdaten verwendet werden. Da der AES-Schlüssel für jede Sitzung nach dem Zufallsprinzip erzeugt wird, bleibt jede Nachricht eindeutig und sicher, auch wenn ein Angreifer den Datenverkehr abfängt. Mit diesem Mechanismus wird effektiv eine sichere Sitzung aufgebaut, die einem vereinfachten HTTPS-Tunnel ähnelt.
Schritt 8 – Die Client-Seite entschlüsselt die Server-Antwort:
uchar responseCipher[]; CryptBase64Decode(result, responseCipher); uchar responsePlain[]; CryptDecode(CRYPT_AES128, responseCipher, aesKey, responsePlain); string serverReply = CharArrayToString(responsePlain); Print("Server replied: ", serverReply);Wenn Sie diese Schritte befolgen, kann die Kryptographie direkt in die MetaTrader 5-Umgebung implementiert werden, was zu einer zuverlässigen und vollständig portablen Lösung für Endnutzer führt.
Schlussfolgerung
In diesem Artikel wurde eine vollständige und funktionale RSA-Verschlüsselungsbibliothek vorgestellt, die komplett in MQL5 geschrieben wurde. Sie zeigt, dass selbst komplexe mathematische Algorithmen direkt in der MetaTrader 5-Umgebung ohne externe Abhängigkeiten implementiert werden können. Durch diese Arbeit haben wir gezeigt, dass MQL5 nicht auf die Automatisierung des Handels beschränkt ist, sondern auch fortgeschrittene Rechenaufgaben wie modulare Arithmetik und Kryptographie mit öffentlichen Schlüsseln durchführen kann.
Mit dieser Grundlage verfügen Entwickler nun über ein praktisches Toolkit zum Aufbau sicherer Messaging-Systeme, zum Schutz von Konfigurationsdateien und zur Unterstützung von sicherem Schlüsselaustausch und verschlüsselter Kommunikation direkt in MetaTrader 5. Dadurch können Expert Advisors und Indikatoren sicher mit Remote-Diensten kommunizieren, Datenquellen authentifizieren und verschlüsselte Handelsbefehle austauschen, ohne sensible Informationen im Netzwerk preiszugeben.
Die Implementierung unterstreicht auch ein wichtiges Konzept: Kryptographie muss nicht auf externe DLLs oder Bibliotheken von Drittanbietern angewiesen sein, um effektiv zu sein. Wenn Algorithmen nativ in MQL5 implementiert werden, bleibt der Code transparent, portabel und kann vom Entwickler vollständig kontrolliert werden. Dies gewährleistet sowohl die Überprüfbarkeit als auch die Einhaltung der Sicherheitsrichtlinien von MetaQuotes, was insbesondere bei der Entwicklung von kommerziellen Handelsinstrumenten, die über den Markt vertrieben werden, von Bedeutung ist.
Für Entwickler, die an einer Erweiterung dieser Arbeit interessiert sind, gibt es mehrere Möglichkeiten. Die RSA-Bibliothek kann mit digitalen Signaturen kombiniert werden, um die Authentizität und Integrität von Nachrichten zu überprüfen, oder mit modernen Auffüllverfahren wie OAEP erweitert werden, um den Widerstand gegen Kryptoanalyse zu erhöhen. Ebenso kann die Integration von AES in authentifizierte Modi wie GCM oder CBC-HMAC sowohl Verschlüsselung als auch Manipulationserkennung in einem einzigen Schritt bieten. Solche Ergänzungen würden die Implementierung näher an moderne Standards wie TLS und PGP heranführen, und zwar vollständig innerhalb der MQL5-Umgebung.
Aus praktischer Sicht ermutigt dieser Artikel die Entwickler, MQL5 nicht nur als eine Skriptsprache für den Handel zu betrachten, sondern als eine vollwertige Programmierumgebung, in der Sicherheit, Netzwerke und Berechnungen nebeneinander bestehen. Durch die Kombination von mathematischer Präzision und sorgfältigem Design können MQL5-Entwickler sichere, in sich geschlossene Systeme erstellen, die zuverlässig mit externen APIs, Brokern oder Cloud-Diensten kommunizieren können.
Zusammenfassend kann gesagt werden, dass Kryptographie in MQL5 sowohl möglich als auch leistungsstark ist. Entwickler haben jetzt die Möglichkeit, mit sicheren Kommunikationsmechanismen zu experimentieren, sie zu erweitern und sie direkt in ihre Projekte zu integrieren. In dem Maße, in dem sich das Handelsökosystem hin zu stärker vernetzten und datengesteuerten Architekturen entwickelt, wird die Fähigkeit, Verschlüsselung und Authentifizierung innerhalb des Terminals zu handhaben, immer wertvoller. Auf dieser Grundlage kann jeder Entwickler sinnvolle Schritte unternehmen, um sicherere, intelligentere und widerstandsfähigere Handelssysteme zu entwickeln.
Autoren und Programmierer
- Vahid Irak
- Siavash Nourmohammadi
Referenzen und weiterführende Literatur
Für Leser, die an den mathematischen Grundlagen und modernen Anwendungen der RSA- und AES-Verschlüsselung interessiert sind, bieten die folgenden Referenzen zuverlässige, ausführliche Erklärungen:
- Wikipedia – PKCS #1: RSA Cryptography Standard
Erläutert den offiziellen RSA-Standard, einschließlich Padding-Schemata wie PKCS#1 v1.5 und OAEP, die für Verschlüsselung und Signaturen verwendet werden.
Die offizielle MQL5-Dokumentation für CryptEncode(), CryptDecode() und andere integrierte Funktionen für AES und Hashing. - Paar, C. & Pelzl, J. (2024). Understanding Cryptography – A Textbook for Students and Practitioners, Chapter 7: The RSA Cryptosystem. Springer. Link
- Mollin, R. A. (2023). RSA and Public-Key Cryptography. Routledge. Link
- National Institute of Standards and Technology. FIPS 197: Advanced Encryption Standard (AES). 2001 (aktualisiert). Link
- Tuo, Z. (2023). “A Comparative Analysis of AES and RSA Algorithms and Their Integrated Application.” Theoretical and Natural Science, Vol 25, pp. 28-35. Link
- “RSA-AES Hybrid Encryption: Combining the Strengths of Symmetric & Asymmetric Algorithms.” IJRAR, 2023. Link
- Ganesh, R., Khan, A. R., et al. (2025). “A Panoramic Survey of the Advanced Encryption Standard (AES)”. International Journal of Information Security. Link
- Einfach erklärte Funktionsweise der RSA-Nachrichtenverschlüsselung/-entschlüsselung (Englisch)
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20273
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Die Grenzen des maschinellen Lernens überwinden (Teil 9): Korrelationsbasierte Lernen von Merkmalen im selbstüberwachten Finanzwesen
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Automatisieren von Handelsstrategien in MQL5 (Teil 45): Inverse Fair Value Gap (IFVG)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.