2017-2018-1 20155202 實驗五 固件程序設計
2017-2018-1 20155202 實驗五 固件程序設計
實驗內容
任務一
- 兩人一組
- 基於Socket實現TCP通信,一人實現服務器,一人實現客戶端
- 研究OpenSSL算法,測試對稱算法中的AES,非對稱算法中的RSA,Hash算法中的MD5
- 選用合適的算法,基於混合密碼系統實現對TCP通信進行機密性、完整性保護。
學有余力者,對系統進行安全性分析和改進。
任務二
在Ubuntu中實現對實驗二中的“wc服務器”通過混合密碼系統進行防護
SSL簡介:
- SSL是
Secure Sockets Layer(安全套接層協議)
的縮寫,可以在Internet上提供秘密性傳輸。Netscape公司在推出第一個Web瀏覽器的同時,提出了SSL協議標準。其目標是保證兩個應用間通信的保密性和可靠性,可在服務器端和用戶端同時實現支持。已經成為Internet上保密通訊的工業標準。 SSL能使用戶/服務器應用之間的通信不被攻擊者竊聽,並且始終對服務器進行認證,還可選擇對用戶進行認證。SSL協議要求建立在可靠的傳輸層協議(TCP)之上。SSL協議的優勢在於它是與應用層協議獨立無關的,高層的應用層協議(例如:HTTP,FTP,TELNET等)能透明地建立於SSL協議之上。SSL協議在應用層協議通信之前就已經完成加密算法、通信密鑰的協商及服務器認證工作。在此之後應用層協議所傳送的數據都會被加密,從而保證通信的私密性。
OpenSSL簡介
- OpenSSL 是一個安全套接字層密碼庫,囊括主要的密碼算法、常用的密鑰和證書封裝管理功能及SSL協議,並提供豐富的應用程序供測試或其它目的使用。
OpenSSL被曝出現嚴重安全漏洞後,發現多數通過SSL協議加密的網站使用名為OpenSSL的開源軟件包。OpenSSL漏洞不僅影響以https開頭的網站,黑客還可利用此漏洞直接對個人電腦發起“心臟出血”(Heartbleed)攻擊。據分析,Windows上有大量軟件使用了存在漏洞的OpenSSL代碼庫,可能被黑客攻擊抓取用戶電腦上的內存數據。
SSL和TLS協議
OpenSSL實現了SSL協議的SSLv2和SSLv3,支持了其中絕大部分算法協議。OpenSSL也實現了TLSv1.0,TLS是SSLv3的標準化版,雖然區別不大,但畢竟有很多細節不盡相同。
雖然已經有眾多的軟件實現了OpenSSL的功能,但是OpenSSL裏面實現的SSL協議能夠讓我們對SSL協議有一個更加清楚的認識,因為至少存在兩點:一是OpenSSL實現的SSL協議是開放源代碼的,我們可以追究SSL協議實現的每一個細節;二是OpenSSL實現的SSL協議是純粹的SSL協議,沒有跟其它協議(如HTTP)協議結合在一起,澄清了SSL協議的本來面目。對稱加密
OpenSSL一共提供了8種對稱加密算法,其中7種是分組加密算法,僅有的一種流加密算法是RC4。這7種分組加密算法分別是AES、DES、Blowfish、CAST、IDEA、RC2、RC5,都支持電子密碼本模式(ECB)、加密分組鏈接模式(CBC)、加密反饋模式(CFB)和輸出反饋模式(OFB)四種常用的分組密碼加密模式。其中,AES使用的加密反饋模式(CFB)和輸出反饋模式(OFB)分組長度是128位,其它算法使用的則是64位。事實上,DES算法裏面不僅僅是常用的DES算法,還支持三個密鑰和兩個密鑰3DES算法。
非對稱加密
OpenSSL一共實現了4種非對稱加密算法,包括DH算法、RSA算法、DSA算法和橢圓曲線算法(EC)。DH算法一般用於密鑰交換。RSA算法既可以用於密鑰交換,也可以用於數字簽名,當然,如果你能夠忍受其緩慢的速度,那麽也可以用於數據加密。DSA算法則一般只用於數字簽名。
信息摘要
OpenSSL實現了5種信息摘要算法,分別是MD2、MD5、MDC2、SHA(SHA1)和RIPEMD。SHA算法事實上包括了SHA和SHA1兩種信息摘要算法。此外,OpenSSL還實現了DSS標準中規定的兩種信息摘要算法DSS和DSS1。
OpenSSL整個軟件包大概可以分成三個主要的功能部分:
- 密碼算法庫
- SSL協議庫
- 應用程序
Linux下OpenSSL的安裝
環境
- OpenSSL最新版本下載:
http://www.openssl.org/source/
-
切記:下載第二個,不然會出錯
下載
安裝過程
Linux下的應用大多可以直接使用,也可以獲取源代碼自己進行編譯、安裝,使用源代碼安裝的過程一般是:
- config
- make
- make install
- OpenSSL的安裝首先解壓源代碼:
tar xzvf openssl-1.1.0-pre1.tar.gz
- 然後進入源代碼目錄:
cd openssl-1.1.0-pre1
- 然後使用下列命令編譯安裝:
- ./config
- make
- sudo make install
- 使用
make test
測試一下有沒有問題。
Linux下OpenSSL的使用
- OpenSSL應用程序
通過man openssl
查看幫助文檔。
OpenSSL密碼算法庫
編寫一個測試代碼
test_openssl.c
#include <stdio.h>
#include <openssl/evp.h>
int main(){
OpenSSL_add_all_algorithms();
return 0;
}
- 然後用下面命令編譯:
gcc -o to test_openssl.c -I /usr/local/ssl/inlcude /usr/local/ssl/lib -ldl -lpthread
執行
./to;echo $?
- 結果打印0
研究OpenSSL算法,測試對稱算法中的AES,非對稱算法中的RSA,Hash算法中的MD5
對稱加密:
使用的標準命令為 enc
openssl enc -ciphername [-in filename] [-out filename] [-pass arg] [-e] [-d] [-a/-base64]
[-A] [-k password] [-kfile filename] [-K key] [-iv IV] [-S salt] [-salt] [-nosalt] [-z] [-md]
[-p] [-P] [-bufsize number] [-nopad] [-debug] [-none] [-engine id]
-in filename:指定要加密的文件存放路徑
-out filename:指定加密後的文件存放路徑
-salt:自動插入一個隨機數作為文件內容加密,默認選項
-e:可以指明一種加密算法,若不指的話將使用默認加密算法
-d:解密,解密時也可以指定算法,若不指定則使用默認算法,但一定要與加密時的算法一致
-a/-base64:使用-base64位編碼格式
AES的用法如下:
openssl enc -aes-128-cbc -in plain.txt -out out.txt -pass pass:123456 //密碼123456
RSA 的用法如下:
openssl rsa [-inform PEM|NET|DER] [-outform PEM|NET|DER] [-in filename] [-passin arg] [-out filename] [-passout arg]
[-sgckey] [-des] [-des3] [-idea] [-text] [-noout] [-modulus] [-check] [-pubin] [-pubout] [-engine id]
常用選項:
-in filename:指明私鑰文件
-out filename:指明將提取出的公鑰保存至指定文件中
-pubout:根據私鑰提取出公鑰
MD5 的用法如下:
openssl passwd -1 -in test.txt -salt 12345678
生成密碼需要使用的標準命令為 passwd ,用法如下:
openssl passwd [-crypt] [-1] [-apr1] [-salt string] [-in file] [-stdin] [-noverify] [-quiet] [-table] {password}
常用選項有:
-1:使用md5加密算法
-salt string:加入隨機數,最多8位隨機數
-in file:對輸入的文件內容進行加密
-stdion:對標準輸入的內容進行加密
測試結果截圖:
任務二:
在Ubuntu中實現對實驗二中的“wc服務器”通過混合密碼系統進行防護
混合密碼系統示意圖如下所示
偽代碼:
服務器:
接收客戶端發來的會話密鑰密文 利用預先生成的RSA公鑰解密得到會話密鑰 接收客戶端發來的密文 用會話密鑰解密密文得到明文 將明文存入文件 調用mywc()函數計算文件中單詞數
客戶端:
利用c語言中的rand()函數生成32字節的偽隨機數數組構成會話密鑰
用預先生成的RSA私鑰加密會話密鑰
將會話密鑰發送給服務器
用會話密鑰加密指定文件
將密文發送給服務器
調用mywc()函數計算文件中單詞數
實驗步驟及思路:
- 頭文件:
#include <openssl/ssl.h>
#include <openssl/err.h>
- SSL庫初始化
SSL_library_init();
- 載入所有 SSL 算法
OpenSSL_add_all_algorithms();
- 載入所有 SSL 錯誤消息
SSL_load_error_strings();
- 產生一個 SSL_CTX
ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL) {
ERR_print_errors_fp(stdout);
exit(1);}
- 載入用戶的數字證書
if (SSL_CTX_use_certificate_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
exit(1);
}
- 載入用戶私鑰
if (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0){
ERR_print_errors_fp(stdout);
exit(1);
}
if (!SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stdout);
exit(1);
}
- 基於 ctx 產生一個新的 SSL,並將連接用戶的 socket 加入到 SSL
ssl = SSL_new(ctx);
SSL_set_fd(ssl, new_server_socket_fd);
- 建立 SSL 連接
if (SSL_accept(ssl) == -1) {
perror("accept");
close(new_fd);
break;
}
- SSL數據傳輸
int len = SSL_read(ssl, buffer, MAXBUF);
if (len > 0)
printf("接收消息成功:‘%s‘,共%d個字節的數據\n", buffer, len);
else
printf("消息接收失敗!錯誤代碼是%d,錯誤信息是‘%s‘\n",errno, strerror(errno));
客戶端與服務器傳輸完數據後,關閉 SSL 連接,釋放 SSL
對telent和server 進行編譯:SSL_shutdown(ssl); SSL_free(ssl); 釋放 CTX SSL_CTX_free(ctx);
實驗結果:
代碼托管
產品代碼:
- 服務器server1.c:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#define MAXBUF 1024
int main(int argc, char **argv)
{
int sockfd, new_fd;
socklen_t len;
struct sockaddr_in my_addr, their_addr;
unsigned int myport, lisnum;
char buf[MAXBUF + 1];
SSL_CTX *ctx;
if (argv[1])
myport = atoi(argv[1]);
else
myport = 7838;
if (argv[2])
lisnum = atoi(argv[2]);
else
lisnum = 2;
/* SSL 庫初始化 */
SSL_library_init();
/* 載入所有 SSL 算法 */
OpenSSL_add_all_algorithms();
/* 載入所有 SSL 錯誤消息 */
SSL_load_error_strings();
/* 以 SSL V2 和 V3 標準兼容方式產生一個 SSL_CTX ,即 SSL Content Text */
ctx = SSL_CTX_new(SSLv23_server_method());
/* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 單獨表示 V2 或 V3標準 */
if (ctx == NULL) {
ERR_print_errors_fp(stdout);
exit(1);
}
/* 載入用戶的數字證書, 此證書用來發送給客戶端。 證書裏包含有公鑰 */
if (SSL_CTX_use_certificate_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
exit(1);
}
/* 載入用戶私鑰 */
if (SSL_CTX_use_PrivateKey_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0){
ERR_print_errors_fp(stdout);
exit(1);
}
/* 檢查用戶私鑰是否正確 */
if (!SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stdout);
exit(1);
}
/* 開啟一個 socket 監聽 */
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
} else
printf("socket created\n");
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(myport);
my_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
== -1) {
perror("bind");
exit(1);
} else
printf("binded\n");
if (listen(sockfd, lisnum) == -1) {
perror("listen");
exit(1);
} else
printf("begin listen\n");
while (1) {
SSL *ssl;
len = sizeof(struct sockaddr);
/* 等待客戶端連上來 */
if ((new_fd =
accept(sockfd, (struct sockaddr *) &their_addr,
&len)) == -1) {
perror("accept");
exit(errno);
} else
printf("server: got connection from %s, port %d, socket %d\n",
inet_ntoa(their_addr.sin_addr),
ntohs(their_addr.sin_port), new_fd);
/* 基於 ctx 產生一個新的 SSL */
ssl = SSL_new(ctx);
/* 將連接用戶的 socket 加入到 SSL */
SSL_set_fd(ssl, new_fd);
/* 建立 SSL 連接 */
if (SSL_accept(ssl) == -1) {
perror("accept");
close(new_fd);
break;
}
/* 開始處理每個新連接上的數據收發 */
bzero(buf, MAXBUF + 1);
strcpy(buf, "server->client");
/* 發消息給客戶端 */
len = SSL_write(ssl, buf, strlen(buf));
if (len <= 0) {
printf
("消息‘%s‘發送失敗!錯誤代碼是%d,錯誤信息是‘%s‘\n",
buf, errno, strerror(errno));
goto finish;
} else
printf("消息‘%s‘發送成功,共發送了%d個字節!\n",
buf, len);
bzero(buf, MAXBUF + 1);
/* 接收客戶端的消息 */
len = SSL_read(ssl, buf, MAXBUF);
if (len > 0)
printf("接收消息成功:‘%s‘,共%d個字節的數據\n",
buf, len);
else
printf
("消息接收失敗!錯誤代碼是%d,錯誤信息是‘%s‘\n",
errno, strerror(errno));
/* 處理每個新連接上的數據收發結束 */
finish:
/* 關閉 SSL 連接 */
SSL_shutdown(ssl);
/* 釋放 SSL */
SSL_free(ssl);
/* 關閉 socket */
close(new_fd);
}
/* 關閉監聽的 socket */
close(sockfd);
/* 釋放 CTX */
SSL_CTX_free(ctx);
return 0;
}
telent1.c:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#define MAXBUF 1024
void ShowCerts(SSL * ssl)
{
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL) {
printf("數字證書信息:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("證書: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("頒發者: %s\n", line);
free(line);
X509_free(cert);
} else
printf("無證書信息!\n");
}
int main(int argc, char **argv)
{
int sockfd, len;
struct sockaddr_in dest;
char buffer[MAXBUF + 1];
SSL_CTX *ctx;
SSL *ssl;
if (argc != 3) {
printf("參數格式錯誤!正確用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用來從某個"
"IP 地址的服務器某個端口接收最多 MAXBUF 個字節的消息",
argv[0], argv[0]);
exit(0);
}
/* SSL 庫初始化,參看 ssl-server.c 代碼 */
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx == NULL) {
ERR_print_errors_fp(stdout);
exit(1);
}
/* 創建一個 socket 用於 tcp 通信 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket");
exit(errno);
}
printf("socket created\n");
/* 初始化服務器端(對方)的地址和端口信息 */
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(atoi(argv[2]));
if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
perror(argv[1]);
exit(errno);
}
printf("address created\n");
/* 連接服務器 */
if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
perror("Connect ");
exit(errno);
}
printf("server connected\n");
/* 基於 ctx 產生一個新的 SSL */
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
/* 建立 SSL 連接 */
if (SSL_connect(ssl) == -1)
ERR_print_errors_fp(stderr);
else {
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl);
}
/* 接收對方發過來的消息,最多接收 MAXBUF 個字節 */
bzero(buffer, MAXBUF + 1);
/* 接收服務器來的消息 */
len = SSL_read(ssl, buffer, MAXBUF);
if (len > 0)
printf("接收消息成功:‘%s‘,共%d個字節的數據\n",
buffer, len);
else {
printf
("消息接收失敗!錯誤代碼是%d,錯誤信息是‘%s‘\n",
errno, strerror(errno));
goto finish;
}
bzero(buffer, MAXBUF + 1);
strcpy(buffer, "from client->server");
/* 發消息給服務器 */
len = SSL_write(ssl, buffer, strlen(buffer));
if (len < 0)
printf
("消息‘%s‘發送失敗!錯誤代碼是%d,錯誤信息是‘%s‘\n",
buffer, errno, strerror(errno));
else
printf("消息‘%s‘發送成功,共發送了%d個字節!\n",
buffer, len);
finish:
/* 關閉連接 */
SSL_shutdown(ssl);
SSL_free(ssl);
close(sockfd);
SSL_CTX_free(ctx);
return 0;
}
實驗中出現的問題:
問題:實驗一編譯時候出錯:
解答:因為我並沒有把libcypto.a和libssl.a導入到lib文件夾下,通過參考狄維佳同學的博客成功導入兩個文件從而完成編譯
- 使用命令:
- 成功導入:
問題2:gcc -o to test_openssl.c -I /usr/local/ssl/inlcude /usr/local/ssl/lib -ldl -lpthread
中 -I是什麽意思?
解答:
-l參數就是用來指定程序要鏈接的庫,-l參數緊接著就是庫名。
放在/lib和/usr/lib和/usr/local/lib裏的庫直接用-l參數就能鏈接了。與-L命令的區別:
如果庫文件沒放在這三個目錄裏,而是放在其他目錄裏,鏈接程序ld在那3個目錄裏找不到libxxx.so,-L參數跟著的是庫文件所在的目錄名。
實驗體會:
本次實驗通過研究OpenSSL算法,測試對稱算法中的AES,非對稱算法中的RSA,Hash算法中的MD5,然後實現雙方通信,感覺越來越貼近生活了,我們日常生活中的qq微信等軟件應該也是以此為基礎進行通信才能保證其安全性,以後生活中再看到類似的東西都會想到這次的實驗。
2017-2018-1 20155202 實驗五 固件程序設計