数据解密和解压缩:CryptDecode

为了执行数据解密和解压缩操作,MQL5 提供了 CryptDecode 函数。

CryptDecode 函数使用指定的方法将 data 数组的数据进行逆向转换到接收的 result 数组中。

int CryptDecode(ENUM_CRYPT_METHOD method, const uchar &data[], const uchar &key[], uchar &result[])

请注意,由 CryptEncode 函数等执行的哈希和计算是单向转换:无法从哈希中恢复原始数据。

函数返回放置在目标数组中的字节数,如果出错则返回 0。错误代码将添加到 _LastError 中。例如,如果我们尝试解码哈希(method 等于 CRYPT_HASH 常量之一),则可能是 INVALID_PARAMETER (4003);如果解密密钥长度不足或缺失,则可能是 INVALID_ARRAY (4006)。

如果密钥不正确(与加密时使用的密钥不同),我们将得到乱码而不是编码的源数据,但错误代码为零。这是函数的正常行为。

我们使用相同的脚本 CryptDecode.mq5 来检查 CryptDecode 的工作。

在输入参数中,你可以指定要转换的文本或文件。文本始终隐含为 Base64 编码,因为所有编码后的数据都是二进制格式,并且 input 参数中不支持。转换方法从 Method 列表中选择。

input string Text// Text (base64, or empty to process File)
input string File = "MQL5Book/clock10.htm.BASE64";
input ENUM_CRYPT_METHOD_EXT Method = _CRYPT_BASE64;

如果 GenerateKey 包含 DUMMY_KEY_CUSTOM 选项,则加密方法需要一个密钥,该密钥可以在 CustomKey 字段中指定为字符串。你还可以从 DUMMY_KEY_LENGTH 枚举(与 CryptEncode.mq5 脚本中的相同)生成所需长度的演示密钥。

input DUMMY_KEY_LENGTH GenerateKey = DUMMY_KEY_CUSTOM// GenerateKey (length, or from CustomKey)
input string CustomKey = "My top secret key is very strong"
input bool DisableCRCinZIP = false;

GenerateKeyCustomKey 中,你应选择与启动 CryptEncode.mq5 时相同的值。

OnStart 中的算法首先描述所需的数组,并从字符串获取密钥或通过简单生成(仅用于演示,应使用专用软件或算法生成可工作的抗加密密钥)。

void OnStart()
{
   ENUM_CRYPT_METHOD method = 0;
   int methods[];
   uchar key[] = {};        // default empty key suitable for zip and base64
   uchar data[], result[];
   uchar zip[], opt[] = {1000};
   
   if(GenerateKey == DUMMY_KEY_CUSTOM)
   {
      if(StringLen(CustomKey))
      {
         PRTF(CustomKey);
         StringToCharArray(CustomKeykey0, -1, CP_UTF8);
         ArrayResize(keyArraySize(key) - 1);
      }
   }
   else if(GenerateKey != DUMMY_KEY_0)
   {
      ArrayResize(keyGenerateKey);
      for(int i = 0i < GenerateKey; ++ikey[i] = (uchar)i;
   }
   
   if(ArraySize(key))
   {   
      Print("Key (bytes):");
      ByteArrayPrint(key);
   }
   else
   {
      Print("Key is not provided");
   }

接下来,我们读取文件内容或从 Text 字段解码 Base64(取决于填充了哪个字段)以获取要处理的数据。

   method = (ENUM_CRYPT_METHOD)Method;
   Print("- "EnumToString(method), ", key required: "KEY_REQUIRED(method));
   if(StringLen(Text))
   {
      if(method != CRYPT_BASE64)
      {
         // since all methods except Base64 produce binary results,
         // they are additionally converted to CryptEncode.mq5 using Base64 to text,
         // so here we want to recover binary data from text input
         // before decryption
         uchar base64[];
         const uchar dummy[] = {};
         PRTF(Text);
         PRTF(StringToCharArray(Textbase640, -1, CP_UTF8));
         ArrayResize(base64ArraySize(base64) - 1);
         Print("Text (bytes):");
         ByteArrayPrint(base64);
         if(!PRTF(CryptDecode(CRYPT_BASE64base64dummydata)))
         {
            return// error
         }
         
         Print("Raw data to decipher (after de-base64):");
         ByteArrayPrint(data);
      }
      else
      {
         PRTF(StringToCharArray(Textdata0StringLen(Text), CP_UTF8));
         ArrayResize(dataArraySize(data) - 1);
      }
   }
   else if(StringLen(File))
   {
      PRTF(File);
      if(PRTF(FileLoad(Filedata)) <= 0)
      {
         return// error
      }
   }

如果用户尝试从哈希中恢复数据,我们将显示警告。

   if(IS_HASH(method))
   {
      Print("WARNING: hashes can not be used to restore data! CryptDecode will fail.");
   }

最后,我们直接执行解密或解压缩(解包)。对于文本,结果只被记录下来。对于文件,我们在名称后添加扩展名 ".dec" 并写入一个新文件:可以将其与原始文件(使用 CryptEncode.mq5 脚本处理的文件)进行比较。

   ResetLastError();
   if(PRTF(CryptDecode(methoddatakeyresult)))
   {
      if(StringLen(Text))
      {
         Print("Text restored:");
         Print(CharArrayToString(result0WHOLE_ARRAYCP_UTF8));
      }
      else // File
      {
         const string filename = File + ".dec";
         if(PRTF(FileSave(filenameresult)))
         {
            Print("File saved: "filename);
         }
      }
   }

如果你使用默认设置运行该脚本,它将尝试解码文件 MQL5Book/clock10.htm.BASE64。假设这是在上一节的实验中创建的,因此该过程应该会成功。

- CRYPT_BASE64, key required: false
File=MQL5Book/clock10.htm.BASE64 / ok
FileLoad(File,data)=1320 / ok
CryptDecode(method,data,key,result)=988 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.BASE64.dec

获得的 clock10.htm.BASE64.dec 文件与原始的 clock10.htm 完全相同。如果你解密扩展名为 AES128、AES256 或 DES 的文件,并且指定了与加密时使用的相同密钥,则应该发生同样的情况。

为清晰起见,我们检查文本的解密。以前,使用 AES128 方法加密一个已知短语会产生一个二进制文件,为方便起见,该二进制文件被转换为以下 Base64 字符串。

AQuvVCoSy1szaN8Owy8tQxl9rIrRj9hOqK7KgYYGh9E=

我们将其输入到 Text 字段,并在 Method 下拉列表中选择 AES128。我们将看到以下日志。

CustomKey=My top secret key is very strong / ok

Key (bytes):

[00] 4D | 79 | 20 | 74 | 6F | 70 | 20 | 73 | 65 | 63 | 72 | 65 | 74 | 20 | 6B | 65 |

[16] 79 | 20 | 69 | 73 | 20 | 76 | 65 | 72 | 79 | 20 | 73 | 74 | 72 | 6F | 6E | 67 |

- CRYPT_AES128, key required: true

Text=AQuvVCoSy1szaN8Owy8tQxl9rIrRj9hOqK7KgYYGh9E= / ok

StringToCharArray(Text,base64,0,-1,CP_UTF8)=44 / ok

Text (bytes):

[00] 41 | 51 | 75 | 76 | 56 | 43 | 6F | 53 | 79 | 31 | 73 | 7A | 61 | 4E | 38 | 4F |

[16] 77 | 79 | 38 | 74 | 51 | 78 | 6C | 39 | 72 | 49 | 72 | 52 | 6A | 39 | 68 | 4F |

[32] 71 | 4B | 37 | 4B | 67 | 59 | 59 | 47 | 68 | 39 | 45 | 3D |

CryptDecode(CRYPT_BASE64,base64,dummy,data)=32 / ok

Raw data to decipher (after de-base64):

[00] 01 | 0B | AF | 54 | 2A | 12 | CB | 5B | 33 | 68 | DF | 0E | C3 | 2F | 2D | 43 |

[16] 19 | 7D | AC | 8A | D1 | 8F | D8 | 4E | A8 | AE | CA | 81 | 86 | 06 | 87 | D1 |

CryptDecode(method,data,key,result)=32 / ok

Text restored:

Let's encrypt this message

信息已成功解密。

如果使用相同的输入文本,选择生成任意密钥(尽管长度足够),得到的将是胡言乱语,而不是信息。

Key (bytes):

[00] 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F |

- CRYPT_AES128, key required: true

Text=AQuvVCoSy1szaN8Owy8tQxl9rIrRj9hOqK7KgYYGh9E= / ok

StringToCharArray(Text,base64,0,-1,CP_UTF8)=44 / ok

Text (bytes):

[00] 41 | 51 | 75 | 76 | 56 | 43 | 6F | 53 | 79 | 31 | 73 | 7A | 61 | 4E | 38 | 4F |

[16] 77 | 79 | 38 | 74 | 51 | 78 | 6C | 39 | 72 | 49 | 72 | 52 | 6A | 39 | 68 | 4F |

[32] 71 | 4B | 37 | 4B | 67 | 59 | 59 | 47 | 68 | 39 | 45 | 3D |

CryptDecode(CRYPT_BASE64,base64,dummy,data)=32 / ok

Raw data to decipher (after de-base64):

[00] 01 | 0B | AF | 54 | 2A | 12 | CB | 5B | 33 | 68 | DF | 0E | C3 | 2F | 2D | 43 |

[16] 19 | 7D | AC | 8A | D1 | 8F | D8 | 4E | A8 | AE | CA | 81 | 86 | 06 | 87 | D1 |

CryptDecode(method,data,key,result)=32 / ok

Text restored:

??? ?L?? ??J Q+?]?v?9?????n?N?Ű

如果你混淆了加密方法,程序的行为将类似。

选择“反哈希”方法是没有意义的:INVALID_PARAMETER (4003)。

- CRYPT_HASH_MD5, key required: false
File=MQL5Book/clock10.htm.MD5 / ok
FileLoad(File,data)=16 / ok
WARNING: hashes can not be used to restore data! CryptDecode will fail.
CryptDecode(method,data,key,result)=0 / INVALID_PARAMETER(4003)

尝试解包 (CRYPT_ARCH_ZIP) 并非压缩的 "deflate" 数据块的内容将导致 INTERNAL_ERROR (4001)。如果你为一个没有 CRC 的“存档”启用了跳过 CRC 选项,或者相反,在没有该选项的情况下解压缩数据,尽管打包时使用了该选项,也可能得到同样的错误。

该网站使用cookies。了解有关我们Cookies政策的更多信息。