1. 程式人生 > >2018-2019-1 20165227 20165228 20165237 實驗五 通訊協議設計

2018-2019-1 20165227 20165228 20165237 實驗五 通訊協議設計

2018-2019-1 20165227 20165228 20165237 實驗五 通訊協議設計

##Linux下OpenSSL的安裝與使用 1.兩人一組 2.基於Socket實現TCP通訊,一人實現伺服器,一人實現客戶端 3.研究OpenSSL演算法,測試對稱演算法中的AES,非對稱演算法中的RSA,Hash演算法中的MD5 4.選用合適的演算法,基於混合密碼系統實現對TCP通訊進行機密性、完整性保護。 ###實驗步驟

  • OpenSSL下載地址下載OpenSSL
  • 解壓OpenSSL原始碼 tar xzvf openssl-1.1.0j.tar.gz
  • 進入原始碼目錄後
$ ./config
$ make
$ make test
$ make install
  • 編寫測試程式碼 test_openssl.c
#include <stdio.h>
#include <openssl/evp.h>
int main(){
    OpenSSL_add_all_algorithms();
    return 0;
}
  • 編譯和執行
**L**指定連結庫的資料夾地址;
**lcrypto**匯入OpenSSL所需包;
**ldl**載入動態庫;
**lpthread**:連結POSIX thread庫
gcc -o test_openssl test_openssl.c -L/usr/local/ssl/lib -lcrypto -ldl -lpthread
echo $?

結果列印0則表示安裝成功

####實現TCP通訊 server.c:

#include<stdlib.h>

#include<pthread.h>

#include<sys/socket.h>

#include<sys/types.h>       //pthread_t , pthread_attr_t and so on.

#include<stdio.h>

#include<netinet/in.h>      //structure sockaddr_in

#include<arpa/inet.h>       //Func : htonl; htons; ntohl; ntohs

#include<assert.h>          //Func :assert

#include<string.h>          //Func :memset

#include<unistd.h>          //Func :close,write,read

#define SOCK_PORT 9988

#define BUFFER_LENGTH 1024

#define MAX_CONN_LIMIT 512     //MAX connection limit



static void Data_handle(void * sock_fd);   //Only can be seen in the file



int main()

{

    int sockfd_server;

    int sockfd;

    int fd_temp;

    struct sockaddr_in s_addr_in;

    struct sockaddr_in s_addr_client;

    int client_length;



    sockfd_server = socket(AF_INET,SOCK_STREAM,0);  //ipv4,TCP

    assert(sockfd_server != -1);



    //before bind(), set the attr of structure sockaddr.

    memset(&s_addr_in,0,sizeof(s_addr_in));

    s_addr_in.sin_family = AF_INET;

    s_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);  //trans addr from uint32_t host byte order to network byte order.

    s_addr_in.sin_port = htons(SOCK_PORT);          //trans port from uint16_t host byte order to network byte order.

    fd_temp = bind(sockfd_server,(struct scokaddr *)(&s_addr_in),sizeof(s_addr_in));

    if(fd_temp == -1)

    {

        fprintf(stderr,"bind error!\n");

        exit(1);

    }



    fd_temp = listen(sockfd_server,MAX_CONN_LIMIT);

    if(fd_temp == -1)

    {

        fprintf(stderr,"listen error!\n");

        exit(1);

    }



    while(1)

    {

        printf("waiting for new connection...\n");

        pthread_t thread_id;

        client_length = sizeof(s_addr_client);



        //Block here. Until server accpets a new connection.

        sockfd = accept(sockfd_server,(struct sockaddr_*)(&s_addr_client),(socklen_t *)(&client_length));

        if(sockfd == -1)

        {

            fprintf(stderr,"Accept error!\n");

            continue;                               //ignore current socket ,continue while loop.

        }

        printf("A new connection occurs!\n");

        if(pthread_create(&thread_id,NULL,(void *)(&Data_handle),(void *)(&sockfd)) == -1)

        {

            fprintf(stderr,"pthread_create error!\n");

            break;                                  //break while loop

        }

    }



    //Clear

    int ret = shutdown(sockfd_server,SHUT_WR); //shut down the all or part of a full-duplex connection.

    assert(ret != -1);



    printf("Server shuts down\n");

    return 0;

}



static void Data_handle(void * sock_fd)

{

    int fd = *((int *)sock_fd);

    int i_recvBytes;

    char data_recv[BUFFER_LENGTH];

    const char * data_send = "Server has received your request!\n";



    while(1)

    {

        printf("waiting for request...\n");

        //Reset data.

        memset(data_recv,0,BUFFER_LENGTH);



        i_recvBytes = read(fd,data_recv,BUFFER_LENGTH);

        if(i_recvBytes == 0)

        {

            printf("Maybe the client has closed\n");

            break;

        }

        if(i_recvBytes == -1)

        {

            fprintf(stderr,"read error!\n");

            break;

        }

        if(strcmp(data_recv,"quit")==0)

        {

            printf("Quit command!\n");

            break;                           //Break the while loop.

        }

        printf("read from client : %s\n",data_recv);

        if(write(fd,data_send,strlen(data_send)) == -1)

        {

            break;

        }

    }



    //Clear

    printf("terminating current client_connection...\n");

    close(fd);            //close a file descriptor.

    pthread_exit(NULL);   //terminate calling thread!

}

client.c:

#include<stdlib.h>

#include<sys/socket.h>

#include<sys/types.h>       //pthread_t , pthread_attr_t and so on.

#include<stdio.h>

#include<netinet/in.h>      //structure sockaddr_in

#include<arpa/inet.h>       //Func : htonl; htons; ntohl; ntohs

#include<assert.h>          //Func :assert

#include<string.h>          //Func :memset

#include<unistd.h>          //Func :close,write,read

#define SOCK_PORT 9988

#define BUFFER_LENGTH 1024

int main()

{

    int sockfd;

    int tempfd;

    struct sockaddr_in s_addr_in;

    char data_send[BUFFER_LENGTH];

    char data_recv[BUFFER_LENGTH];

    memset(data_send,0,BUFFER_LENGTH);

    memset(data_recv,0,BUFFER_LENGTH);



    sockfd = socket(AF_INET,SOCK_STREAM,0);       //ipv4,TCP

    if(sockfd == -1)

    {

        fprintf(stderr,"socket error!\n");

        exit(1);

    }



    //before func connect, set the attr of structure sockaddr.

    memset(&s_addr_in,0,sizeof(s_addr_in));

    s_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1");      //trans char * to in_addr_t

    s_addr_in.sin_family = AF_INET;

    s_addr_in.sin_port = htons(SOCK_PORT);



    tempfd = connect(sockfd,(struct sockaddr *)(&s_addr_in),sizeof(s_addr_in));

    if(tempfd == -1)

    {

        fprintf(stderr,"Connect error! \n");

        exit(1);

    }



    while(1)

    {

        printf("Please input something you wanna say(input \"quit\" to quit):\n");

        gets(data_send);

        //scanf("%[^\n]",data_send);         //or you can also use this

        tempfd = write(sockfd,data_send,BUFFER_LENGTH);

        if(tempfd == -1)

        {

            fprintf(stderr,"write error\n");

            exit(0);

        }



        if(strcmp(data_send,"quit") == 0)  //quit,write the quit request and shutdown client

        {

            break;

        }

        else

        {

            tempfd = read(sockfd,data_recv,BUFFER_LENGTH);

            assert(tempfd != -1);

            printf("%s\n",data_recv);

            memset(data_send,0,BUFFER_LENGTH);

            memset(data_recv,0,BUFFER_LENGTH);

        }

    }



    int ret = shutdown(sockfd,SHUT_WR);       //or you can use func close()--<unistd.h> to close the fd

    assert(ret != -1);

    return 0;

}

###執行結果

##在Ubuntu中實現對實驗二中的“wc伺服器”通過混合密碼系統進行防護

  • 標頭檔案:
#include <openssl/ssl.h>
#include <openssl/err.h>
  • OpenSSL初始化:int SSL_library_int(void);
  • 選擇會話協議
  • 建立會話環境
  • 申請SSL會話環境的OpenSSL函式: SSL_CTX *SSL_CTX_new(SSL_METHOD * method);
  • 制定證書驗證方式的函式:int SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int(*verify_callback),int(X509_STORE_CTX *));
  • 為SSL會話環境載入CA證書的函式:SSL_CTX_load_verify_location(SSL_CTX *ctx,const char *Cafile,const char *Capath);
  • 為SSL會話載入使用者證書的函式:SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file,int type);
  • 為SSL會話載入使用者私鑰的函式:SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx,const char* file,int type);
  • 驗證私鑰和證書是否相符:int SSL_CTX_check_private_key(SSL_CTX *ctx);
  • 建立SSL套接字
SSL *SSl_new(SSL_CTX *ctx);//申請一個SSL套接字
int SSL_set_fd(SSL *ssl,int fd);)//繫結讀寫套接字
int SSL_set_rfd(SSL *ssl,int fd);//繫結只讀套接字
int SSL_set_wfd(SSL *ssl,int fd);//繫結只寫套接字
  • 完成SSL握手
  • 進行資料傳輸
  • 結束SSL通訊 ###實驗步驟 server.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 = 5227;

									    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, "hello");
																																																																							        /* 發訊息給客戶端
																																																																									 * */
																																																																							        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;
}

telent.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, "nihao");  
																															    /* 發訊息給伺服器
																																 * */  
																															    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;  
}  
  • 編譯
gcc -o server server.c -I /usr/local/ssl/include -L/usr/local/ssl/lib -lssl -lcrypto -ldl -lpthread
gcc -o telent telent.c -I /usr/local/ssl/include -L/usr/local/ssl/lib -lssl -lcrypto -ldl -lpthread
  • 生產私鑰和證書
openssl genrsa -out privkey.pem 1024
openssl req -new -x509 -key privkey.pem -out CAcert.pem -days 1095
  • 執行
./server 7838 1 CAcert.pem privkey.pem
./telent 127.0.0.1 7838

實驗中的問題和解決過程

  • 問題1:編譯時出現openssl/evpp.h:錯誤提示
  • 解決方法:原因是在執行make install命令時沒有使用root許可權,在執行命令時加上sudo後,重新編譯即可。或者通過su直接獲取許可權執行

實驗體會

本次實驗讓我對OpenSSL的使用有了更為深入的瞭解。