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的使用有了更为深入的了解。

posted on 2018-12-16 14:27  月半月半白勺  阅读(154)  评论(0编辑  收藏  举报

导航