Mã hóa, băm và đóng gói dữ liệu: CryptEncode
Hàm MQL5 chịu trách nhiệm mã hóa, băm và nén dữ liệu là CryptEncode
. Nó chuyển đổi dữ liệu của mảng nguồn được truyền data
thành mảng đích result
bằng phương pháp được chỉ định.
int CryptEncode(ENUM_CRYPT_METHOD method, const uchar &data[], const uchar &key[], uchar &result[])
Các phương pháp mã hóa cũng yêu cầu truyền một mảng byte key
với khóa riêng (bí mật): độ dài của nó phụ thuộc vào phương pháp cụ thể và được chỉ định trong bảng phương pháp ENUM_CRYPT_METHOD ở phần trước. Nếu kích thước của mảng key
lớn hơn, chỉ các byte đầu tiên trong số lượng cần thiết sẽ được sử dụng cho khóa.
Không cần khóa cho băm hoặc nén, nhưng có một lưu ý đối với CRYPT_ARCH_ZIP. Thực tế là việc triển khai thuật toán "deflate" tích hợp trong terminal thêm một số byte vào dữ liệu kết quả để kiểm soát tính toàn vẹn: 2 byte đầu chứa cài đặt của thuật toán "deflate", và 4 byte ở cuối chứa tổng kiểm Adler32. Do tính năng này, container được đóng gói kết quả khác với container được tạo bởi các kho lưu trữ ZIP cho mỗi phần tử riêng lẻ của kho lưu trữ (tiêu chuẩn ZIP lưu trữ CRC32, tương tự về ý nghĩa, trong các tiêu đề của nó). Vì vậy, để có thể tạo và đọc các kho lưu trữ ZIP tương thích dựa trên dữ liệu được đóng gói bởi hàm CryptEncode
, MQL5 cho phép vô hiệu hóa kiểm tra tính toàn vẹn của riêng nó và việc tạo các byte bổ sung bằng cách sử dụng một giá trị đặc biệt trong mảng key
.
uchar key[] = {1, 0, 0, 0};
CryptEncode(CRYPT_ARCH_ZIP, data, key, result);
2
Bất kỳ khóa nào có độ dài ít nhất 4 byte đều có thể được sử dụng. Mảng result
thu được có thể được bổ sung tiêu đề theo định dạng ZIP tiêu chuẩn (câu hỏi này nằm ngoài phạm vi của sách) để tạo một kho lưu trữ có thể truy cập bởi các chương trình khác.
Hàm trả về số byte được đặt trong mảng đích hoặc 0 nếu có lỗi. Mã lỗi, như thường lệ, sẽ được lưu trong _LastError
.
Hãy kiểm tra hiệu suất của hàm bằng script CryptEncode.mq5
. Nó cho phép người dùng nhập văn bản (Text
) hoặc chỉ định một tệp (File
) để xử lý. Để sử dụng tệp, bạn cần xóa trường Text
.
Bạn có thể chọn một Method
cụ thể hoặc lặp qua tất cả các phương pháp cùng một lúc để xem và so sánh các kết quả khác nhau một cách trực quan. Đối với vòng lặp xem xét như vậy, hãy để giá trị mặc định _CRYPT_ALL
trong tham số Method
.
Nhân tiện, để giới thiệu chức năng này, chúng ta một lần nữa cần mở rộng liệt kê tiêu chuẩn (lần này là ENUM_CRYPT_METHOD), nhưng vì các liệt kê trong MQL5 không thể được kế thừa như các lớp, một liệt kê mới ENUM_CRYPT_METHOD_EXT
thực sự được khai báo ở đây. Một lợi ích bổ sung của điều này là chúng ta đã thêm các tên thân thiện hơn cho các phần tử (trong phần bình luận, với gợi ý sẽ được hiển thị trong hộp thoại cài đặt).
enum ENUM_CRYPT_METHOD_EXT
{
_CRYPT_ALL = 0xFF, // Thử tất cả trong một vòng lặp
_CRYPT_DES = CRYPT_DES, // DES (yêu cầu khóa, 7 byte)
_CRYPT_AES128 = CRYPT_AES128, // AES128 (yêu cầu khóa, 16 byte)
_CRYPT_AES256 = CRYPT_AES256, // AES256 (yêu cầu khóa, 32 byte)
_CRYPT_HASH_MD5 = CRYPT_HASH_MD5, // MD5
_CRYPT_HASH_SHA1 = CRYPT_HASH_SHA1, // SHA1
_CRYPT_HASH_SHA256 = CRYPT_HASH_SHA256, // SHA256
_CRYPT_ARCH_ZIP = CRYPT_ARCH_ZIP, // ZIP
_CRYPT_BASE64 = CRYPT_BASE64, // BASE64
};
input string Text = "Let's encrypt this message"; // Văn bản (trống để xử lý Tệp)
input string File = "MQL5Book/clock10.htm"; // Tệp (chỉ sử dụng nếu Văn bản trống)
input ENUM_CRYPT_METHOD_EXT Method = _CRYPT_ALL;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Theo mặc định, tham số Text
được điền với một tin nhắn được cho là sẽ mã hóa. Bạn có thể thay thế nó bằng tin nhắn của riêng mình. Nếu chúng ta xóa Text
, chương trình sẽ xử lý tệp. Ít nhất một trong các tham số (Text
hoặc File
) nên chứa thông tin.
Vì mã hóa yêu cầu một khóa, hai tùy chọn khác cho phép bạn nhập trực tiếp khóa dưới dạng văn bản (mặc dù khóa không cần phải là văn bản và có thể chứa bất kỳ dữ liệu nhị phân nào, nhưng chúng không được hỗ trợ trong đầu vào) hoặc tạo độ dài mong muốn, tùy thuộc vào phương pháp mã hóa.
enum DUMMY_KEY_LENGTH
{
DUMMY_KEY_0 = 0, // 0 byte (không có khóa)
DUMMY_KEY_7 = 7, // 7 byte (đủ cho DES)
DUMMY_KEY_16 = 16, // 16 byte (đủ cho AES128)
DUMMY_KEY_32 = 32, // 32 byte (đủ cho AES256)
DUMMY_KEY_CUSTOM, // sử dụng CustomKey
};
input DUMMY_KEY_LENGTH GenerateKey = DUMMY_KEY_CUSTOM; // Tạo khóa (độ dài, hoặc từ CustomKey)
input string CustomKey = "My top secret key is very strong";
2
3
4
5
6
7
8
9
10
11
Cuối cùng, có một tùy chọn DisableCRCinZIP
để bật chế độ tương thích ZIP, chỉ ảnh hưởng đến phương pháp CRYPT_ARCH_ZIP.
input bool DisableCRCinZIP = false;
Để đơn giản hóa việc kiểm tra xem phương pháp có yêu cầu khóa mã hóa hay tính toán một hash (chuyển đổi một chiều không thể đảo ngược), 2 macro được định nghĩa.
#define KEY_REQUIRED(C) ((C) == CRYPT_DES || (C) == CRYPT_AES128 || (C) == CRYPT_AES256)
#define IS_HASH(C) ((C) == CRYPT_HASH_MD5 || (C) == CRYPT_HASH_SHA1 || (C) == CRYPT_HASH_SHA256)
2
Phần đầu của OnStart
chứa mô tả về các biến và mảng cần thiết.
void OnStart()
{
ENUM_CRYPT_METHOD method = 0;
int methods[]; // ở đây chúng ta sẽ thu thập tất cả các phần tử của ENUM_CRYPT_METHOD để lặp qua chúng
uchar key[] = {}; // mặc định trống: phù hợp cho băm, zip, base64
uchar zip[], opt[] = {1, 0, 0, 0}; // "tùy chọn" cho zip
uchar data[], result[]; // dữ liệu ban đầu và kết quả
2
3
4
5
6
7
Theo cài đặt của GenerateKey
, chúng ta lấy khóa từ trường CustomKey
hoặc chỉ điền mảng key
với các giá trị số nguyên tăng dần đều. Trong thực tế, khóa nên là một bí mật; khối giá trị không tầm thường, được chọn tùy ý.
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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Ở đây và dưới đây, hãy lưu ý việc sử dụng ArrayResize
sau StringToCharArray
. Hãy chắc chắn giảm mảng đi 1 phần tử, vì trong trường hợp hàm StringToCharArray
chuyển đổi chuỗi thành mảng byte, bao gồm số 0 cuối, điều này có thể làm gián đoạn việc thực thi chương trình như mong đợi. Đặc biệt, trong trường hợp này, chúng ta sẽ có một byte số 0 thừa trong khóa bí mật, và nếu một chương trình với hiện tượng tương tự không được sử dụng ở phía nhận, thì nó sẽ không thể giải mã tin nhắn. Những số 0 thừa như vậy cũng có thể ảnh hưởng đến khả năng tương thích với các giao thức trao đổi dữ liệu (nếu một hoặc một tích hợp khác của chương trình MQL với "thế giới bên ngoài" được thực hiện).
Tiếp theo, chúng ta ghi lại biểu diễn thô của khóa kết quả ở định dạng thập lục phân: điều này được thực hiện bởi hàm ByteArrayPrint
đã được sử dụng trong phần Ghi và đọc tệp ở chế độ đơn giản hóa.
if(ArraySize(key))
{
Print("Key (bytes):");
ByteArrayPrint(key);
}
else
{
Print("Key is not provided");
}
2
3
4
5
6
7
8
9
Tùy thuộc vào sự có sẵn của Text
hoặc File
, chúng ta điền mảng data
bằng các ký tự văn bản hoặc nội dung tệp.
if(StringLen(Text))
{
PRTF(Text);
PRTF(StringToCharArray(Text, data, 0, -1, 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
Cuối cùng, chúng ta lặp qua tất cả các phương pháp hoặc thực hiện chuyển đổi một lần với một phương pháp cụ thể.
const int n = (Method == _CRYPT_ALL) ?
EnumToArray(method, methods, 0, UCHAR_MAX) : 1;
ResetLastError();
for(int i = 0; i < n; ++i)
{
method = (ENUM_CRYPT_METHOD)((Method == _CRYPT_ALL) ? methods[i] : Method);
Print("- ", i, " ", EnumToString(method), ", key required: ",
KEY_REQUIRED(method));
if(method == CRYPT_ARCH_ZIP)
{
if(DisableCRCinZIP)
{
ArrayCopy(zip, opt); // mảng với tùy chọn bổ sung động cho ArraySwap
}
ArraySwap(key, zip); // thay đổi khóa thành trống hoặc tùy chọn
}
if(PRTF(CryptEncode(method, data, key, result)))
{
if(StringLen(Text))
{
// trang mã Latin (Tây) để thống nhất hiển thị cho tất cả người dùng
Print(CharArrayToString(result, 0, WHOLE_ARRAY, 1252));
ByteArrayPrint(result);
if(method != CRYPT_BASE64)
{
const uchar dummy[] = {};
uchar readable[];
if(PRTF(CryptEncode(CRYPT_BASE64, result, dummy, readable)))
{
PrintFormat("Try to decode this with CryptDecode.mq5 (%s):",
EnumToString(method));
// để nhận lại dữ liệu đã mã hóa để giải mã
// qua đầu vào chuỗi, áp dụng Base64 lên kết quả nhị phân
Print("base64:'" + CharArrayToString(readable, 0, WHOLE_ARRAY, 1252) + "'");
}
}
}
else
{
string parts[];
const string filename = File + "." +
parts[StringSplit(EnumToString(method), '_', parts) - 1];
if(PRTF(FileSave(filename, result)))
{
Print("File saved: ", filename);
if(IS_HASH(method))
{
ByteArrayPrint(result, 1000, "");
}
}
}
}
}
}
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Khi chúng ta chuyển đổi văn bản, chúng ta ghi lại kết quả, nhưng vì nó hầu như luôn là dữ liệu nhị phân, ngoại trừ phương pháp CRYPT_BASE64, việc hiển thị của chúng sẽ hoàn toàn vô nghĩa (nói thật, dữ liệu nhị phân không nên được ghi lại, nhưng chúng ta làm điều này để rõ ràng). Các ký hiệu không in được và các ký hiệu có mã lớn hơn 128 được hiển thị khác nhau trên các máy tính với ngôn ngữ khác nhau. Do đó, để thống nhất việc hiển thị các ví dụ cho tất cả độc giả, khi tạo một dòng trong CharArrayToString
, chúng ta sử dụng một trang mã rõ ràng (1252, ngôn ngữ Tây Âu). Đúng vậy, các phông chữ được sử dụng khi xuất bản sách có thể sẽ ảnh hưởng đến cách hiển thị của một số ký tự nhất định (tập hợp các glyph trong phông chữ có thể bị giới hạn).
Cần lưu ý rằng chúng ta chỉ kiểm soát việc chọn trang mã trong phương pháp hiển thị, và các byte trong mảng
result
không thay đổi vì điều này (tất nhiên, chuỗi thu được theo cách này không nên được gửi đi đâu thêm; nó chỉ cần thiết để hình dung nhằm sử dụng chính các byte của kết quả cho việc trao đổi dữ liệu).
Tuy nhiên, chúng ta vẫn mong muốn cung cấp cho người dùng một số cơ hội để lưu kết quả mã hóa nhằm giải mã nó sau này. Cách đơn giản nhất là chuyển đổi lại dữ liệu nhị phân bằng phương pháp CRYPT_BASE64.
Trong trường hợp mã hóa tệp, chúng ta chỉ cần lưu kết quả vào một tệp mới với tên mà phần mở rộng của từ cuối cùng trong tên phương pháp được thêm vào tên gốc. Ví dụ, bằng cách áp dụng CRYPT_HASH_MD5 cho tệp Example.txt
, chúng ta sẽ nhận được tệp đầu ra Example.txt.MD5
chứa hàm băm MD5 của tệp nguồn. Lưu ý rằng đối với phương pháp CRYPT_ARCH_ZIP, chúng ta sẽ nhận được một tệp với phần mở rộng ZIP, nhưng nó không phải là một kho lưu trữ ZIP tiêu chuẩn (do thiếu các tiêu đề với thông tin meta và bảng mục lục).
Hãy chạy script với cài đặt mặc định: chúng tương ứng với việc kiểm tra trong vòng lặp tất cả các phương pháp cho văn bản "Let's encrypt this message".
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 |
Text=Let's encrypt this message / ok
StringToCharArray(Text,data,0,-1,CP_UTF8)=26 / ok
- 0 CRYPT_BASE64, key required: false
CryptEncode(method,data,key,result)=36 / ok
TGV0J3MgZW5jcnlwdCB0aGlzIG1lc3NhZ2U=
[00] 54 | 47 | 56 | 30 | 4A | 33 | 4D | 67 | 5A | 57 | 35 | 6A | 63 | 6E | 6C | 77 |
[16] 64 | 43 | 42 | 30 | 61 | 47 | 6C | 7A | 49 | 47 | 31 | 6C | 63 | 33 | 4E | 68 |
[32] 5A | 32 | 55 | 3D |
- 1 CRYPT_AES128, key required: true
CryptEncode(method,data,key,result)=32 / ok
¯T* Ë[3hß Ã/-C }¬ÑØN¨®Ê Ñ
[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 |
CryptEncode(CRYPT_BASE64,result,dummy,readable)=44 / ok
Try to decode this with CryptDecode.mq5 (CRYPT_AES128):
base64:'AQuvVCoSy1szaN8Owy8tQxl9rIrRj9hOqK7KgYYGh9E='
- 2 CRYPT_AES256, key required: true
CryptEncode(method,data,key,result)=32 / ok
øUL»ÉsëDCô ¬.K)ýÁ LḠ+< !Dï
[00] F8 | 91 | 55 | 4C | BB | C9 | 73 | EB | 44 | 43 | 89 | F4 | 06 | 13 | AC | 2E |
[16] 4B | 29 | 8C | FD | C1 | 11 | 4C | E1 | B8 | 05 | 2B | 3C | 14 | 21 | 44 | EF |
CryptEncode(CRYPT_BASE64,result,dummy,readable)=44 / ok
Try to decode this with CryptDecode.mq5 (CRYPT_AES256):
base64:'+JFVTLvJc+tEQ4n0BhOsLkspjP3BEUzhuAUrPBQhRO8='
- 3 CRYPT_DES, key required: true
CryptEncode(method,data,key,result)=32 / ok
µ b &#ÇÅ+ýº'¥ B8f¡rØ-Pè<6âìË£
[00] B5 | 06 | 9D | 62 | 11 | 26 | 93 | 23 | C7 | C5 | 2B | FD | BA | 27 | A5 | 10 |
[16] 42 | 38 | 66 | A1 | 72 | D8 | 2D | 50 | E8 | 3C | 36 | E2 | EC | 82 | CB | A3 |
CryptEncode(CRYPT_BASE64,result,dummy,readable)=44 / ok
Try to decode this with CryptDecode.mq5 (CRYPT_DES):
base64:'tQadYhEmkyPHxSv9uielEEI4ZqFy2C1Q6Dw24uyCy6M='
- 4 CRYPT_HASH_SHA1, key required: false
CryptEncode(method,data,key,result)=20 / ok
§ßö*©ºø
|)bËbzÇÍ Û
[00] A7 | DF | F6 | 2A | A9 | BA | F8 | 0A | 80 | 7C | 29 | 62 | CB | 62 | 7A | C7 |
[16] CD | 0E | DB | 80 |
CryptEncode(CRYPT_BASE64,result,dummy,readable)=28 / ok
Try to decode this with CryptDecode.mq5 (CRYPT_HASH_SHA1):
base64:'p9/2Kqm6+AqAfCliy2J6x80O24A='
- 5 CRYPT_HASH_SHA256, key required: false
CryptEncode(method,data,key,result)=32 / ok
ÚZ2»¾7
ñÄÁ´¦ome2r@¾ô®³
[00] DA | 5A | 32 | 9A | 80 | BB | 94 | BE | 37 | 0C | 80 | 85 | 07 | F1 | 96 | C4 |
[16] C1 | B4 | 98 | A6 | 93 | 6F | 6D | 65 | 32 | 72 | 40 | BE | F4 | AE | B3 | 94 |
CryptEncode(CRYPT_BASE64,result,dummy,readable)=44 / ok
Try to decode this with CryptDecode.mq5 (CRYPT_HASH_SHA256):
base64:'2loymoC7lL43DICFB/GWxMG0mKaTb21lMnJAvvSus5Q='
- 6 CRYPT_HASH_MD5, key required: false
CryptEncode(method,data,key,result)=16 / ok
zIGT
Fû;3þèå
[00] 7A | 49 | 47 | 54 | 85 | 1B | 7F | 11 | 46 | FB | 3B | 97 | 33 | FE | E8 | E5 |
CryptEncode(CRYPT_BASE64,result,dummy,readable)=24 / ok
Try to decode this with CryptDecode.mq5 (CRYPT_HASH_MD5):
base64:'eklHVIUbfxFG+zuXM/7o5Q=='
- 7 CRYPT_ARCH_ZIP, key required: false
CryptEncode(method,data,key,result)=34 / ok
x^óI-Q/VHÍK.ª,(Q(ÉÈ,VÈM-.NLO
[00] 78 | 5E | F3 | 49 | 2D | 51 | 2F | 56 | 48 | CD | 4B | 2E | AA | 2C | 28 | 51 |
[16] 28 | C9 | C8 | 2C | 56 | C8 | 4D | 2D | 2E | 4E | 4C | 4F | 05 | 00 | 80 | 07 |
[32] 09 | C2 |
CryptEncode(CRYPT_BASE64,result,dummy,readable)=48 / ok
Try to decode this with CryptDecode.mq5 (CRYPT_ARCH_ZIP):
base64:'eF7zSS1RL1ZIzUsuqiwoUSjJyCxWyE0tLk5MTwUAgAcJwg=='
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
Khóa trong trường hợp này có độ dài đủ cho cả ba phương pháp mã hóa, và các phương pháp khác không cần nó chỉ đơn giản là bỏ qua. Do đó, tất cả các lệnh gọi hàm đã hoàn thành thành công.
Trong phần tiếp theo, chúng ta sẽ tìm hiểu cách giải mã các mã hóa và có thể kiểm tra xem hàm CryptDecode
có trả về tin nhắn gốc hay không. Vui lòng lưu ý phần này của nhật ký.
Tùy chọn DisableCRCinZIP
được bật sẽ giảm kết quả của phương pháp CRYPT_ARCH_ZIP đi một vài byte dư thừa.
- 7 CRYPT_ARCH_ZIP, key required: false
CryptEncode(method,data,key,result)=28 / ok
óI-Q/VHÍK.ª,(Q(ÉÈ,VÈM-.NLO
[00] F3 | 49 | 2D | 51 | 2F | 56 | 48 | CD | 4B | 2E | AA | 2C | 28 | 51 | 28 | C9 |
[16] C8 | 2C | 56 | C8 | 4D | 2D | 2E | 4E | 4C | 4F | 05 | 00 |
CryptEncode(CRYPT_BASE64,result,dummy,readable)=40 / ok
Try to decode this with CryptDecode.mq5 (CRYPT_ARCH_ZIP):
base64:'80ktUS9WSM1LLqosKFEoycgsVshNLS5OTE8FAA=='
2
3
4
5
6
7
8
Bây giờ hãy chuyển các thử nghiệm về mã hóa sang tệp. Để làm điều này, chạy lại script và xóa văn bản khỏi trường Text
. Kết quả là, chương trình sẽ xử lý tệp MQL5Book/clock10.htm
nhiều lần và sẽ tạo ra một số tệp dẫn xuất với các phần mở rộng khác nhau.
File=MQL5Book/clock10.htm / ok
FileLoad(File,data)=988 / ok
- 0 CRYPT_BASE64, key required: false
CryptEncode(method,data,key,result)=1320 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.BASE64
- 1 CRYPT_AES128, key required: true
CryptEncode(method,data,key,result)=992 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.AES128
- 2 CRYPT_AES256, key required: true
CryptEncode(method,data,key,result)=992 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.AES256
- 3 CRYPT_DES, key required: true
CryptEncode(method,data,key,result)=992 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.DES
- 4 CRYPT_HASH_SHA1, key required: false
CryptEncode(method,data,key,result)=20 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.SHA1
[00] 486ADFDD071CD23AB28E820B164D813A310B213F
- 5 CRYPT_HASH_SHA256, key required: false
CryptEncode(method,data,key,result)=32 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.SHA256
[00] 8990BBAC9C23B1F987952564EBCEF2078232D8C9D6F2CCC2A50784E8CDE044D0
- 6 CRYPT_HASH_MD5, key required: false
CryptEncode(method,data,key,result)=16 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.MD5
[00] 0CC4FBC899554BE0C0DBF5C18748C773
- 7 CRYPT_ARCH_ZIP, key required: false
CryptEncode(method,data,key,result)=687 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.htm.ZIP
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
Bạn có thể xem bên trong tất cả các tệp từ trình quản lý tệp và đảm bảo rằng không còn gì chung với nội dung gốc. Nhiều trình quản lý tệp có lệnh hoặc plugin để tính toán tổng băm để có thể so sánh với các giá trị hex MD5, SHA1 và SHA256 được in ra nhật ký.
Nếu chúng ta cố gắng mã hóa một văn bản hoặc tệp mà không cung cấp khóa có độ dài đúng, chúng ta sẽ nhận được lỗi INVALID_ARRAY(4006). Ví dụ, đối với một tin nhắn văn bản mặc định, chúng ta chọn AES256 trong tham số method
(yêu cầu khóa 32 byte). Sử dụng tham số GenerateKey
, chúng ta yêu cầu một khóa có độ dài 16 byte (hoặc bạn có thể xóa một phần hoặc hoàn toàn văn bản khỏi trường CustomKey
, để GenerateKey
mặc định).
Key (bytes):
[00] 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F |
Text=Let's encrypt this message / ok
StringToCharArray(Text,data,0,-1,CP_UTF8)=26 / ok
- 0 CRYPT_AES256, key required: true
CryptEncode(method,data,key,result)=0 / INVALID_ARRAY(4006)
2
3
4
5
6
Bạn cũng có thể nén cùng một tệp (như chúng ta đã làm với clock10.htm
) bằng phương pháp CRYPT_ARCH_ZIP hoặc sử dụng một trình lưu trữ thông thường. Nếu sau đó bạn xem bằng tiện ích xem nhị phân (thường được tích hợp trong trình quản lý tệp), thì cả hai kết quả sẽ hiển thị một khối nén chung, và sự khác biệt chỉ nằm ở dữ liệu meta bao quanh nó.
So sánh tệp nén bằng phương pháp CRYPT_ARCH_ZIP (trái) và kho lưu trữ ZIP tiêu chuẩn với nó (phải)
Nó cho thấy phần giữa và chính của kho lưu trữ là một chuỗi byte (được tô sáng màu tối) giống hệt với những gì được tạo ra bởi hàm CryptEncode
.
Cuối cùng, chúng ta sẽ chỉ ra cách biểu diễn văn bản Base64
của tệp đồ họa clock10.png
được tạo ra. Để làm điều này, xóa trường Text
và ghi MQL5Book/clock10.png
vào tham số File
. Chọn Base64
trong danh sách thả xuống Method
.
File=MQL5Book/clock10.png / ok
FileLoad(File,data)=457 / ok
- 0 CRYPT_BASE64, key required: false
CryptEncode(method,data,key,result)=612 / ok
FileSave(filename,result)=true / ok
File saved: MQL5Book/clock10.png.BASE64
2
3
4
5
6
Tệp clock10.png.BASE64
đã được tạo ra như một kết quả. Bên trong nó, chúng ta sẽ thấy chính dòng được chèn vào mã trang web, trong thẻ img
.
Nhân tiện, phương pháp nén "deflate" là cơ sở cho định dạng đồ họa PNG, vì vậy chúng ta có thể sử dụng CryptEncode
để lưu các bitmap tài nguyên vào tệp PNG. Tệp tiêu đề PNG.mqh
được đính kèm với sách, với hỗ trợ tối thiểu cho các cấu trúc bên trong cần thiết để mô tả hình ảnh: bạn nên tự thử nghiệm với mã nguồn của nó. Sử dụng PNG.mqh
, chúng ta đã viết một script đơn giản CryptPNG.mq5
chuyển đổi tài nguyên từ tệp "euro.bmp" được cung cấp cùng terminal sang tệp "my.png". Việc tải tệp PNG không được triển khai.
#resource "\\Images\\euro.bmp"
#include <MQL5Book/PNG.mqh>
void OnStart()
{
uchar null[]; // khóa trống cho CRYPT_ARCH_ZIP
uchar result[]; // mảng nhận
uint data[]; // pixel gốc
uchar bytes[]; // byte gốc
int width, height;
PRTF(ResourceReadImage("::Images\\euro.bmp", data, width, height));
ArrayResize(bytes, ArraySize(data) * 3 + width); // *3 cho PNG_CTYPE_TRUECOLOR (RGB)
ArrayInitialize(bytes, 0);
int j = 0;
for(int i = 0; i < ArraySize(data); ++i)
{
if(i % width == 0) bytes[j++] = 0; // mỗi dòng được thêm một byte chế độ bộ lọc
const uint c = data[i];
// bytes[j++] = (uchar)((c >> 24) & 0xFF); // alpha, cho PNG_CTYPE_TRUECOLORALPHA (ARGB)
bytes[j++] = (uchar)((c >> 16) & 0xFF);
bytes[j++] = (uchar)((c >> 8) & 0xFF);
bytes[j++] = (uchar)(c & 0xFF);
}
PRTF(CryptEncode(CRYPT_ARCH_ZIP, bytes, null, result));
int h = PRTF(FileOpen("my.png", FILE_BIN | FILE_WRITE));
PNG::Image image(width, height, result); // mặc định PNG_ctype_TRUECOLOR (RGB)
image.write(h);
FileClose(h);
}
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