2017-2018-1 20155330 實驗五 通信協議設計
2017-2018-1 20155330 實驗五 通信協議設計
任務一
在Ubuntu中完成 http://www.cnblogs.com/rocedu/p/5087623.html 中的作業
實驗步驟
安裝OpenSSL
- 在瀏覽器中打開網址
http://www.openssl.org/source/
,找到,安裝1.0.2版本。- PS:在獲得下載安裝包地址後,也可使用
wget 網址
下載安裝包。
- PS:在獲得下載安裝包地址後,也可使用
下載安裝包完成後,首先解壓源代碼
$ tar xzvf openssl-1.0.2n.tar.gz
- 進入
openssl-1.0.2n
文件夾
$ cd openssl-1.0.2n
編譯安裝
$ ./config $ make $ make install
安裝測試
編寫測試代碼test_openssl.c
#include <stdio.h> #include <openssl/evp.h> int main(){ OpenSSL_add_all_algorithms(); return 0; }
- 執行命令
gcc -o to test_openssl test_openssl.c -I /usr/local/ssl/include /usr/local/ssl/lib/libcrypto.a /usr/local/ssl/lib/libssl.a -ldl -lpthread
編譯test_openssl.c文件並生成可執行文件test_openssl
- 執行命令
./to;echo $?
運行結果:
作業
研究OpenSSL算法,測試對稱算法中的AES,非對稱算法中的RSA,Hash算法中的MD5
- AES算法
測試命令:
openssl enc -aes-128-cbc -in plain.txt -out encrypt.txt -pass pass:123456 -p
- RSA算法
測試命令:
openssl genrsa -out rsa_private_key.pem 1024
生成一個沒有加密的ca私鑰
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
openssl rsautl -encrypt -in readme.txt -inkey a_private_key.pem -out hello.en
加密(加密的內容寫在了readme.txt文件中,內容為20155321lrt)
openssl rsautl -decrypt -in hello.en -inkey a_private_key.pem -out hello.de
解密
cat hello.de
查看解密後的結果,發現與剛開始卸載readme.txt中的內容一致
- 測試MD5算法
使用
echo "20155330" | openssl dgst -md5
用MD5算法加密信息"20155330"
任務二-
- 在Ubuntu中實現對實驗二中的“wc服務器”通過混合密碼系統進行防護
- 服務器端
#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;
}
- 客戶端
#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;
}
- 運行結果:
新學到的知識點
OpenSSL
OpenSSL 是一個安全套接字層密碼庫,囊括主要的密碼算法、常用的密鑰和證書封裝管理功能及SSL協議,並提供豐富的應用程序供測試或其它目的使用。
SSL是利用公開密鑰的加密技術(RSA)來作為用戶端與服務器端在傳送機密資料時的加密通訊協定。
功能
1.基本功能
OpenSSL整個軟件包大概可以分成三個主要的功能部分:
- 密碼算法庫
- SSL協議庫
- 應用程序
OpenSSL的目錄結構自然也是圍繞這三個功能部分進行規劃的。
2.輔助功能
BIO機制是OpenSSL提供的一種高層IO接口,該接口封裝了幾乎所有類型的IO接口,如內存訪問、文件訪問以及Socket等。這使得代碼的重用性大幅度提高,OpenSSL提供API的復雜性也降低了很多。
OpenSSL對於隨機數的生成和管理也提供了一整套的解決方法和支持API函數。隨機數的好壞是決定一個密鑰是否安全的重要前提。
OpenSSL還提供了其它的一些輔助功能,如從口令生成密鑰的API,證書簽發和管理中的配置文件機制等等。
密碼證書管理
密鑰和證書管理是PKI的一個重要組成部分,OpenSSL為之提供了豐富的功能,支持多種標準。
OpenSSL實現了ASN.1的證書和密鑰相關標準,提供了對證書、公鑰、私鑰、證書請求以及CRL等數據對象的DER、PEM和BASE64的編解碼功能。OpenSSL提供了產生各種公開密鑰對和對稱密鑰的方法、函數和應用程序,同時提供了對公鑰和私鑰的DER編解碼功能。並實現了私鑰的PKCS#12和PKCS#8的編解碼功能。OpenSSL在標準中提供了對私鑰的加密保護功能,使得密鑰可以安全地進行存儲和分發。
在此基礎上,OpenSSL實現了對證書的X.509標準編解碼、PKCS#12格式的編解碼以及PKCS#7的編解碼功能。並提供了一種文本數據庫,支持證書的管理功能,包括證書密鑰產生、請求產生、證書簽發、吊銷和驗證等功能。
對稱密碼
OpenSSL一共提供了8種對稱加密算法,其中7種是分組加密算法,僅有的一種流加密算法是RC4。這7種分組加密算法分別是AES、DES、Blowfish、CAST、IDEA、RC2、RC5,都支持電子密碼本模式(ECB)、加密分組鏈接模式(CBC)、加密反饋模式(CFB)和輸出反饋模式(OFB)四種常用的分組密碼加密模式。其中,AES使用的加密反饋模式(CFB)和輸出反饋模式(OFB)分組長度是128位,其它算法使用的則是64位。
非對稱密碼
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相關命令
OpenSSL有兩種運行模式:交互模式和批處理模式。 直接輸入openssl回車進入交互模式,輸入帶命令選項的openssl進入批處理模式。
- 常用command:
名稱 | 功能 |
---|---|
version | 用於查看版本信息 |
enc | 用於加解密 |
ciphers | 列出加密套件 |
genrsa | 用於生成私鑰 |
rsa | RSA密鑰管理(例如:從私鑰中提取公鑰) |
req | 生成證書簽名請求(CSR) |
crl | 證書吊銷列表(CRL)管理 |
ca | CA管理(例如對證書進行簽名) |
dgst | 生成信息摘要 |
rsautl | 用於完成RSA簽名、驗證、加密和解密功能 |
passwd | 生成散列密碼 |
rand | 生成偽隨機數 |
speed | 用於測試加解密速度 |
s_client | 通用的SSL/TLS客戶端測試工具 |
X509 | X.509證書管理 |
verify | X.509證書驗證 |
pkcs7 | PKCS#7協議數據管理 |
- 應用例子(RSA)
產生1024位RSA私匙,用3DES加密它,口令為trousers,輸出到文件rsaprivatekey.pem
openssl genrsa -out rsaprivatekey.pem -passout pass:trousers -des3 1024
從文件rsaprivatekey.pem讀取私匙,用口令trousers解密,生成的公鑰匙輸出到文件rsapublickey.pem
openssl rsa -in rsaprivatekey.pem -passin pass:trousers -pubout -out rsapubckey.pem
用公鑰匙rsapublickey.pem加密文件plain.txt,輸出到文件cipher.txt
openssl rsautl -encrypt -pubin -inkey rsapublickey.pem -in plain.txt -out cipher.txt
使用私鑰匙rsaprivatekey.pem解密密文cipher.txt,輸出到文件plain.txt
openssl rsautl -decrypt -inkey rsaprivatekey.pem -in cipher.txt -out plain.txt
用私鑰匙rsaprivatekey.pem給文件plain.txt簽名,輸出到文件signature.bin
openssl rsautl -sign -inkey rsaprivatekey.pem -in plain.txt -out signature.bin
用公鑰匙rsapublickey.pem驗證簽名signature.bin,輸出到文件plain.txt
openssl rsautl -verify -pubin -inkey rsapublickey.pem -in signature.bin -out plain
遇到的問題
- 問題1:沒有頭文件
- 解決方案:安裝libssh-dev,安裝命令為
sudo apt-get install libssh-dev
參考資料
openssl命令
linux下openssl命令詳解
2017-2018-1 20155330 實驗五 通信協議設計