Bài giảng Lập trình an toàn - Chương 4: Mã hóa đối xứng - Lương Ánh Hoàng
Biểu diễn khóa
Khóa đối xứng: Một số rất lớn sử dụng để mã hóa và giải mã thông điệp.
Biểu diễn khóa:
• Phân tách thành các byte và lưu dưới dạng một mảng.
unsigned char key[KEYLEN_BYTES]
• Biểu diễn dưới dạng số nguyên lớn nếu khóa có chiều dài 64-bit
long long key
• Biểu diễn dưới dạng chuỗi chữ số hexa
char key[]=“AF12B5C7E0.”
• Biểu diễn dưới dạng xâu ASCII (mật khẩu).
char key[]=“secret!!!”
• Lưu ý về tính “endian” của máy thực hiện mã hóa.
4.3 Mã hóa và giải mã Base64
Mã hóa Base64
• Sử dụng 6-bit để mã hóa dữ liệu và biểu diễn dưới dạng các chữ cái ASCII.
• Cứ 3 byte dữ liệu vào sẽ được biểu diễn thành 4 byte dữ liệu ra.
• Các ký tự ra nằm trong khoảng:
• 'A' - 'Z' tương đương các giá trị của từ mã từ 0-25.
• 'a' - 'z' tương đương các giá trị của từ mã từ 26-51.
• '0'- ‘9' tương đương các giá trị từ mã từ 52-61.
• '+' , '-' tương ứng với các giá trị mã 62,63.
• Nếu dữ liệu vào có kích thước không chia hết cho 3 sẽ thì được thêm vào bằng ký tự
í í
• VD
Dữ liệu gốc: 'A' - 0100.0001
Dữ liệu mã hóa dạng Base64: 010000.010000.000000.000000 ~ QQ==
Dữ liệu gốc: 'AA' - 0100.0001.0100.0001
Dữ liệu mã hóa dạng Base64: 010000.010100.000100.000000 ~ QUE=
Dữ liệu gốc: 'AAA' - 0100.0001.0100.0001.0100.0001
Dữ liệu dạng mã hóa Base64: 010000.010100.000101.000001 ~ QUFB
Tóm tắt nội dung tài liệu: Bài giảng Lập trình an toàn - Chương 4: Mã hóa đối xứng - Lương Ánh Hoàng
Chương 4. Mã hóa đối xứng Symmetric Crytography Lương Ánh Hoàng hoangla@soict.hut.edu.vn Nội dung Biểu diễn khóa Chuyển đổi chuỗi hexa và khóa nhị phân. Mã hóa và giải mã Base64 Các phương pháp mã hóa đối xứng Mã hóa đối xứng với OpenSSL Mã hóa đối xứng với Microsoft Crypto API 4.1 Biểu diễn khóa Khóa đối xứng: Một số rất lớn sử dụng để mã hóa và giải mã thông điệp. Biểu diễn khóa: Phân tách thành các byte và lưu dưới dạng một mảng. unsigned char key[KEYLEN_BYTES] Biểu diễn dưới dạng số nguyên lớn nếu khóa có chiều dài 64-bit long long key Biểu diễn dưới dạng chuỗi chữ số hexa char key[]=“AF12B5C7E0...” Biểu diễn dưới dạng xâu ASCII (mật khẩu). char key[]=“secret!!!” Lưu ý về tính “endian” của máy thực hiện mã hóa. Chuyển đổi khóa nhị phân sang dạng chuỗi chữ số hexa #define MAX_KEY_LEN 32 unsigned char key[MAX_KEY_LEN]; char result[MAX_KEY_LEN*2+i]; for (int ì=0;ì<MaX_KEY_LEN;ì++) sprintf(result+i*2,”%2X”,key[i]); printf(”Key:%s”,result); • Chuyển đổi chuỗi hexa sang khóa nhị phân char Hex2Dec(char c) { if (('a'<=c)&&(c<='z')) return c - 'a'+1o; if (('A'<=c)&&(c<='Z')) return c - 'A'+1o; if (('0'<=c)&&(c<='9')) return c - 'o'; return -1; } #define MAX_KEY_LENGTH 32 char hexa[]=”AF125C4D8E”; unsigned char key[wAx_KEY_LENGTH]; int keylen = strlen(hexa); char ci,c2; if ((keylen%2!=o)||(keylen/2 > MAX_KEY_LENGTH)) printf(”Invalid key length”); keylen = keylen/2; for (int i=o;i<keylen;i++) ci = Hex2Dec(hexa[i*2]); c2 = Hex2Dec(hexa[i*2+i]); if ((C1==-1)||(C2==-1)) { printf(”Invalid character !!!”); break; }; key[i] = (c1<<4)|c2; Mã hóa và giải mã Base64 Mã hóa Base64 Sử dụng 6-bit để mã hóa dữ liệu và biểu diễn dưới dạng các chữ cái ASCII. Cứ 3 byte dữ liệu vào sẽ được biểu diễn thành 4 byte dữ liệu ra. Các ký tự ra nằm trong khoảng: 'A' - 'Z' tương đương các giá trị của từ mã từ 0-25. 'a' - 'z' tương đương các giá trị của từ mã từ 26-51. '0'- ‘9' tương đương các giá trị từ mã từ 52-61. '+' , '-' tương ứng với các giá trị mã 62,63. Nếu dữ liệu vào có kích thước không chia hết cho 3 sẽ thì được thêm vào bằng ký tự í í VD Dữ liệu gốc: 'A' - 0100.0001 Dữ liệu mã hóa dạng Base64: 010000.010000.000000.000000 ~ QQ== Dữ liệu gốc: 'AA' - 0100.0001.0100.0001 Dữ liệu mã hóa dạng Base64: 010000.010100.000100.000000 ~ QUE= Dữ liệu gốc: 'AAA' - 0100.0001.0100.0001.0100.0001 Dữ liệu dạng mã hóa Base64: 010000.010100.000101.000001 ~ QUFB Mã hóa và giải mã Base64 Mã hóa Base64 Value Char Value Char Value Char Value Char 0 A 16 Q 32 g 48 w 1 B 17 R 33 h 49 x 2 C 18 S 34 i 50 y 3 D 19 T 35 ỉ 51 z 4 E 20 U 36 k 52 0 5 F 21 V 37 l 53 1 6 G 22 W 38 m 54 2 7 H 23 X 39 n 55 3 8 I 24 Y 40 o 56 4 9 J 25 Z 41 p 57 5 10 K 26 a 42 q 58 6 11 L 27 b 43 r 59 7 12 M 28 c 44 s 60 8 13 N 29 d 45 t 61 9 14 O 30 e 46 u 62 + 15 P 31 f 47 v 63 / Mã hóa và giải mã Base64 Đoạn chương trình mã hóa Base64: P4.5 - Secure C Programming Cookbook Đoạn chương trình giải mã Base64: P4.6 - Secure C Programming Cookbook Các phương pháp mã hóa đối xứng Mã hóa đối xứng: Sử dụng chung một khóa cho mã hóa và giải mã Có hai loại: Mã khối và mã dòng Có nhiều chế độ mã hóa: ECB, CBC, CFB, OFB, CTR, CWC... Có nhiều giải thuật: Cipher Key size Speed B Implementation Notes AES 128 bitsl51 14.1 cpb in asm, 22.6 cpb in C Brian Gladman'sl6! The assembly version currently works only on Windows. AES 128 bits 41.3 cpb OpenSSL Triple DES 192 bits[7] 108.2 cpb OpenSSL SNOW 2.0 128 or 256 bits 6.4 cpb Fast reference implementation!8! This implementation is written in C. RC4 Up to 256 bits (usually 128 bits) 10.7 cpb OpenSSL Serpent 128, 192, or 256 bits 35.6 cpb Fast reference implementation It gets a lot faster on 64-bit platforms and is at least as fast as AES in hardware. Blowfish Up to 256 bits (usually 128 bits) 23.2 cpb OpenSSL Thư viện OpenSSL: Thư viện mã nguồn mở, mạnh mẽ và dễ sử dụng. OpenSSL hỗ trợ: Nhiều thuật toán mã hóa: AES, DES , 3DES, Blowfish, CAST, Idea, RC2, RC5. Nhiều chế độ mã hóa: ECB, CBC, CFB, OFB, CTR... Mã hóa dòng: RC4. Các giải thuật băm: MD2, MD4, MD5,SHA-1,SHA-224,SHA-256... MAC: HMAC. MDC2 Các giải thuật mã hóa công khai: DH, DSA, RSA, ECC Sử dụng thư viện: Trên Unix/Linux: Tải source về và biên dịch. Kết quả là file libcrypto.[so/ a], libssl.[so/a] và các file .h để include vào chương trình. Trên Windows: Tải bản binary đã biên dịch sẵn: libeay32.dll, ssleay32.dll, tệp tiêu đề (.h) và tệp thư viện (.lib). Link openssl.html Giao diện OpenSSL EVP Là API mức cao của OpenSSL, cho phép truy nhập đến các thuật toán ở mức thấp một cách tập trung, dễ dàng. Tệp tiêu đề . Tệp thư viện: libeay32.lib, ssleay32.lib Mã hóa AES với OpenSSL EVP. Khởi tạo khóa, vector khởi tạo, salt với EVP_BytesToKey hoặc tự chọn một bộ Key, IV nào đó. Khởi tạo ngữ cảnh mã hóa với hàm EVP_EncryptInit_ex. Khởi tạo ngữ cảnh giải mã với hàm EVP_DecryptInit_ex. Mã hóa dữ liệu bằng việc liên tục gọi hàm EVP_EncryptUpdate, kết thúc quá trình mã hóa bằng hàm EVP_EncryptFinal_ex. Giải mã dữ liệu bằng việc liên tục gọi hàm EVP_DecryptUpdate, kết thúc quá trình giải mã bằng hàm EVP_DecryptFinal_ex. VD Sinh key và iv bằng hàm EVP_BytesToKey char key[32]; char iv[32]; char * key_data = “nopass”; unsigned int salt[] = {12345, 54321}; EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, 6, 1, key, iv); Khởi tạo ngữ cảnh mã hóa với key và iv đã chọn EVP_CIPHER_CTX e_ctx; EVP_CIPHE R_CTX_i n it(&e_ctx); EVP_EncryptInit_ex(&e_ctx, EVP_aes_256_cbc(),NULL, key, iv); Khởi tạo ngữ cảnh giải mã với key và iv đã chọn EVP_CIPHER_CTX d_ctx; EVP_CIPHE R_CTX_i n it(&d_ctx); EVP_DecryptInit_ex(&d_ctx, EVP_aes_256_cbc(),NULL, key, iv); VD (tiếp) • Mã hóa với ngữ cảnh đã được khởi tạo char * plaintext=“Hello”; int len = strlen(plaintext); char ciphertext[i024]; int c_len = 0, f_len = 0; /* Gọi lại hàm này để cho phép OpenSSL sử dụng lại ngữ cảnh phiên mã hóa trước */ EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); • • // Mỗi chu kỳ Update, c_len sẽ chứa số byte của xâu mã được EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, len); • • // Cuối chu kỳ Update, f_len sẽ chưa số byte còn lại của xâu mã EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len); VD (tiếp) • Giải mã với ngữ cảnh đã được khởi tạo char plaintext[i024]; int p_len = 0; /* Gọi lại hàm này để cho phép OpenSSL sử dụng lại ngữ cảnh phiên giãi mã hóa trước */ EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL); • • // Giải mã với ciphertext và len được cung cấp trước EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len); • • // Kết thúc quá trình giải mã, cập nhật dữ liệu còn lại vào plaintext. EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len); Thư viện CryptoAPI Cung cấp các hàm mật mã học cơ bản thông qua các Cryptographic Service Providers (CSP). Microsoft Base Cryptographic Service Provider: RC2, RC4, DES Microsoft Enhanced Cryptographic Service Provider: Triple-DES Microsoft AES Cryptographic Service Provider: AES • Cung cấp các hàm mã hóa và giải mã chứng thư số, và đồng thời bổ sung các hàm băm. Cung cấp các hàm quản lý và lưu trữ chứng thư số. Các hàm mã thông điệp hóa mức cao (Simplified Message Functions). Các hàm mã hóa thông điệp mức thấp (Low-Level Message Functions). Thư viện CryptoAPI CryptoAPI Architecture I Certificate Functions I Message Functions Tạo khóa Ngẫu nhiên Từ mật khẩu Từ bên ngoài Đặt chế độ mã CBC ECB Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. Tệp tiêu đề wincript.h Thư viện Crypt32.lib Trình tự sử dụng Khởi tạo Provider V > Thực hiện Mã hóa/Giải mã V / Thiết lập vector khởi tạo l/ Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Khởi tạo ngữ cảnh Provider thông qua hàm CryptAcquireContext BOOL WINAPI CryptAcquireContext( out HCRYPTPROV* phProv, _in LPCTSTR pszContainer, in LPCTSTR pszProvider, in DWORD dwProvType, in DWORD dwFlags ); VD: HCRYPTPROV hProvider; if (!CryptAcquireContext(&hProvider, 0, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) return 0; Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Sử dụng Key thông qua một trong ba hàm. Kết quả trả về là đối tượng HCRYPTKEY CryptGenKey( ): Sinh khóa ngẫu nhiên. CryptDeriveKey( ): Sinh khóa từ mật khẩu. CryptImportKey( ) : Sinh khóa từ một đối tượng trong bộ nhớ. VD1. Sinh khóa ngẫu nhiên DWORD dwFlags; HCRYPTKEY hKey; DWORD dwSize = 256; dwFlags = ((dwSize << 16) & oxFFFFoooo) | CRYPT_EXPORTABLE; if (!CryptGenKey(hProvider, CALG_AES_256, dwFlags, &hKey)) return 0; Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. VD2. Sinh khóa từ mật khẩu: Cần phải băm mật khẩu và truyền vào hàm CryptDeriveKey char * password = “nopass”; BOOL bResult; DWORD cbData; HCRYPTKEY hKey; // Lưu Key HCRYPTHASH hHash; // Lưu giá trị băm của mật khẩu if (!CryptCreateHash(hProvider, CALG_SHA1, 0, 0, &hHash)) // Khởi tạo hàm băm return 0; cbData = lstrlen(password) * sizeof(TCHAR); if (!CryptHashData(hHash, (BYTE *)password, cbData, 0)) // Băm mật khẩu { CryptDestroyHash(hHash); return 0; _ } , A // Tạo key từ giá trị băm của mật khẩu bResult = CryptDeriveKey(hProvider, CALG_AES_256, hHash, CRYPT_EXPORTABLE, &hKey); CryptDestroyHash(hHash); Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. Thiết lập chế độ mã hóa CBC với hàm CryptSetKeyParam DWORD dwMode = CRYPT_MODE_CBC; CryptSetKeyParam(hKey, KP_MODE, (BYTE *)&dwMode, 0); Sinh ngẫu nhiên vector khởi tạo (IV) BOOL bResult; BYTE *pbTemp; // Lưu kết quả // Lưu vector khởi tạo DWORD dwBlockLen, dwDataLen; dwDataLen = sizeof(dwBlockLen); // Lấy kích thước block của thuật toán mã hóa if (!CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE *)&dwBlockLen, &dwDataLen, 0)) return 0; dwBlockLen /= 8; if (!(pbTemp = (BYTE *)LocalAlloc(LMEM_FIXED, dwBlockLen))) return FALSE; // Sinh ngẫu nhiên IV bResult = CryptGenRandom(hProvider, dwBlockLen, pbTemp); // Thiết lập IV bResult = CryptSetKeyParam(hKey, KP_IV, pbTemp, 0); 75 LocalFree(pbTemp); Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Mã hóa với CryptEncrypt Với các giải thuật mã hóa dòng thì kích thước dữ liệu ra = kích thước dữ liệu vào. Với các giải thuật mã hóa khối thì kích thước dữ liệu ra <= kích thước dữ liệu vào + kích thước khối. Hàm CryptEncrypt sẽ ghi đè dữ liệu mã hóa được vào bộ đệm chứa dữ liệu vào. Đoạn chương trình thực hiện mã hóa chung cho cả hai loại. ALG_ID Algid; // Giải thuật mã char * pbData = "Hello CryptAPI"; // Xâu nguồn cần mã char * pbResult = 0; // Xâu kết quả DWORD dwDataLen = 0,dwBlockLen = 0; cbData = strlen(pbData); // Chiều dài xâu nguồn dwDataLen = sizeof(ALG_ID); // Lấy thông tin về giải thuật mã hóa với key cho trước if (!CryptGetKeyParam(hKey, KP_ALGID, (BYTE *)&Algid, &dwDataLen, 0)) return 0; Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Mã hóa với CryptEncrypt if (GET_ALG_TYPE(Algid) != ALG_TYPE_STREAM) // Mã hóa khối { .... dwDataLen = sizeof(DWORD); ret = CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE*)&dwBlockLen, &dwDataLen, 0); // Lấy kích thước block theo bit dwBlockLen = dwBlockLen/8; // Đổi kích thước block ra đơn vị byte // Cấp phát bộ nhớ để chứa kết quả pbResult = (char*)malloc(cbData+dwBlockLen); memcpy(pbResult,pbData,cbData); // Thực hiện mã hóa, kết quả là dwDataLen byte lưu trong pbResult dwDataLen = cbData; CryptEncrypt(hKey, 0, TRUE, 0, (BYTE*)pbResult, &dwDataLen, cbData+16)) ; } Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Mã hóa với CryptEncrypt [tiếp) else // Mã hóa dòng { // Cấp phát bộ nhớ lưu kết quả pbResult = (char*)malloc(cbData); // Bảo toàn dữ liệu nguồn memcpy(pbResult,pbData,cbData); // Thực hiện mã hóa CryptEncrypt(hKey,0,TRUE,0,pbResult,&dwDataLen,cbData); } Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp với thuật toán AES. • Giải mã với CryptDecrypt Kích thước dữ liệu đích <= kích thước dữ liệu nguồn Thực hiện đơn giản hơn so với CryptEncrypt Ví dụ char * pbData ; DWORD cbData; char * pbResult; DWORD dwDataLen; // Dữ liệu nguồn // Kích thước nguồn // Dữ liệu đích // Kích thước đích // Cấp phát bộ nhớ và sao chép dữ liệu nguồn vào đích pbResult = (char*)malloc(cbData); memcpy(pbResult, pbData, cbData); dwDataLen = cbDataLen; // Giải mã, kết quả là dwDataLen byte lưu trong pbResult CryptDecrypt(hKey,0,TRUE,0,pbResult,&dwDataLen); Trao đổi khóa với OpenSSL CryptoAPI không cho phép nhập và xuất khóa dạng thô như OpenSSL. Để trao đổi khóa với thư viện khác, cần mã hóa khóa theo giải thuật AT_KEYEXCHANGE, và thực hiện nhập xuất dưới dạng cấu trúc BLOB. Hàm CryptImportKeyvà CryptExportKey dùng để thực hiện nhập xuất khóa. Xem thêm phần 5.26, 5.27 trong Secure Programming Cookbook.
File đính kèm:
- bai_giang_lap_trinh_an_toan_chuong_4_ma_hoa_doi_xung_luong_a.docx
- secureprogramming_4_5235_505050.pdf