Chương 4. Mã hóa đối xứng Symmetric Crytography Lương Ánh Hoàng hoangla@soict.hut.edu.vn
Nội dung
4.1 Biểu diễn khóa 4.2 Chuyển đổi chuỗi hexa và khóa nhị phân. 4.3 Mã hóa và giải mã Base64 4.4 Các phương pháp mã hóa đối xứng 4.5 Mã hóa đối xứng với OpenSSL 4.6 Mã hóa đối xứng với Microsoft Crypto API
56
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). key[]=“secret!!!”
char
• Lưu ý về tính “endian” của máy thực hiện mã hóa.
57
4.2 Chuyển đổi chuỗi hexa và khóa nhị phân
• Chuyển đổi khóa nhị phân sang dạng chuỗi chữ số hexa
32
result[MAX_KEY_LEN*2+1];
sprintf(result+i*2,"%2X",key[i]);
#define
MAX_KEY_LEN
unsigned
char
key[MAX_KEY_LEN];
char
for
(int
i=0;i
58
4.2
Chuyển
đổi
chuỗi
hexa
và
khóa
nhị
phân
• Chuyển
đổi
chuỗi
hexa
sang
khóa
nhị
phân
MAX_KEY_LENGTH))
printf("Invalid
key
length");
keylen
=
keylen/2;
for
(int
i=0;i
MAX_KEY_LENGTH
32
c1
=
Hex2Dec(hexa[i*2]);
c2
=
Hex2Dec(hexa[i*2+1]);
if
((c1==-‐1)||(c2==-‐1))
{
printf("Invalid
character
!!!");
break;
};
key[i]
=
(c1<<4)|c2;
};
char
Hex2Dec(char
c)
{
if
(('a'<=c)&&(c<='z'))
return
c
-‐
'a'+10;
if
(('A'<=c)&&(c<='Z'))
return
c
-‐
'A'+10;
if
(('0'<=c)&&(c<='9'))
return
c
-‐
'0';
return
-‐1;
}
…
#define
char
hexa[]="AF125C4D8E";
unsigned
char
key[MAX_KEY_LENGTH];
int
keylen
=
strlen(hexa);
char
c1,c2;
if
((keylen%2!=0)||(keylen/2
>
59
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
60
4.3
Mã
hóa
và
giải
mã
Base64
Value Char Value Char Value Char
• Mã
hóa
Base64
Char
Value
16
0 A Q 32 g 48 w
1 B R 17 33 h 49 x
2 C S 18 34 i 50 y
3 D T 19 35 j 51 z
4 E U 20 36 k 52 0
5 F V 21 37 l 53 1
6 G W 22 38 m 54 2
7 H X 23 39 n 55 3
8 I Y 24 40 o 56 4
9 J Z 25 41 p 57 5
10 K a 26 42 q 58 6
11 L b 27 43 r 59 7
12 M c 28 44 s 60 8
13 N d 29 45 t 61 9
14 O e 30 46 u 62 +
61
15 P f 31 47 v 63 /
4.3
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
62
4.4
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[4]
Implementation
Notes
Brian
Gladman's[6]
128
bits[5]
AES
14.1
cpb
in
asm,
22.6
cpb
in
C
The
assembly
version
currently
works
only
on
Windows.
128
bits
41.3
cpb
AES
OpenSSL
Triple
DES
192
bits[7]
108.2
cpb
OpenSSL
SNOW
2.0
128
or
256
bits
6.4
cpb
This
implementation
is
written
in
C.
Fast
reference
implementation[8]
RC4
10.7
cpb
OpenSSL
Up
to
256
bits
(usually
128
bits)
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
23.2
cpb
OpenSSL
Up
to
256
bits
(usually
128
bits)
63
4.5
Mã
hóa
đối
xứng
với
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,
Blow}ish,
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à
}ile
libcrypto.[so/
a],
libssl.[so/a]
và
các
}ile
.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
http://www.ie7pro.com/
openssl.html
64
4.5
Mã
hóa
đối
xứng
với
OpenSSL
• 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.
65
4.5
Mã
hóa
đối
xứng
với
OpenSSL
• VD
• Sinh
key
và
iv
bằng
hàm
EVP_BytesToKey
key[32];
iv[32];
*
key_data
=
“nopass”;
char
char
char
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_CIPHER_CTX_init(&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_CIPHER_CTX_init(&d_ctx);
EVP_DecryptInit_ex(&d_ctx,
EVP_aes_256_cbc(),NULL,
key,
iv);
66
4.5
Mã
hóa
đối
xứng
với
OpenSSL
• VD
(tiếp)
• Mã
hóa
với
ngữ
cảnh
đã
được
khởi
tạo
plaintext=“Hello”;
len
=
strlen(plaintext);
ciphertext[1024];
c_len
=
0,
f_len
=
0;
char
*
int
char
int
/*
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);
…
67
4.5
Mã
hóa
đối
xứng
với
OpenSSL
• VD
(tiếp)
• Giải
mã
với
ngữ
cảnh
đã
được
khởi
tạo
plaintext[1024];
p_len
=
0;
char
int
/*
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);
68
4.6
Microsoft
Crypto
API
• 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
(Simpli}ied
Message
Functions).
• Các
hàm
mã
hóa
thông
điệp
mức
thấp
(Low-‐Level
Message
Functions).
69
4.6
Microsoft
Crypto
API
• Thư
viện
CryptoAPI
70
4.6
Microsoft
Crypto
API
• Sử
dụng
thư
viện
CryptoAPI
để
thực
hiện
mã
hóa
đối
xứng
thông
điệp
Khởi
tạo
Provider
với
thuật
toán
AES.
• Tệp
tiêu
đề
wincript.h
• Thư
viện
Crypt32.lib
• Trình
tự
sử
dụng
Tạo
khóa
• Ngẫu
nhiên
• Từ
mật
khẩu
• Từ
bên
ngoài
Đặt
chế
độ
mã
• CBC
• ECB
• …
Thực
hiện
Mã
hóa/Giải
mã
Thiết
lập
vector
khởi
tạo
71
4.6
Microsoft
Crypto
API
• 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
__in
LPCTSTR
pszContainer,
__in
LPCTSTR
pszProvider,
__in
DWORD
dwProvType,
__in
DWORD
dwFlags
);
hProvider;
0,
MS_ENH_RSA_AES_PROV,
PROV_RSA_AES,
CRYPT_VERIFYCONTEXT))
return
0;
BOOL
WINAPI
CryptAcquireContext(__out
HCRYPTPROV*
phProv,
VD:
HCRYPTPROV
if
(!CryptAcquireContext(&hProvider,
72
4.6
Microsoft
Crypto
API
• 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)
&
0xFFFF0000)
|
CRYPT_EXPORTABLE;
if
(!CryptGenKey(hProvider,
CALG_AES_256,
dwFlags,
&hKey))
return
0;
73
4.6
Microsoft
Crypto
API
• Sử
dụng
thư
viện
CryptoAPI
để
thực
hiện
mã
hóa
đối
xứng
thông
điệp
//
Lưu
Key
//
Lưu
giá
trị
băm
của
mật
khẩu
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;
HCRYPTHASH
hHash;
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;
{
}
74
//
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);
4.6
Microsoft
Crypto
API
• 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)
//
Lưu
kết
quả
//
Lưu
vector
khởi
tạo
75
BOOL
bResult;
BYTE
*pbTemp;
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);
LocalFree(pbTemp);
4.6
Microsoft
Crypto
API
• 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.
*
pbData
=
"Hello
CryptAPI";
*
pbResult
=
0;
//
Giải
thuật
mã
//
Xâu
nguồn
cần
mã
//
Xâu
kết
quả
ALG_ID
Algid;
char
char
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;
76
4.6
Microsoft
Crypto
API
• 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,
0);
//
Lấy
kích
thước
block
theo
bit
dwDataLen
=
sizeof(DWORD);
ret
=
CryptGetKeyParam(hKey,
KP_BLOCKLEN,
(BYTE*)&dwBlockLen,
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))
;
}
77
4.6
Microsoft
Crypto
API
• 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)
//
Mã
hóa
dòng
else
{
//
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);
}
78
4.6
Microsoft
Crypto
API
• 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ụ
//
Dữ
liệu
nguồn
//
Kích
thước
nguồn
//
Dữ
liệu
đích
//
Kích
thước
đích
char
*
pbData
;
DWORD
cbData;
char
*
pbResult;
DWORD
dwDataLen;
…
//
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);
79
4.6
Microsoft
Crypto
API
• 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.
80
58
4.2 Chuyển đổi chuỗi hexa và khóa nhị phân
• Chuyển đổi chuỗi hexa sang khóa
nhị phân
MAX_KEY_LENGTH))
printf("Invalid
key
length");
keylen
=
keylen/2;
for
(int
i=0;i
MAX_KEY_LENGTH
32
c1
=
Hex2Dec(hexa[i*2]);
c2
=
Hex2Dec(hexa[i*2+1]);
if
((c1==-‐1)||(c2==-‐1))
{
printf("Invalid
character
!!!");
break;
};
key[i]
=
(c1<<4)|c2;
};
char
Hex2Dec(char
c)
{
if
(('a'<=c)&&(c<='z'))
return
c
-‐
'a'+10;
if
(('A'<=c)&&(c<='Z'))
return
c
-‐
'A'+10;
if
(('0'<=c)&&(c<='9'))
return
c
-‐
'0';
return
-‐1;
}
…
#define
char
hexa[]="AF125C4D8E";
unsigned
char
key[MAX_KEY_LENGTH];
int
keylen
=
strlen(hexa);
char
c1,c2;
if
((keylen%2!=0)||(keylen/2
>
59
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
60
4.3
Mã
hóa
và
giải
mã
Base64
Value Char Value Char Value Char
• Mã
hóa
Base64
Char
Value
16
0 A Q 32 g 48 w
1 B R 17 33 h 49 x
2 C S 18 34 i 50 y
3 D T 19 35 j 51 z
4 E U 20 36 k 52 0
5 F V 21 37 l 53 1
6 G W 22 38 m 54 2
7 H X 23 39 n 55 3
8 I Y 24 40 o 56 4
9 J Z 25 41 p 57 5
10 K a 26 42 q 58 6
11 L b 27 43 r 59 7
12 M c 28 44 s 60 8
13 N d 29 45 t 61 9
14 O e 30 46 u 62 +
61
15 P f 31 47 v 63 /
4.3
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
62
4.4
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[4]
Implementation
Notes
Brian
Gladman's[6]
128
bits[5]
AES
14.1
cpb
in
asm,
22.6
cpb
in
C
The
assembly
version
currently
works
only
on
Windows.
128
bits
41.3
cpb
AES
OpenSSL
Triple
DES
192
bits[7]
108.2
cpb
OpenSSL
SNOW
2.0
128
or
256
bits
6.4
cpb
This
implementation
is
written
in
C.
Fast
reference
implementation[8]
RC4
10.7
cpb
OpenSSL
Up
to
256
bits
(usually
128
bits)
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
23.2
cpb
OpenSSL
Up
to
256
bits
(usually
128
bits)
63
4.5
Mã
hóa
đối
xứng
với
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,
Blow}ish,
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à
}ile
libcrypto.[so/
a],
libssl.[so/a]
và
các
}ile
.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
http://www.ie7pro.com/
openssl.html
64
4.5
Mã
hóa
đối
xứng
với
OpenSSL
• 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.
65
4.5
Mã
hóa
đối
xứng
với
OpenSSL
• VD
• Sinh
key
và
iv
bằng
hàm
EVP_BytesToKey
key[32];
iv[32];
*
key_data
=
“nopass”;
char
char
char
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_CIPHER_CTX_init(&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_CIPHER_CTX_init(&d_ctx);
EVP_DecryptInit_ex(&d_ctx,
EVP_aes_256_cbc(),NULL,
key,
iv);
66
4.5
Mã
hóa
đối
xứng
với
OpenSSL
• VD
(tiếp)
• Mã
hóa
với
ngữ
cảnh
đã
được
khởi
tạo
plaintext=“Hello”;
len
=
strlen(plaintext);
ciphertext[1024];
c_len
=
0,
f_len
=
0;
char
*
int
char
int
/*
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);
…
67
4.5
Mã
hóa
đối
xứng
với
OpenSSL
• VD
(tiếp)
• Giải
mã
với
ngữ
cảnh
đã
được
khởi
tạo
plaintext[1024];
p_len
=
0;
char
int
/*
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);
68
4.6
Microsoft
Crypto
API
• 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
(Simpli}ied
Message
Functions).
• Các
hàm
mã
hóa
thông
điệp
mức
thấp
(Low-‐Level
Message
Functions).
69
4.6
Microsoft
Crypto
API
• Thư
viện
CryptoAPI
70
4.6
Microsoft
Crypto
API
• Sử
dụng
thư
viện
CryptoAPI
để
thực
hiện
mã
hóa
đối
xứng
thông
điệp
Khởi
tạo
Provider
với
thuật
toán
AES.
• Tệp
tiêu
đề
wincript.h
• Thư
viện
Crypt32.lib
• Trình
tự
sử
dụng
Tạo
khóa
• Ngẫu
nhiên
• Từ
mật
khẩu
• Từ
bên
ngoài
Đặt
chế
độ
mã
• CBC
• ECB
• …
Thực
hiện
Mã
hóa/Giải
mã
Thiết
lập
vector
khởi
tạo
71
4.6
Microsoft
Crypto
API
• 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
__in
LPCTSTR
pszContainer,
__in
LPCTSTR
pszProvider,
__in
DWORD
dwProvType,
__in
DWORD
dwFlags
);
hProvider;
0,
MS_ENH_RSA_AES_PROV,
PROV_RSA_AES,
CRYPT_VERIFYCONTEXT))
return
0;
BOOL
WINAPI
CryptAcquireContext(__out
HCRYPTPROV*
phProv,
VD:
HCRYPTPROV
if
(!CryptAcquireContext(&hProvider,
72
4.6
Microsoft
Crypto
API
• 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)
&
0xFFFF0000)
|
CRYPT_EXPORTABLE;
if
(!CryptGenKey(hProvider,
CALG_AES_256,
dwFlags,
&hKey))
return
0;
73
4.6
Microsoft
Crypto
API
• Sử
dụng
thư
viện
CryptoAPI
để
thực
hiện
mã
hóa
đối
xứng
thông
điệp
//
Lưu
Key
//
Lưu
giá
trị
băm
của
mật
khẩu
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;
HCRYPTHASH
hHash;
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;
{
}
74
//
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);
4.6
Microsoft
Crypto
API
• 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)
//
Lưu
kết
quả
//
Lưu
vector
khởi
tạo
75
BOOL
bResult;
BYTE
*pbTemp;
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);
LocalFree(pbTemp);
4.6
Microsoft
Crypto
API
• 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.
*
pbData
=
"Hello
CryptAPI";
*
pbResult
=
0;
//
Giải
thuật
mã
//
Xâu
nguồn
cần
mã
//
Xâu
kết
quả
ALG_ID
Algid;
char
char
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;
76
4.6
Microsoft
Crypto
API
• 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,
0);
//
Lấy
kích
thước
block
theo
bit
dwDataLen
=
sizeof(DWORD);
ret
=
CryptGetKeyParam(hKey,
KP_BLOCKLEN,
(BYTE*)&dwBlockLen,
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))
;
}
77
4.6
Microsoft
Crypto
API
• 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)
//
Mã
hóa
dòng
else
{
//
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);
}
78
4.6
Microsoft
Crypto
API
• 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ụ
//
Dữ
liệu
nguồn
//
Kích
thước
nguồn
//
Dữ
liệu
đích
//
Kích
thước
đích
char
*
pbData
;
DWORD
cbData;
char
*
pbResult;
DWORD
dwDataLen;
…
//
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);
79
4.6
Microsoft
Crypto
API
• 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.
80
MAX_KEY_LENGTH 32
c1 = Hex2Dec(hexa[i*2]); c2 = Hex2Dec(hexa[i*2+1]); if ((c1==-‐1)||(c2==-‐1)) { printf("Invalid character !!!"); break; }; key[i] = (c1<<4)|c2;
};
char Hex2Dec(char c) { if (('a'<=c)&&(c<='z')) return c -‐ 'a'+10; if (('A'<=c)&&(c<='Z')) return c -‐ 'A'+10; if (('0'<=c)&&(c<='9')) return c -‐ '0'; return -‐1; } … #define char hexa[]="AF125C4D8E"; unsigned char key[MAX_KEY_LENGTH]; int keylen = strlen(hexa); char c1,c2; if ((keylen%2!=0)||(keylen/2 >
59
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
60
4.3 Mã hóa và giải mã Base64
Value Char Value Char Value Char
• Mã hóa Base64 Char
Value 16
0 A Q 32 g 48 w
1 B R 17 33 h 49 x
2 C S 18 34 i 50 y
3 D T 19 35 j 51 z
4 E U 20 36 k 52 0
5 F V 21 37 l 53 1
6 G W 22 38 m 54 2
7 H X 23 39 n 55 3
8 I Y 24 40 o 56 4
9 J Z 25 41 p 57 5
10 K a 26 42 q 58 6
11 L b 27 43 r 59 7
12 M c 28 44 s 60 8
13 N d 29 45 t 61 9
14 O e 30 46 u 62 +
61
15 P f 31 47 v 63 /
4.3 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
62
4.4 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[4]
Implementation
Notes
Brian Gladman's[6]
128 bits[5]
AES
14.1 cpb in asm, 22.6 cpb in C
The assembly version currently works only on Windows.
128 bits
41.3 cpb
AES
OpenSSL
Triple DES
192 bits[7]
108.2 cpb
OpenSSL
SNOW 2.0
128 or 256 bits
6.4 cpb
This implementation is written in C.
Fast reference implementation[8]
RC4
10.7 cpb
OpenSSL
Up to 256 bits (usually 128 bits)
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
23.2 cpb
OpenSSL
Up to 256 bits (usually 128 bits)
63
4.5 Mã hóa đối xứng với 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, Blow}ish, 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à }ile libcrypto.[so/
a], libssl.[so/a] và các }ile .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 http://www.ie7pro.com/ openssl.html
64
4.5 Mã hóa đối xứng với OpenSSL
• 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
đề
• 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.
65
4.5 Mã hóa đối xứng với OpenSSL
• VD
• Sinh key và iv bằng hàm EVP_BytesToKey
key[32]; iv[32]; * key_data = “nopass”;
char char char 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_CIPHER_CTX_init(&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_CIPHER_CTX_init(&d_ctx); EVP_DecryptInit_ex(&d_ctx, EVP_aes_256_cbc(),NULL, key, iv);
66
4.5 Mã hóa đối xứng với OpenSSL
• VD (tiếp)
• Mã hóa với ngữ cảnh đã được khởi tạo
plaintext=“Hello”; len = strlen(plaintext); ciphertext[1024]; c_len = 0, f_len = 0;
char * int char int /* 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); …
67
4.5 Mã hóa đối xứng với OpenSSL
• VD (tiếp)
• Giải mã với ngữ cảnh đã được khởi tạo
plaintext[1024]; p_len = 0;
char int /* 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);
68
4.6 Microsoft Crypto API
• 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 (Simpli}ied Message Functions). • Các hàm mã hóa thông điệp mức thấp (Low-‐Level Message Functions).
69
4.6 Microsoft Crypto API
• Thư viện CryptoAPI
70
4.6 Microsoft Crypto API
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp
Khởi tạo Provider
với thuật toán AES. • Tệp tiêu đề wincript.h • Thư viện Crypt32.lib • Trình tự sử dụng
Tạo khóa • Ngẫu nhiên • Từ mật khẩu • Từ bên ngoài
Đặt chế độ mã • CBC • ECB • …
Thực hiện Mã hóa/Giải mã
Thiết lập vector khởi tạo
71
4.6 Microsoft Crypto API
• 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
__in LPCTSTR pszContainer, __in LPCTSTR pszProvider, __in DWORD dwProvType, __in DWORD dwFlags );
hProvider;
0, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
return 0;
BOOL WINAPI CryptAcquireContext(__out HCRYPTPROV* phProv, VD: HCRYPTPROV if (!CryptAcquireContext(&hProvider,
72
4.6 Microsoft Crypto API
• 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) & 0xFFFF0000) | CRYPT_EXPORTABLE; if (!CryptGenKey(hProvider, CALG_AES_256, dwFlags, &hKey)) return 0;
73
4.6 Microsoft Crypto API
• Sử dụng thư viện CryptoAPI để thực hiện mã hóa đối xứng thông điệp
// Lưu Key // Lưu giá trị băm của mật khẩu
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; HCRYPTHASH hHash; 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;
{ }
74
// 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);
4.6 Microsoft Crypto API
• 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)
// Lưu kết quả // Lưu vector khởi tạo
75
BOOL bResult; BYTE *pbTemp; 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); LocalFree(pbTemp);
4.6 Microsoft Crypto API
• 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.
* pbData = "Hello CryptAPI"; * pbResult = 0;
// Giải thuật mã // Xâu nguồn cần mã // Xâu kết quả
ALG_ID Algid; char char 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;
76
4.6 Microsoft Crypto API
• 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, 0); // Lấy kích thước block theo bit
dwDataLen = sizeof(DWORD); ret = CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE*)&dwBlockLen, 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)) ;
}
77
4.6 Microsoft Crypto API
• 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)
// Mã hóa dòng
else {
// 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);
}
78
4.6 Microsoft Crypto API
• 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ụ
// Dữ liệu nguồn // Kích thước nguồn // Dữ liệu đích // Kích thước đích
char * pbData ; DWORD cbData; char * pbResult; DWORD dwDataLen; … // 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);
79
4.6 Microsoft Crypto API
• 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.
80