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