1. 程式人生 > >2017-2018-1 20155202 實驗五 固件程序設計

2017-2018-1 20155202 實驗五 固件程序設計

要求 管理 地址 需要 filetype verify unsigned -c cnblogs

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
  1. OpenSSL的安裝首先解壓源代碼:

tar xzvf openssl-1.1.0-pre1.tar.gz

  • 技術分享圖片
  1. 然後進入源代碼目錄:

cd openssl-1.1.0-pre1

  1. 然後使用下列命令編譯安裝:
- ./config
- make
- sudo make install
  • 技術分享圖片
  1. 使用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()函數計算文件中單詞數

實驗步驟及思路:

  1. 頭文件:
#include <openssl/ssl.h>
#include <openssl/err.h>
  1. SSL庫初始化
SSL_library_init();
  1. 載入所有 SSL 算法
OpenSSL_add_all_algorithms();
  1. 載入所有 SSL 錯誤消息
SSL_load_error_strings();
  1. 產生一個 SSL_CTX
ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL) {
    ERR_print_errors_fp(stdout);
    exit(1);}
  1. 載入用戶的數字證書
if (SSL_CTX_use_certificate_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stdout);
    exit(1);
    }
  • 技術分享圖片
  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);
    }
  • 技術分享圖片
  1. 基於 ctx 產生一個新的 SSL,並將連接用戶的 socket 加入到 SSL
ssl = SSL_new(ctx);
SSL_set_fd(ssl, new_server_socket_fd);
  1. 建立 SSL 連接
if (SSL_accept(ssl) == -1) {
    perror("accept");
    close(new_fd);
    break;
    }
  1. 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));
  1. 客戶端與服務器傳輸完數據後,關閉 SSL 連接,釋放 SSL

    SSL_shutdown(ssl);
    SSL_free(ssl);
    釋放 CTX
    SSL_CTX_free(ctx);
    對telent和server 進行編譯:
  • 技術分享圖片
    實驗結果:
  • 技術分享圖片

代碼托管

  • 技術分享圖片

產品代碼:

  • 服務器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 實驗五 固件程序設計