1

I have a string, say "My secret text".

It has been encrypted using MQL4 CryptEncode() function ( where the used method was CRYPT_AES256 ).

The key ( 32 bytes ) was, say, "1234567890ABCDEFGHIJKLMNOPQRSTUV".

MQL4 function doesn't offer input IV possibility, also I don't know whether there was used CBC-mode or something else.

How can I get back "My secret text" using PHP?

( I have to tell, tried
openssl_decrypt(...)
and
mcrypt_decrypt(...),
but nothing success.
)


Update: I got answer from MQ:
The used mode is ECB.
The IV is indeed not used.
So, my code is:

<?php

$hexMessage       = $_POST["enc_data"];   //this is in hex form, I used ArrayToHex before sending
$encryptedMessage = hex2bin($hexMessage);
$encryptionMethod = "AES-256-ECB";  
$secretKey        = "1234567890ABCDEFGHIJKLMNOPQRSTUV";

$decryptedMessage = openssl_decrypt($encryptedMessage, $encryptionMethod, $secretKey);

echo "Decrypted: $decryptedMessage";

?>

No result (even if I used OPENSSL_RAW_DATA as an option).


Update2:
using mcrypt_decrypt(...) it works. But, I'm curious why openssl_decrypt(...) did not work (it gave nothing as a result )?

ZsG
  • 73
  • 1
  • 8
  • 1
    When you try something, you post that code with errors you get, exact input and exact output. Otherwise, the only answer you can get to your question is this one: `$decrypted = openssl_decrypt($data, $method, $key);` which is *almost* the same thing written at php.net. – N.B. Dec 11 '16 at 22:44
  • As I wrote, I have an AES256-encrypted string, and a key. I have no IV (and don't know the mode). How can I get back the original string in PHP? (I think my totally clumsy attempts are pretty irrelevant. I have no bad solution which needs a bit correction. I have no solution at all.) – ZsG Dec 11 '16 at 23:36
  • 1
    As I wrote, this would be so much easier to solve if you could post exact encrypted string, exact secret key and exact expected output. Then at least we can try to decrypt the string. This way.. all you can do is what I wrote, which apparently doesn't help. You can try to decrypt using `AES-256-*` where `*` is the mode, so loop through all available modes. – N.B. Dec 12 '16 at 09:31
  • @N.B. are you interested in such experiment? I can provide you a testing case ( MT4-side encrypted ). The issue I see is, that no one, who indeed does ( or must ) care about cryptographical protection would easy any kind of attacker the search-space, for a naive brute-force attack. So how do we think that we know, what the encrypt-side details were used...? – user3666197 Dec 12 '16 at 09:43
  • It is best not to use mcrypt, it has been abandonware for nearly a decade now. It has therefore been deprecated and will be removed from the core and into PECL in PHP 7.2. It does not support standard PKCS#7 (née PKCS#5) padding, only non-standard null padding that can't even be used with binary data. mcrypt has many outstanding [bugs](https://sourceforge.net/p/mcrypt/bugs/) dating back to 2003. Instead consider using [defuse](https://github.com/defuse/php-encryption) or [RNCryptor](https://github.com/RNCryptor), they provide a complete solution, are being maintained and is correct. – zaph Dec 12 '16 at 13:12
  • I assume the OP wants to achieve secure encryption thus the suggestions. `mcryopt` has been deprecated and ECB mode is not secure, that is a bad and insecure combination. – zaph Dec 12 '16 at 13:33
  • 1
    @zaph: as user3666197 mentioned, I cannot use any other mode than the given one (IV as a prefix seems to be OK). I should write an own encrypt function for it in MQL4. – ZsG Dec 12 '16 at 13:35
  • @ZsG well, if indeed going to spend any time to "extend" MQL4 and if you are under full control of the MT4-side -- **the better if not the best way** would be to connect the MQL4-code to an external, professional, encryptor engine ( may check my other posts on distributed solutions MQL4 / ZeroMQ / as an example how to use external tools in distributed-MQL4 realms ). – user3666197 Dec 12 '16 at 13:42
  • In MQL4 I'm quite well. Can you show me an example for an "external, professional, encryptor engine", which is reachable for my expert? – ZsG Dec 12 '16 at 13:54
  • I've tried with added `OPENSSL_RAW_DATA` as an option, but the result was nothing. The data is in hex form originally, and converted to binary with `hex2bin` before decrypting. – ZsG Dec 12 '16 at 14:39
  • @RyanVincent you might have already realised, that such reference HEX-string ( representation of aKnownSTRING that has been encrypted right by the MetaTrader Terminal 4 [Black-Box]-encryptor ) has been posted some five hours ago ( in the **MCVE-section** trailer of the answer below ). – user3666197 Dec 12 '16 at 15:58

2 Answers2

1

For keen DownVoters:
kindly notice first the given O/P context of use, where there are literally ZERO-options to chose any AES256-configuration options, but still the O/P needs to decipher the "black-box"-produced BLOBs on the php-side. Thank you for re-consideration.

Feel free to improve the solution or present here in written any better solution for the O/P-given-use-case, which will be always warm welcome, won't it?


An MCVE-segment of the practical solution below :
MQL4-side code :

//+------------------------------------------------------------------+
//|                                  __StackOverflow_CryptENCODE.mq4 |
//|                                               msMODs (1987-2016) |
//|                                                       nowhere.no |
//+------------------------------------------------------------------+
#property copyright "msMODs (1987-2016)"
#property link      "nowhere.no"
#property version   "1.00"
#property strict
#property script_show_inputs
extern string  aKnown_OriginalSTRING_asMql4STRING     = "How to decrypt an MT4 / AES256 encrypted string with PHP tools?";
       uchar   aKnown_OriginalSTRING_ucharCONTAINER[];
extern string  aKnown_SecretKEY_asMql4STRING          = "123456789o123456789o12";
       uchar   aKnown_SecretKEY_ucharCONTAINER[32];
       uchar   aCryptoBLOB_ucharCONTAINER[];
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void   OnStart(){
       StringToCharArray( aKnown_OriginalSTRING_asMql4STRING,
                          aKnown_OriginalSTRING_ucharCONTAINER,
                          0,
                          StringLen( aKnown_OriginalSTRING_asMql4STRING )
                          );
       StringToCharArray( aKnown_SecretKEY_asMql4STRING,
                          aKnown_SecretKEY_ucharCONTAINER,
                          0,
                          32
                          );

       int        aFH = FileOpen( "DEMO_OUTPUT.txt", FILE_WRITE | FILE_TXT );
       FileWrite( aFH, "START: GetLastError() == ", GetLastError(), "\n" );
       FileFlush( aFH );

       ResetLastError();

       int nBYTEs = CryptEncode( CRYPT_AES256,                        // a principally unsure ENUM_ ( ref. above )
                                 aKnown_OriginalSTRING_ucharCONTAINER,
                                      aKnown_SecretKEY_ucharCONTAINER,
                                           aCryptoBLOB_ucharCONTAINER
                                 );
       
       if (  nBYTEs > 0 ){
             FileWrite( aFH, StringFormat( "OK.\nMQL4 CryptEncode() has produced [nBYTEs == %d ] bytes.\nMQL4 aCryptoBLOB_asHEX\n== [%s]",
                                            nBYTEs,
                                            show_asHEX( aCryptoBLOB_ucharCONTAINER )
                                            )
                        );
             Comment( "INF:",StringFormat( "OK.\nMQL4 CryptEncode() has produced [nBYTEs == %d ] bytes.\nMQL4 aCryptoBLOB_asHEX\n== [%s]\n\nSTORED IN GlobalVariable()...",
                                            nBYTEs,
                                            show_asHEX( aCryptoBLOB_ucharCONTAINER )
                                            )
                        );
       }                           
       else
             FileWrite( aFH, StringFormat( "ERR: in MQL4 CryptEncode()[ Err == %d ].",
                                            GetLastError()
                                            )
                        );
      FileFlush( aFH );
      FileClose( aFH );               
   }
//+------------------------------------------------------------------+
string show_asHEX( uchar &_ucharCONTAINER_arr[], int count = -1 ){
       string HEX_asPrintableSTRING = "";
       if (  count <  0
          || count >  ArraySize( _ucharCONTAINER_arr )
          )  count =  ArraySize( _ucharCONTAINER_arr );
       for (  int ii = 0; ii <  count; ii++ )
               HEX_asPrintableSTRING += StringFormat( "%.2X", _ucharCONTAINER_arr[ii] );
       return( HEX_asPrintableSTRING );
   }
//+------------------------------------------------------------------+

Asks for user-input interaction: enter image description here

And has produced ( on these default values ) a reference MCVE-output to validate any decipher-trial:

START: GetLastError() == 0
OK.
MQL4 CryptEncode() has produced [nBYTEs == 64 ] bytes.
MQL4 aCryptoBLOB_asHEX
== [1979FE46DB64652067C136F57F0971F20FB5C407CE043AAF972C8AED3DEB6D4260181448FE2FDF69AEA7DD8B33B1484A21935AAFBB649FB95DBB05BBA88E4A31]

Academia / Theoretical Approach :

Reverse scan all the possible configurations of the php-tools of one's choice to find those settings, that correctly reconstruct the MT4-side CryptEncode()-ed aKnown-[Test]-OriginalSTRING, using aKnown-[Test]-SecretKEY back to the matching string.

Being principally unsure about the MT4-side details ( where crypto-engine is due to obvious reasons not an open-source ), one faces the following possible alternatives for the (unknown-choice) on the encryption-process side:

CRYPT_BASE64
CRYPT_AES128
CRYPT_AES256
CRYPT_DES
CRYPT_HASH_SHA1
CRYPT_HASH_SHA256
CRYPT_HASH_MD5

The php-decrypt-side is much "richer" to set an test
~ { OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING }
x { each-method-from-the-choice-below }:

(
    [0] => AES-128-CBC
    [1] => AES-128-CFB
    [2] => AES-128-CFB1
    [3] => AES-128-CFB8
    [4] => AES-128-ECB
    [5] => AES-128-OFB
    [6] => AES-192-CBC
    [7] => AES-192-CFB
    [8] => AES-192-CFB1
    [9] => AES-192-CFB8
    [10] => AES-192-ECB
    [11] => AES-192-OFB
    [12] => AES-256-CBC
    [13] => AES-256-CFB
    [14] => AES-256-CFB1
    [15] => AES-256-CFB8
    [16] => AES-256-ECB
    [17] => AES-256-OFB
    [18] => BF-CBC
    [19] => BF-CFB
    [20] => BF-ECB
    [21] => BF-OFB
    [22] => CAST5-CBC
    [23] => CAST5-CFB
    [24] => CAST5-ECB
    [25] => CAST5-OFB
    [26] => DES-CBC
    [27] => DES-CFB
    [28] => DES-CFB1
    [29] => DES-CFB8
    [30] => DES-ECB
    [31] => DES-EDE
    [32] => DES-EDE-CBC
    [33] => DES-EDE-CFB
    [34] => DES-EDE-OFB
    [35] => DES-EDE3
    [36] => DES-EDE3-CBC
    [37] => DES-EDE3-CFB
    [38] => DES-EDE3-OFB
    [39] => DES-OFB
    [40] => DESX-CBC
    [41] => IDEA-CBC
    [42] => IDEA-CFB
    [43] => IDEA-ECB
    [44] => IDEA-OFB
    [45] => RC2-40-CBC
    [46] => RC2-64-CBC
    [47] => RC2-CBC
    [48] => RC2-CFB
    [49] => RC2-ECB
    [50] => RC2-OFB
    [51] => RC4
    [52] => RC4-40
    [53] => aes-128-cbc
    [54] => aes-128-cfb
    [55] => aes-128-cfb1
    [56] => aes-128-cfb8
    [57] => aes-128-ecb
    [58] => aes-128-ofb
    [59] => aes-192-cbc
    [60] => aes-192-cfb
    [61] => aes-192-cfb1
    [62] => aes-192-cfb8
    [63] => aes-192-ecb
    [64] => aes-192-ofb
    [65] => aes-256-cbc
    [66] => aes-256-cfb
    [67] => aes-256-cfb1
    [68] => aes-256-cfb8
    [69] => aes-256-ecb
    [70] => aes-256-ofb
    [71] => bf-cbc
    [72] => bf-cfb
    [73] => bf-ecb
    [74] => bf-ofb
    [75] => cast5-cbc
    [76] => cast5-cfb
    [77] => cast5-ecb
    [78] => cast5-ofb
    [79] => des-cbc
    [80] => des-cfb
    [81] => des-cfb1
    [82] => des-cfb8
    [83] => des-ecb
    [84] => des-ede
    [85] => des-ede-cbc
    [86] => des-ede-cfb
    [87] => des-ede-ofb
    [88] => des-ede3
    [89] => des-ede3-cbc
    [90] => des-ede3-cfb
    [91] => des-ede3-ofb
    [92] => des-ofb
    [93] => desx-cbc
    [94] => idea-cbc
    [95] => idea-cfb
    [96] => idea-ecb
    [97] => idea-ofb
    [98] => rc2-40-cbc
    [99] => rc2-64-cbc
    [100] => rc2-cbc
    [101] => rc2-cfb
    [102] => rc2-ecb
    [103] => rc2-ofb
    [104] => rc4
    [105] => rc4-40
)

Yes, it may take a long time, but this approach is principally possible.

Anyone interested in doing this may realise and ought verify also any kind of risk of becoming a subject of legal investigation or even legal enforcement task-force countermeasures, as under some local jurisdictions, using this or similar practice may be considered illegal and/or violating IP rights of some other party. ( So, 've just been warned. )


Practical Approach:

Go distributed.

Use php-side to connect and communicate with MetaTrader Terminal 4 ( many ways to do so, incl. production-grade, low-latency frameworks ).

php-side sends anMt4EncryptedBLOB to the hands of an MT4-process

MT4-process calls the matching ( by design, the MetaQuotes' "home-made" - is a matching implementation of the encrypt/decrypt-service pair )
nBytes = CryptDecode( TheSAME_ENUM_CRYPT_METHOD, BLOBarray, KEYarray, RESarray );

Sending the RESarray back to the php-side for any further processing is obvious and useless to note here, but that closes the ring.

And you are done!
( Just do not get confused, that MQL4 string is not in reality a string but a struct and similar suprises, that one will meet on the MT4-side, if going into DLL/API integration details, so be cautious. Still a pretty fun to hack this fast and smart into a rapid-prototyped MVP. )


An MCVE-segment:
MQL4-side code:

//+------------------------------------------------------------------+
//|                                  __StackOverflow_CryptENCODE.mq4 |
//|                                               msMODs (1987-2016) |
//|                                                       nowhere.no |
//+------------------------------------------------------------------+
#property copyright "msMODs (1987-2016)"
#property link      "nowhere.no"
#property version   "1.00"
#property strict
#property script_show_inputs
extern string  aKnown_OriginalSTRING_asMql4STRING     = "How to decrypt an MT4 / AES256 encrypted string with PHP tools?";
       uchar   aKnown_OriginalSTRING_ucharCONTAINER[];
extern string  aKnown_SecretKEY_asMql4STRING          = "123456789o123456789o12";
       uchar   aKnown_SecretKEY_ucharCONTAINER[32];
       uchar   aCryptoBLOB_ucharCONTAINER[];
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void   OnStart(){
       StringToCharArray( aKnown_OriginalSTRING_asMql4STRING,
                          aKnown_OriginalSTRING_ucharCONTAINER,
                          0,
                          StringLen( aKnown_OriginalSTRING_asMql4STRING )
                          );
       StringToCharArray( aKnown_SecretKEY_asMql4STRING,
                          aKnown_SecretKEY_ucharCONTAINER,
                          0,
                          32
                          );

       int        aFH = FileOpen( "DEMO_OUTPUT.txt", FILE_WRITE | FILE_TXT );
       FileWrite( aFH, "START: GetLastError() == ", GetLastError(), "\n" );
       FileFlush( aFH );

       ResetLastError();

       int nBYTEs = CryptEncode( CRYPT_AES256,                        // a principally unsure ENUM_ ( ref. above )
                                 aKnown_OriginalSTRING_ucharCONTAINER,
                                      aKnown_SecretKEY_ucharCONTAINER,
                                           aCryptoBLOB_ucharCONTAINER
                                 );
       
       if (  nBYTEs > 0 ){
             FileWrite( aFH, StringFormat( "OK.\nMQL4 CryptEncode() has produced [nBYTEs == %d ] bytes.\nMQL4 aCryptoBLOB_asHEX\n== [%s]",
                                            nBYTEs,
                                            show_asHEX( aCryptoBLOB_ucharCONTAINER )
                                            )
                        );
             Comment( "INF:",StringFormat( "OK.\nMQL4 CryptEncode() has produced [nBYTEs == %d ] bytes.\nMQL4 aCryptoBLOB_asHEX\n== [%s]\n\nSTORED IN GlobalVariable()...",
                                            nBYTEs,
                                            show_asHEX( aCryptoBLOB_ucharCONTAINER )
                                            )
                        );
       }                           
       else
             FileWrite( aFH, StringFormat( "ERR: in MQL4 CryptEncode()[ Err == %d ].",
                                            GetLastError()
                                            )
                        );
      FileFlush( aFH );
      FileClose( aFH );               
   }
//+------------------------------------------------------------------+
string show_asHEX( uchar &_ucharCONTAINER_arr[], int count = -1 ){
       string HEX_asPrintableSTRING = "";
       if (  count <  0
          || count >  ArraySize( _ucharCONTAINER_arr )
          )  count =  ArraySize( _ucharCONTAINER_arr );
       for (  int ii = 0; ii <  count; ii++ )
               HEX_asPrintableSTRING += StringFormat( "%.2X", _ucharCONTAINER_arr[ii] );
       return( HEX_asPrintableSTRING );
   }
//+------------------------------------------------------------------+

Asks for user-input interaction: enter image description here

And has produced ( on these default values ) a reference MCVE-output to validate any decipher-trial:

START: GetLastError() == 0
OK.
MQL4 CryptEncode() has produced [nBYTEs == 64 ] bytes.
MQL4 aCryptoBLOB_asHEX
== [1979FE46DB64652067C136F57F0971F20FB5C407CE043AAF972C8AED3DEB6D4260181448FE2FDF69AEA7DD8B33B1484A21935AAFBB649FB95DBB05BBA88E4A31]
user3666197
  • 1
  • 6
  • 50
  • 92
  • It was run in the terminal 3 hour ago. The reference MCVE-output was copy/paste. What is the problem-indication? Could you kindly post the content of the output file from <~TERMINAL_DIR~> \ MQL4 \ Files \ ? – user3666197 Dec 12 '16 at 13:48
  • The answer includes over 100 encryption options that are non-responsive to the OP provided CRYPT_AES256. – zaph Dec 12 '16 at 13:48
  • Negative, Sir. The OP is in a principally unsure situation, what exact cryptographic method was used on a source-side. Any brute-force approach ( **which one might have noticed was not recommended to undergo** ) simply needs to cover the whole search-space ( available for the decryptor side family of tools ) to become able to come to any conclusions about ( principally ) **hidden sub-options ( that have been used in the Black-Box encryptor )** when providing the available BLOB ( from a known plain-text original string ) to decrypt on other tool-set, than was used in the Black-Box encryptor. – user3666197 Dec 12 '16 at 13:53
0

Here are two different ways to decrypt:

$decryptedMessage = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $secretKey, $encryptedMessage, MCRYPT_MODE_ECB);
$decryptedMessage = openssl_decrypt($encryptedMessage, "AES-256-ECB", $secretKey, OPENSSL_ZERO_PADDING|OPENSSL_RAW_DATA);
hkn
  • 1,453
  • 19
  • 21