1. 程式人生 > >2017-2018-1 20155330 實驗五 通信協議設計

2017-2018-1 20155330 實驗五 通信協議設計

套接字 ext pub 輸出 連接 用戶 程序 %d 輔助

2017-2018-1 20155330 實驗五 通信協議設計

任務一

  • 在Ubuntu中完成 http://www.cnblogs.com/rocedu/p/5087623.html 中的作業

    實驗步驟

    安裝OpenSSL

  • 在瀏覽器中打開網址http://www.openssl.org/source/,找到,安裝1.0.2版本。
    • PS:在獲得下載安裝包地址後,也可使用wget 網址下載安裝包。
  • 下載安裝包完成後,首先解壓源代碼

    $ 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 實驗五 通信協議設計