Giải mã và giải nén dữ liệu: CryptDecode
Để thực hiện các thao tác giải mã và giải nén dữ liệu, MQL5 cung cấp hàm CryptDecode
.
Hàm CryptDecode
thực hiện một phép biến đổi ngược của mảng data
sang mảng nhận result
bằng phương pháp được chỉ định.
int CryptDecode(ENUM_CRYPT_METHOD method, const uchar &data[], const uchar &key[], uchar &result[])
Vui lòng lưu ý rằng việc thu thập các tổng băm, đặc biệt được thực hiện bởi hàm CryptEncode
, là một phép biến đổi một chiều: không thể khôi phục dữ liệu gốc từ các băm.
Hàm trả về số byte được đặt trong mảng đích hoặc 0 nếu có lỗi. Mã lỗi sẽ được thêm vào _LastError
. Ví dụ, điều này có thể là INVALID_PARAMETER (4003) nếu chúng ta cố gắng giải mã băm (method
bằng một trong các hằng số CRYPT_HASH) hoặc INVALID_ARRAY (4006) nếu khóa giải mã không đủ dài hoặc bị thiếu.
Nếu khóa không chính xác (khác với khóa được sử dụng trong mã hóa), chúng ta sẽ nhận được kết quả vô nghĩa thay vì dữ liệu nguồn đã mã hóa nhưng mã lỗi là zero. Đây là hành vi bình thường của hàm.
Hãy kiểm tra hoạt động của CryptDecode
bằng cùng script CryptDecode.mq5
.
Trong các tham số đầu vào, bạn có thể chỉ định văn bản hoặc tệp cần chuyển đổi. Văn bản luôn được ngụ ý trong mã hóa Base64
vì tất cả dữ liệu đã mã hóa đều ở định dạng nhị phân và không được hỗ trợ trong các tham số input
. Phương pháp chuyển đổi được chọn từ danh sách Method
.
input string Text; // Văn bản (base64, hoặc để trống để xử lý File)
input string File = "MQL5Book/clock10.htm.BASE64";
input ENUM_CRYPT_METHOD_EXT Method = _CRYPT_BASE64;
2
3
Các phương pháp mã hóa yêu cầu một khóa, có thể được chỉ định dưới dạng chuỗi trong trường CustomKey
nếu GenerateKey
chứa tùy chọn DUMMY_KEY_CUSTOM. Bạn cũng có thể tạo một khóa demo có độ dài yêu cầu từ bảng liệt kê DUMMY_KEY_LENGTH (nó giống như trong script CryptEncode.mq5
).
input DUMMY_KEY_LENGTH GenerateKey = DUMMY_KEY_CUSTOM; // GenerateKey (độ dài, hoặc từ CustomKey)
input string CustomKey = "My top secret key is very strong";
input bool DisableCRCinZIP = false;
2
3
Trong GenerateKey
và CustomKey
, bạn nên chọn cùng giá trị như khi chạy CryptEncode.mq5
.
Thuật toán trong OnStart
bắt đầu với việc mô tả các mảng cần thiết và lấy khóa từ chuỗi hoặc bằng cách tạo đơn giản (chỉ để demo, sử dụng phần mềm chuyên dụng hoặc thuật toán để tạo khóa chống mã hóa hoạt động).
void OnStart()
{
ENUM_CRYPT_METHOD method = 0;
int methods[];
uchar key[] = {}; // khóa trống mặc định phù hợp cho zip và base64
uchar data[], result[];
uchar zip[], opt[] = {1, 0, 0, 0};
if(GenerateKey == DUMMY_KEY_CUSTOM)
{
if(StringLen(CustomKey))
{
PRTF(CustomKey);
StringToCharArray(CustomKey, key, 0, -1, CP_UTF8);
ArrayResize(key, ArraySize(key) - 1);
}
}
else if(GenerateKey != DUMMY_KEY_0)
{
ArrayResize(key, GenerateKey);
for(int i = 0; i < GenerateKey; ++i) key[i] = (uchar)i;
}
if(ArraySize(key))
{
Print("Key (bytes):");
ByteArrayPrint(key);
}
else
{
Print("Key is not provided");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Tiếp theo, chúng ta đọc nội dung của tệp hoặc giải mã Base64
từ trường Text
(tùy thuộc vào cái nào được điền) để lấy dữ liệu cần xử lý.
method = (ENUM_CRYPT_METHOD)Method;
Print("- ", EnumToString(method), ", key required: ", KEY_REQUIRED(method));
if(StringLen(Text))
{
if(method != CRYPT_BASE64)
{
// vì tất cả các phương pháp trừ Base64 tạo ra kết quả nhị phân,
// chúng được chuyển đổi thêm trong CryptEncode.mq5 bằng Base64 thành văn bản,
// nên ở đây chúng ta muốn khôi phục dữ liệu nhị phân từ đầu vào văn bản
// trước khi giải mã
uchar base64[];
const uchar dummy[] = {};
PRTF(Text);
PRTF(StringToCharArray(Text, base64, 0, -1, CP_UTF8));
ArrayResize(base64, ArraySize(base64) - 1);
Print("Text (bytes):");
ByteArrayPrint(base64);
if(!PRTF(CryptDecode(CRYPT_BASE64, base64, dummy, data)))
{
return; // lỗi
}
Print("Raw data to decipher (after de-base64):");
ByteArrayPrint(data);
}
else
{
PRTF(StringToCharArray(Text, data, 0, StringLen(Text), CP_UTF8));
ArrayResize(data, ArraySize(data) - 1);
}
}
else if(StringLen(File))
{
PRTF(File);
if(PRTF(FileLoad(File, data)) <= 0)
{
return; // lỗi
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Nếu người dùng cố gắng khôi phục dữ liệu từ băm, chúng ta sẽ hiển thị cảnh báo.
if(IS_HASH(method))
{
Print("WARNING: hashes can not be used to restore data! CryptDecode will fail.");
}
2
3
4
Cuối cùng, chúng ta thực hiện giải mã hoặc giải nén (giải gói) trực tiếp. Trong trường hợp văn bản, kết quả chỉ được ghi lại. Trong trường hợp tệp, chúng ta thêm phần mở rộng ".dec" vào tên và ghi một tệp mới: nó có thể được so sánh với tệp gốc, đã được xử lý bằng script CryptEncode.mq5
.
ResetLastError();
if(PRTF(CryptDecode(method, data, key, result)))
{
if(StringLen(Text))
{
Print("Text restored:");
Print(CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8));
}
else // File
{
const string filename = File + ".dec";
if(PRTF(FileSave(filename, result)))
{
Print("File saved: ", filename);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Nếu bạn chạy script với cài đặt mặc định, nó sẽ cố gắng giải mã tệp MQL5Book/clock10.htm.BASE64
. Giả định rằng tệp này đã được tạo trong các thử nghiệm ở phần trước, quá trình sẽ thành công.
- 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
2
3
4
5
6
Tệp thu được clock10.htm.BASE64.dec
hoàn toàn giống với tệp gốc clock10.htm
. Điều tương tự sẽ xảy ra nếu bạn giải mã các tệp có phần mở rộng AES128, AES256 hoặc DES, với điều kiện bạn chỉ định cùng khóa như khi mã hóa.
Để rõ ràng, hãy kiểm tra việc giải mã văn bản. Trước đây, việc mã hóa một cụm từ đã biết bằng phương pháp AES128 đã tạo ra một nhị phân được chuyển đổi thành chuỗi Base64
sau đây để tiện lợi.
AQuvVCoSy1szaN8Owy8tQxl9rIrRj9hOqK7KgYYGh9E=
Hãy nhập nó vào trường Text
và chọn AES128 trong danh sách thả xuống Method
. Chúng ta sẽ thấy các nhật ký sau.
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Tin nhắn đã được giải mã thành công.
Nếu với cùng văn bản đầu vào, bạn chọn tạo một khóa ngẫu nhiên (dù có độ dài đủ), bạn sẽ nhận được kết quả vô nghĩa thay vì tin nhắn.
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?Ű
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Chương trình sẽ hoạt động tương tự nếu bạn nhầm phương pháp mã hóa.
Việc chọn các phương pháp "giải băm" không có ý nghĩa: 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)
2
3
4
5
Việc cố gắng giải nén (CRYPT_ARCH_ZIP) một thứ không phải là khối "deflate" nén sẽ dẫn đến INTERNAL_ERROR (4001). Lỗi tương tự có thể xảy ra nếu bạn bật tùy chọn bỏ qua CRC cho "kho lưu trữ" không có nó, hoặc ngược lại, giải nén dữ liệu mà không có tùy chọn, mặc dù việc đóng gói đã được thực hiện với nó.