Tải bản đầy đủ (.docx) (26 trang)

Tìm hiểu và viết phần mềm truyền dữ liệu giữa hai máy sử dụng kỹ thuật mã hóa IDEA

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (319.36 KB, 26 trang )

1

BỘ GIÁO DỤC VÀ ĐÀO TẠO
TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI

---------------------------------------

BÁO CÁO MÔN

AN NINH MẠNG
Đề tài : : Tìm hiểu và viết phần mềm truyền dữ
liệu giữa hai máy sử dụng kỹ thuật mã hóa IDEA

Giản viên hướng dẫn : Lê Xuân Thành
Sinh viên thực hiện

:

Hà Nội 5-2015


2

Mục Lục

I.

Lời mở đầu

Phương pháp IDEA (International Data Encryption Algorithm) là một
phương pháp mã khối. Được phát triển bởi Lai Học Gia và James L.Massey


của ETH Zurich và được công bố lần đầu tiên vào năm 1991. Phương pháp này
đưa ra như là một sự thay thế cho phương pháp cũ DEA (Data Encryption
Standard). Tiền thân của IDEA là phương pháp mã trước đó có tên
là PES (Proposed Encryption Standard); lúc đầu IDEA còn có tên là IPES
(Improved PES). Sau này IDEA đã trở thành tên thương mại. Nó sẽ hết hạn vào


3

nm 2010-2011. IDEA ó c xỏc nhp sau khi phiờn bn u tiờn V1.0 c
phỏt hin l khụng cũn an ton na.
IDEA l h mó thao tỏc trờn tng khi 64bit, mó húa cm rừ 64bit thnh
cm mó 64bit, s dng 128bit lm khúa bao gm 1 chui 8 ln bin i liờn
tip v tip theo l 1 ln bin i u ra. Ln bin i th r s dng 6 khi
khúa con 16 bit Ki(r), 1<=i<=6, bin i 64bit u vo X thnh 1 u ra
vi 4 khi 16bit v u ra ny li l u vo cho ln bin i tip theo. u
ra ca ln bin i th 8 l u vo cho ln bin i cui cựng l u ra ca
phng phỏp ny. Trong ln bin i ny nú dựng 4 khúa con Ki(9),
1<=i<=4, tớnh toỏn cm mó cui cựng Y= (Y1,Y2, Y3, Y4). Tt c cỏc
khúa con nhn c t K.Mt khỏi nim trong thit k mang tớnh t phỏ ca
phng phỏp IDEA l cỏch s dng hn n cỏc phộp toỏn t tp 3 phộp
toỏn i s khỏc nhau ca nhúm 2**n phn t. Cỏc phộp toỏn nhúm thc
hin trờn cỏc phn t biu din bng 16 bit a v b l: XOR theo tng bit: a ^
b; cng modulo 2**n: (a+b) mod 2**n, kớ hiu: a [+] b, v nhõn modulo
2**n+1, ký hiu a (*) b.
II.

Nhng c im chớnh
IDEA là phơng pháp mã khối sử dụng 128 bit khóa để mã khối dữ
liệu 64 bit. IDEA đợc xây dựng nhằm mục đích kết hợp với nhiều yếu tố

khác nhau để tăng độ an toàn và khả năng thực hiện.
* Độ an toàn:
- Độ dài của khối: khối phải có độ dài đủ để chống lại các phơng pháp
phân tích thống kê và ngăn việc một số khối nào đó xuất hiện nhiều hơn các
khối khác. Mặt khác sự phức tạp của thuật toán tăng theo hàm mũ với độ dài
khối. Với khối có độ dài 64 bit là đủ độ an toàn. Bên cạnh đó việc sử dụng
chế độ feedback sẽ làm tăng thêm độ an toàn của thuật toán.
- Độ dài khóa : Khóa phải đủ dài để có thể chống lại phơng pháp vét cạn
khóa.
- Độ phức tạp : Bản mã phải phụ thuộc một cách phức tạp vào bản rõ và
khóa. Mục tiêu đặt ra ở đây là phải làm phức tạp hóa sự phụ thuộc của bộ
mặt thống kê của bản mã vào bản rõ. IDEA đạt đợc điều này nhờ việc sử
dụng 3 phép toán sẽ trình bày sau đây.
- Sự phân bố : IDEA đã đạt đợc việc mỗi bit của bản rõ phải có ảnh hởng
đến nhiều bit của bản mã và mỗi bít khóa cũng tác động đến nhiều bit của
bản mã. Điều này làm cho cấu trúc của bản rõ sẽ bị phá vỡ trong bản mã.


4

III.

Cỏc phộp toỏn trong IDEA

- Phép XOR theo bit. Ký hiệu là - Phép cộng 2 số nguyên lấy modulo 216
(65536) với đầu vào và đầu ra là 2 số nguyên không dấu 16 bit. Ký hiệu . Phép nhân 2 số nguyên lấy modulo 216 + 1 với đầu vào và đầu ra là 2 số
nguyên không dấu 16 bit. Qui ớc là khối toàn số 0 biểu thị cho 216. Ký
hiệu .
Ba phép toán này thỏa mãn :
- Không có 2 phép toán nào thỏa mãn luật phân phối:

a ( b c ) (a b) (a c)
- Không có 2 phép toán nào thỏa mãn luật kết hợp:
a ( b c ) (a b) c
Việc sử dụng kết hợp 3 phép toán này tạo ra một sự biến đổi phức tạp dữ
liệu đầu vào làm cho việc mã thám trở nên khó khăn hơn so với việc chỉ sử
dụng một phép toán đơn giản.
Trong IDEA sự phân bố đợc tạo ra dựa trên khối thuật toán có cấu trúc
nh hình vẽ gọi là cấu trúc MA (Multiplication/Addition)

Khối này nhận 16 bit từ bản rõ và 16 bit đợc lấy từ khóa ra theo một
qui tắc nào đó ( 16 bit này đợc gọi là subkey và qui tắc lấy subkey từ khóa
sẽ đợc trình bày ở sau) để tạo ra 16 bit đầu ra. Một chơng trình kiểm tra
trên máy tính bằng phơng pháp vét cạn xác định rằng mỗi bit ở đầu ra phụ


5

thuộc vào các bit rõ và bit subkey đầu vào. Cấu trúc này đợc sử dụng lặp
lại 8 lần trong thuật toán và tạo nên một sự phân bố có hiệu quả.
IDEA đợc xây dựng sao cho việc thực hiện nó đợc dễ dàng cả trên
phần cứng và phần mềm. Việc thực hiện trên phần cứng, điển hình là trên vi
mạch VLSI, đợc thiết kế để đạt đợc tốc độ cao. Việc xây dựng trên phần
mềm thì thuận tiện và giá thành thấp.
- Những điểm chủ yếu trong việc xây dựng phần mềm:
+ Sử dụng những khối nhỏ: những phép toán mã thực hiện trên những khối
có độ dài 8, 16, 32 bit phù hợp với việc xử lý trên máy tính.
+ Sử dụng thuật toán giản đơn: Phép toán mã dễ dàng trong lập trình nh
phép cộng, phép dịch chuyển (shift),...Cả 3 phép toán của IDEA đều thỏa
mãn những yêu cầu này. Điểm khó khăn nhất là phép toán nhân modulo (216
+ 1) cũng có thể xây dựng dễ dàng từ những phép toán sẵn có.

- Những điểm chủ yếu trong việc thực hiện trên phần cứng:
+ Sự tơng tự trong mã hóa và giải mã: Mã hóa và giải mã chỉ khác nhau
trong việc sử dụng khóa và nhờ đó một phơng tiện có thể dùng cho cả mã
hóa và giải mã.
+ Cấu trúc lặp lại: Phơng pháp mã nên có cấu trúc modul lặp lại để các
mạch VLSI có thể thực hiện đợc dễ dàng. IDEA đợc xây dựng từ hai khối
modulo đơn giản và sử dụng lặp lại nhiều lần.
IV.

Mó húa v gii mó trong IDEA


6

Mã hóa và giải mã trong IDEA
IV.1. Mã hóa


7

Cu trỳc ca IDEA
Giống nh các sơ đồ mã hóa khác, hàm mã hóa có 2 tham số ở đầu
vào là bản rõ cần mã và khóa. Trong trừơng hợp này là 64 bit rõ và 128 bit
khóa.
Từ đầu vào đến đầu ra, các bit rõ lần lợt đi qua 8 modul và một hàm
biến đổi cuối cùng. Tám modul này có cấu trúc giống nhau và thực hiện
các thao tác nh nhau đối với dữ liệu đầu vào. Mỗi modul nhận 4 khối 16
bit rõ ở đầu vào cùng với các subkey và đa ra 4 khối 16 bit đã đợc mã
hóa. Do đó 64 bit rõ sẽ đợc chia thành 4 khối nhỏ gọi là các subblock,
mỗi subblock là 16 bit. Cùng với các subblock này là 6 khối subkey cũng

sẽ đợc đa vào từng modul. Nh vậy thêm 4 subkey cần thiết cho hàm
biến đổi cuối cùng, ta cần tổng cộng 52 khối subkey cho một lần mã.


8

Cu trỳc 1 modulo
Nh đã trình bầy ở trên, các modul có cấu trúc giống nhau và chỉ khác
nhau ở dữ liệu đầu vào. Trừ modul đầu tiên nhận 64 bit rõ đa từ ngoài
vào, các modul đứng sau sẽ nhận 4 khối subblock 16 bit đầu ra của modul
đứng trớc nó làm các bit rõ đầu vào. Trong quá trình đầu tiên các modul
kết hợp 4 subblock với 4 subkey bằng các phép toán. Bốn khối đầu ra của
quá trình này XOR với nhau nh trong sơ đồ để tạo ra 2 khối đầu vào cho
cấu trúc MA và cấu trúc MA sẽ kết hợp chúng với 2 subkey còn lại để tạo
ra 2 khối 16 bit mới.
Cuối cùng, 4 khối đợc tạo ra từ quá trình đầu tiên sẽ đợc XOR với 2
khối đầu ra của cấu trúc MA để tạo ra 4 khối đầu ra của modul. Chú ý 2


9

khối đầu vào X2 và X3 đơc hoán đổi cho nhau để tạo ra 2 khối W12 và
W13 đợc đa ra ngoài. Điều này làm tăng sự hòa trộn của các bit đợc
xử lý và tăng khả năng chống lại các phơng pháp mã thám.
Hàm biến đổi ở cuối cùng ta cũng có thể coi nh là một modul thứ 9. Hàm
này có cấu trúc giống nh cấu trúc đã thực hiện trong quá trình đầu tiên
của một modul chỉ khác là khối thứ 2 và thứ 3 ở đầu vào đựơc đổi chỗ cho
nhau trớc khi đc đa tới các đơn vị phép toán. Thực ra đây chỉ là việc
trả lại thứ tự đã bị đổi sau modul thứ 8. Lý do của việc này là sự giống
nhau về cấu trúc của quá trình giải mã quá trình mã hóa.


Hm bin i ca IDEA
*Qui tắc tạo ra subkey:
Nh trên đã trình bày, cần thiết phải có 52 khối subkey 16 bit đợc tạo ra
từ 128 bit khóa. Qui tắc tạo nh sau:
- 8 subkey đầu tiên, Z1...Z8, đợc lấy trực tiếp từ khóa với Z1 là 16 bit đầu
(bit có trọng số cao nhất), Z2 là 16 bit tiếp theo và cứ tiếp tục nh vậy. Sau
đó khóa đợc quay trái 25 bit và 8 subkey tiếp theo đợc tạo ra theo qui
tắc trên. Thao tác này đợc lặp lại cho đến khi có đủ 52 khối subkey.
Qui tắc này là một phơng pháp hiệu quả cho việc đa dạng hóa các bit
khóa dùng cho các modul. Ta nhận thấy rằng những subkey đầu tiên dùng
trong mỗi modul sử dụng những tập hợp bit khác nhau của khóa. Nếu nh
khóa 128 bit đợc ký hiệu là Z[1..128] thì subkey đầu tiên của 8 modul sẽ
là:


10

Z1 = Z[1..16]
Z25 = Z[76..91] Z7
= Z[97..112]
Z31 = Z[44..59] Z13 =
Z[90..105]
Z37 = Z[37..52] Z19 =
Z[83..98]
Z43 = Z[30..45] Nh vậy,
96 bit subkey sử dụng cho mỗi modul, trừ modul thứ nhất và modul thứ 8,
là không liên tục. Do đó không có một mối liên hệ dịch chuyển đơn giản
nào giữa các subkey của một modul và giữa các modul với nhau. Nguyên
nhân có đợc kết quả này là việc chỉ có 6 khối subkey đợc sử dụng trong

khi có 8 khối subkey đợc tạo ra trong mỗi lần dịch chuyển khóa.

IV.2. Gii mó
Quá trình giải mã về cơ bản giống quá trình mã hóa. Giải mã nhận
bản mã ở đầu vào và cũng đi qua những cấu trúc nh ở trên, chỉ khác ở sự
lựa chọn các subkey. Các subkey để giải mã U1, U2,...U52 nhận đợc từ
khóa mã theo qui tắc sau:
- Đối với modul giải mã i ta lấy 4 subkey đầu của modul mã hóa thứ (10-i),
ở đây hàm biến đổi đợc coi nh modul thứ 9. Sau đó lấy nhân đảo
modulo (216 + 1) của subkey thứ 1 và thứ 4 để dùng cho subkey giải mã thứ
1 và thứ 4 tơng ứng. Đối với các modul từ thứ 2 đến thứ 8, subkey giải mã
thứ 2 và thứ 3 là cộng đảo modulo 216 của subkey thứ 3 và thứ 2 tơng ứng.
Đối với các modul thứ 1 và thứ 9, subkey giải mã thứ 2 và thứ 3 là cộng
đảo modulo 216 của subkey thứ 2 và thứ 3 tơng ứng.
- Đối với 8 modul đầu tiên, 2 subkey cuối của modul i là 2 subkey cuối của
modul mã hóa thứ (9 - i).
đây nhân đảo Zj-1 của Zj là phần tử nghịch đảo của Zj đối với phép
toán nhân tức:
Z j Zj-1 = 1
Vì 216 + 1 là một số nguyên tố nên mỗi số nguyên Zj < 216 có một số
nhân đảo modulo (216 +1) duy nhất. Với cộng đảo modulo 216 thì:
-Z j + Zj = 0


11

Hình vẽ sau thể hiện quá trình mã hóa (theo chiều đi xuống bên trái)
và quá trình giải mã (chiều đi lên bên phải) của thuật toán IDEA.
Mỗi modul đợc chia thành 2 khối nhỏ : khối biến đổi và khối mã
hóa. Khối biến đổi tơng ứng với quá trình đầu tiên trong mỗi modul, còn

khối mã hóa tơng ứng với các quá trình còn lại. ở phía cuối của sơ đồ, bên
mã hóa ta nhận đợc các mối quan hệ sau giữa đầu ra và đầu vào của hàm
biến đổi:
Y1 = W81 Z49
Y3 = W82 + Z51
Y2 = W83 + Z50
Y4 = W84 Z52
Tại khối biến đổi của modul thứ nhất trong quá trình giải mã, đầu ra
và đầu vào có mối quan hệ sau:
J11 = Y1 U1
J13 = Y3 + U3
J12 = Y2 + U2
J14 = Y4 U4
Ta có: J11 = Y1 Z49-1
= W81 Z49 Z49-1
= W81
J12 = Y2 + - Z50
= W83 + Z50 + -Z50
= W83
J13 = Y3 + - Z51
= W82 + Z51 + -Z51
= W82
J14 = Y4 Z50-1
= W84 Z50 Z50-1 = W84


12

Nh vậy, kết quả thu đợc sau khối biến đổi thứ nhất của quá trình
giải mã chính là dữ liệu rõ đa vào khối mã hóa cuối cùng của quá trình

mã hóa chỉ khác là khối dữ liệu thứ 2 và khối dữ liệu thứ 3 đã đổi chỗ cho
nhau. Bây giờ ta sẽ xét đến mối quan hệ thu đợc theo sơ đồ 711:
W81 = I81 + MAR(I81 + I83, I82 + I84 )
W82 = I83 + MAR(I81 + I83, I82 + I84 )
W83 = I82 + MAR(I81 + I83, I82 + I84 )
W84 = I84 + MAR(I81 + I83, I82 + I84 )
trong đó MAR(X,Y) là đầu ra phía bên phải còn MAL(X,Y) là đầu
ra phía bên trái của cấu trúc MA trong hình 79 khi đầu vào là X và Y. Và:
V11 = J11 + MAR(J11 + J13, J12 + J14 )
=W81 + MAR(W81 + W82, W83 + W84 )
=I81 + MAR(I81 + I83, I82 + I84 ) +
MAR[I81+MAR(I81+I83,I82+I84)+I83+MAR(I81+I83,I82+I84 ),
I82+MAL(I81+I83,I82 +I84) +I84+MAL(I81+I83, I82 + I84 )]
= I81+MAR(I81+I83,I82 +I84) +MAR(I81+I83, I82 + I84 )
= I81
Tơng tự ta có:
V12 = I82
V13 = I83
V14 = I84
Nh vậy, kết quả thu đợc sau khối mã hóa thứ nhất của quá trình
giải mã lại là dữ liệu đa vào khối biến đổi của modul cuối cùng của quá
trình mã hóa chỉ khác là khối dữ liệu thứ 2 và khối dữ liệu thứ 3 đã đổi chỗ
cho nhau. Cứ nh vậy, ta sẽ thu đợc:
V81 = I11
V82 = I13
V83 = I12


13


V84 = I14
Vì hàm biến đổi cuối cùng của quá trình giải mã cũng giống nh
khối biến đổi trong modul đầu tiên của quá trình mã hóa chỉ khác là có đổi
chỗ của khối dữ liệu thứ 2 và khối dữ liệu thứ 3 nên ta có bản rõ thu đợc
sau giải mã giống bản rõ đa vào mã hóa.

V.

Phn mm th nghim

V.1. Client
#define WIN32_LEAN_AND_MEAN
#include "stdafx.h"
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <idea.h>
#include "osrng.h"
using CryptoPP::AutoSeededRandomPool;
#include <conio.h>
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
#include <string>
using std::string;
#include <cstdlib>

using std::exit;
#include "cryptlib.h"
using CryptoPP::Exception;
#include "hex.h"
using CryptoPP::HexEncoder;


14

using CryptoPP::HexDecoder;
#include "filters.h"
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::StreamTransformationFilter;
#include "idea.h"
using CryptoPP::IDEA;
#include "modes.h"
using CryptoPP::CBC_Mode;
#include "secblock.h"
using CryptoPP::SecByteBlock;
// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int __cdecl main(int argc, char **argv)
{
AutoSeededRandomPool prng;
SecByteBlock

key(IDEA::DEFAULT_KEYLENGTH),keyTest(IDEA::DEFAULT_KEYLE
NGTH);
prng.GenerateBlock(key, key.size());
byte keyB[IDEA::DEFAULT_KEYLENGTH],
cipherB[IDEA::DEFAULT_KEYLENGTH];
for (int i = 0; i < IDEA::DEFAULT_KEYLENGTH; i++)
{
keyB[i] = key[i];
}
byte iv[IDEA::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));


15

string keyS, ivS, cipherS;
// Xau ki tu can ma hoa
string plain = "Hello World!!!";
string cipher, encoded, recovered;
char *keyC, *ivC;
const char *cipherC;
/*********************************\
\*********************************/
keyC = (char*)&keyB;
ivC = (char*)&iv;

// Pretty print key
encoded.clear();
StringSource(key, key.size(), true,
new HexEncoder(

new StringSink(keyS)
) // HexEncoder
); // StringSource
cout << "key: " << keyS << endl;
encoded.clear();
StringSource(iv, sizeof(iv), true,
new HexEncoder(
new StringSink(ivS)
) // HexEncoder
); // StringSource
/*********************************\
\*********************************/
try
{
cout << "plain text: " << plain << endl;
CBC_Mode< IDEA >::Encryption e;
e.SetKeyWithIV(key, key.size(), iv);


16

// The StreamTransformationFilter adds padding
// as required. ECB and CBC Mode must be padded
// to the block size of the cipher.
StringSource(plain, true,
new StreamTransformationFilter(e,
new StringSink(cipher)
) // StreamTransformationFilter
); // StringSource
cipherC = cipher.c_str();

}
catch(const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
exit(1);
}
/*********************************\
\*********************************/
// Pretty print
encoded.clear();
StringSource(cipher, true,
new HexEncoder(
new StringSink(encoded)
) // HexEncoder
); // StringSource
/*********************************\
\*********************************/

////////////////////////////////////
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int iResult;
int recvbuflen = DEFAULT_BUFLEN;



17

// Validate the parameters
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port
iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
if ( iResult != 0 ) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Attempt to connect to an address until one succeeds
for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {
// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}

// Connect to server.
iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;


18

}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
// Send an initial buffer
iResult = send( ConnectSocket, keyC, IDEA::DEFAULT_KEYLENGTH,
0 );
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
iResult = send( ConnectSocket, ivC, IDEA::BLOCKSIZE, 0 );
if (iResult == SOCKET_ERROR) {

printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
iResult = send( ConnectSocket, cipherC,
IDEA::DEFAULT_KEYLENGTH, 0 );
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// shutdown the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());


19

closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// Receive until the peer closes the connection
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if ( iResult > 0 )

printf("Bytes received: %d\n", iResult);
else if ( iResult == 0 )
printf("Connection closed\n");
else
printf("recv failed with error: %d\n", WSAGetLastError());
} while( iResult > 0 );
// cleanup
closesocket(ConnectSocket);
WSACleanup();
_getch();
return 0;
}

V.2. Server
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#include "stdafx.h"
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include "osrng.h"
using CryptoPP::AutoSeededRandomPool;


20

#include <iostream>

using std::cout;
using std::cerr;
using std::endl;
#include <string>
using std::string;
#include <cstdlib>
using std::exit;
#include "cryptlib.h"
using CryptoPP::Exception;
#include "hex.h"
using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;
#include "filters.h"
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::StreamTransformationFilter;
#include "idea.h"
using CryptoPP::IDEA;
#include "modes.h"
using CryptoPP::CBC_Mode;
#include "secblock.h"
using CryptoPP::SecByteBlock;
// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int __cdecl main(void)
{
SecByteBlock key(IDEA::DEFAULT_KEYLENGTH);

byte iv[IDEA::BLOCKSIZE];
byte cipher[IDEA::DEFAULT_KEYLENGTH];


21

char **data;
string cipherS;
/////////////////////////////////
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port

iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if ( iResult != 0 ) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result>ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();


22

return 1;
}
// Setup the TCP listening socket
iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {

printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// No longer need server socket
closesocket(ListenSocket);
// Receive until the peer shuts down the connection
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
// Echo the buffer back to the sender
iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
if (iSendResult == SOCKET_ERROR) {


23

printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;

}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
printf("Connection closing...\n");
else {
printf("recv failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (iResult > 0);
for (int i = 0; i < 16; i++)
{
key[i] = (byte) recvbuf[i];
}
string keyS;
StringSource(key, key.size(), true,
new HexEncoder(
new StringSink(keyS)
) // HexEncoder
); // StringSource
cout << "key: " << key << endl;
cout << "key: " << keyS << endl;
for (int i = 16;i< 24; i++)
{
iv[i-16] = (byte) recvbuf[i];
}
string ivS;
StringSource(iv, sizeof(iv), true,

new HexEncoder(
new StringSink(ivS)
) // HexEncoder
); // StringSource


24

cout << "iv: " << iv << endl;
cout << "iv: " << ivS << endl;
for (int i = 24; i < 40; i++)
{
cipher[i-24] = (byte) recvbuf[i];
}
string encoded;
encoded.clear();
StringSource(cipher,sizeof(cipher), true,
new HexEncoder(
new StringSink(cipherS)
) // HexEncoder
); // StringSource
cout << "cipher: " << cipher << endl;
cout << "cipher: " << cipherS << endl;

////////////////////////////
string recovered;
try
{
CBC_Mode< IDEA >::Decryption d;
d.SetKeyWithIV(key, key.size(), iv);

// The StreamTransformationFilter removes
// padding as required.
StringSource s(cipher, sizeof(cipher), true,
new StreamTransformationFilter(d,
new StringSink(recovered)
) // StreamTransformationFilter
); // StringSource
cout << "recovered text: " << recovered << endl;
}
catch(const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
exit(1);
}
// shutdown the connection since we're done


25

iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ClientSocket);
WSACleanup();
_getch();

return 0;
}

V.3. Thử nghiệm chương trình
Bước 1: Khởi động server
Bước 2: Khởi động client
-

Xâu ký tự cần mã hóa có thể thay đổi trong mã nguồn chương trình.

Ở đây ta có xâu ký tự: “Hello World!!!”
Kết quả:
-

Client:

-

Server:


×