zoukankan      html  css  js  c++  java
  • 基于SSL的select编程Linux系统

    在这个功能上我参考了一下的文章:
    https://www.openssl.org
    (这是openssl的官方网站,可以查询到很多函数的使用,英文网站)
    https://www.cnblogs.com/hjbf/p/10248388.html
    (关于SSL协议有相当详细的介绍 如果有足够的时间可以了解一下)
    https://blog.csdn.net/nyyjs/article/details/72832896
    (关于SSL中涉及到的各种证书的介绍)
    https://www.cnblogs.com/LiuYanYGZ/p/10438861.html
    (在这篇文章中使用了多个证书和密钥是因为使用的双向认证,本程序只是一个demo比较简单,使用的是单向认证方式)
    https://www.jianshu.com/p/fb5fe0165ef2
    (关于SSL的单向认证和双向认证的介绍)
    https://www.cnblogs.com/AloneSword/p/3809002.html
    (使用OpenSSL生成证书,基本都是命令,可以大致了解一下流程,在下面我会给出明确指令生成证书)
    https://blog.csdn.net/xs574924427/article/details/17240793
    (这篇文章是我代码的原型,这篇文章的开始对SSL编程的流程介绍的很仔细,值得一看,但是实现代码部分稍显凌乱)

    Server端程序
    1.
    一定要有CA证书和密钥,做SSL协议是不可以绕过的,我尝试省略这一步,因为这里的校验过程相当复杂琐碎,我会用一个好理解的方式总结一下【https://www.cnblogs.com/y-c-y/p/12124685.html】,但是CS两端不能成功通信,因为在SSL握手的过程中会对结构体SSL进行一些赋值,如果没有证书和密钥,有些流程是走不下去的。
    2.
    用下面这两个命令产生上述cacert.pem和privkey.pem文件:
    可以cd到你的代码目录生成就可以了

    openssl genrsa -out privkey.pem 2048
    openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
    

    cacert.pem生成的时候会让你输入一些东西,类似省份,公司名称之类的,可以随便输入

    #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 "utils.h"
    #define MAXBUF 1024
    #define PORT 7838
    #define CAR "cacert.pem"
    #define PRIVA_KEY "privkey.pem"
    
    int main()
    {
    	int sockfd, new_fd;
    	FILE *fp
    	socklen_t len;
    	struct sockaddr_in my_addr, their_addr;
    	char buf[MAXBUF + 1];
    	SSL_METHOD *meth;
    	SSL_CTX *ctx;
    	fd_set readFdSet;
    
        /* 初始化OpenSSL */
    	SSL_library_init();
    	/*加载算法库 */
    	OpenSSL_add_all_algorithms();
    	/*加载错误处理信息 */
    	SSL_load_error_strings();
    	/* 选择会话协议  有多种版本可以选择*/
    	meth = (SSL_METHOD *)SSLv23_server_method();
    	/* 创建会话环境 */
    	ctx = SSL_CTX_new(meth);
    	/* 制定证书验证方式 */
    	SSL_CTX_set_security_level(ctx, 0);
    	
    	if (ctx == NULL) 
    	{
    		ERR_print_errors_fp(stdout);
    		exit(1);
    	}
        /* 为SSL会话加载CA证书 */
    	if (SSL_CTX_use_certificate_file(ctx, CAR, SSL_FILETYPE_PEM) <= 0) 
    	{
    		ERR_print_errors_fp(stdout);
    		exit(1);
    	}
    	/* 为SSL会话加载私钥 */
    	if (SSL_CTX_use_PrivateKey_file(ctx, PRIVA_KEY, 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);
    	}
    	
        /* 建立TCP的socket */
    	if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) 
    	{
    		perror("socket");
    		exit(1);
    	} 
    	else
    	    printf("socket created
    ");
    	
    	bzero(&my_addr, sizeof(my_addr));
    	my_addr.sin_family = PF_INET;
    	my_addr.sin_port = htons(PORT);
    	my_addr.sin_addr.s_addr = INADDR_ANY;
    	/* TCP绑定socket */
    	if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) 
    	{
    		perror("bind");
    		exit(1);
    	} 
    	else
    	    printf("binded
    ");
    	/* TCP监听socket */
    	if (listen(sockfd, 5) == -1) 
    	{
    		perror("listen");
    		exit(1);
    	} 
    	else
    	    printf("begin listen
    ");
    	 
    	FD_ZERO(&readFdSet);
    	FD_SET(fileno(fp),&readFdSet);
    	FD_SET(sockfd,&readFdSet);
    	while (1) 
    	{
    		FD_SET(fileno(fp),&readFdSet);
    	    FD_SET(sockfd,&readFdSet);
    		/* select函数调用 */
    		if(select(sockfd+1,&readFdSet,NULL,NULL,NULL)>0)
    		{
    			if(FD_ISSET(sockfd,&readFdSet))
    			{
    				SSL *ssl;
    				ssl = SSL_new(ctx);
    				/* 将socket号转换为ssl结构体相关 */
    				SSL_set_fd(ssl, sockfd);
    				/* SSL接收socket 并且得到最终的接收消息的socket号 */
    				if (SSL_accept(ssl) == -1) 
    				{
    					perror("accept");
    					close(new_fd);
    					break;
    				}
    				
    				bzero(buf, MAXBUF + 1);
    				scanf("%s",buf);
    				len = SSL_write(ssl, buf, strlen(buf));
    				if (len <= 0) 
    				{
    					printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'
    ",buf, errno, strerror(errno));
    					SSL_shutdown(ssl);
    					SSL_free(ssl);
    					close(new_fd);
    					SSL_CTX_free(ctx);
    					return 0;
    				} 
    				else
    					printf("消息'%s'发送成功,共发送了%d个字节!
    ",buf, len);
    				bzero(buffer, MAXBUF + 1);
    				/* SSL read 参数是和recevfrom函数个数不同 */
    				len = SSL_read(ssl, buffer, MAXBUF);
    				if (len > 0)
    					printf("接收消息成功:'%s',共%d个字节的数据
    ",buffer, len);
    				else 
    				{
    					printf("消息接收失败!错误代码是%d,错误信息是'%s'
    ",errno, strerror(errno));
    					/* 关闭连接 */
    					SSL_shutdown(ssl);
    					SSL_free(ssl);
    					close(sockfd);
    					SSL_CTX_free(ctx);
    					return 0;
    				}
    			}
    			else if(FD_ISSET(fileno(fp),&readFdSet))
    			{
    				memset(buf,0,sizeof(buf));
    				if((n = readline(fileno(fp),buf,MAXLINE)) == 0)
    				{
    					shutdown(sockfd,SHUT_WR);
    					FD_CLR(fileno(fp),&rset);
    					INFO_PRINT("nothing input!");
    					continue;
    				}
    				else if(n >0)
    				{
    					SSL_writen(sockfd,buf,n);
    				}
    				else
    				{
    					ERR_EXIT("some error occurred ");
    				}
    				printf("FD_ISSET(fileno(fp),&rset)----%d--%s
    ",n,buf);
    				SSL_writen(sockfd,buf,n);
    			}
    			else
    			{
    				printf("消息接收失败!错误代码是%d,错误信息是'%s'
    ",errno, strerror(errno));
    			}
    		}
    		else
    		{
    			printf("消息接收失败!错误代码是%d,错误信息是'%s'
    ",errno, strerror(errno));
    		}
    		
    	}
    	SSL_shutdown(ssl);
    	SSL_free(ssl);
    	SSL_CTX_free(ctx);
    	close(new_fd);
    	return ;
    }
    

    Client端程序

    #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 "utils.h"
    #define MAXBUF 1024
    #define PORT 7838
    #define IP "127.0.0.1"
     
    void ShowCerts(SSL * ssl)
    {
    	X509 *cert;
    	char *line;
    	 
    	cert = SSL_get_peer_certificate(ssl);
    	if (cert != NULL) 
    	{
    		printf("数字证书信息:
    ");
    		line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
    		printf("证书: %s
    ", line);
    		free(line);
    		line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
    		printf("颁发者: %s
    ", line);
    		free(line);
    		X509_free(cert);
    	} 
    	else
    	    printf("无证书信息!
    ");
    }
     
    int main()
    {
    	int sockfd, len;
    	FILE *fp
    	struct sockaddr_in dest;
    	char buffer[MAXBUF + 1];
    	SSL_CTX *ctx;
    	SSL *ssl;
    	fd_set readFdSet;
    
    	/* 初始化OpenSSL */
    	SSL_library_init();
    	/*加载算法库 */
    	OpenSSL_add_all_algorithms();
    	/*加载错误处理信息 */
    	SSL_load_error_strings();
    	/* 选择会话协议 */
    	meth = (SSL_METHOD *) SSLv23_client_method ();
    	/* 创建会话环境 */
    	ctx = SSL_CTX_new(meth);
    	if (ctx == NULL) 
    	{
    		ERR_print_errors_fp(stdout);
    		exit(1);
    	}
    	
    	/* 建立TCP的socket */
    	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    	{
    		perror("Socket");
    		exit(errno);
    	}
    	printf("socket created
    ");
    	
    	bzero(&dest, sizeof(dest));
    	dest.sin_family = AF_INET;
    	dest.sin_port = htons(PORT);
    	if (inet_aton(IP, (struct in_addr *) &dest.sin_addr.s_addr) == 0) 
    	{
    		perror(IP);
    		exit(errno);
    	}
    	printf("address created
    ");
    	/* 建立TCP 连接 */
    	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) 
    	{
    		perror("Connect ");
    		exit(errno);
    	}
    	printf("server connected
    ");
    
    	ssl = SSL_new(ctx);
    	/* 将socket号转换为ssl结构体相关 */
    	SSL_set_fd(ssl, sockfd);
    	/* 建立SSL连接 */
    	if (SSL_connect(ssl) == -1)
    		ERR_print_errors_fp(stderr);
    	else 
    	{
    		printf("Connected with %s encryption
    ", SSL_get_cipher(ssl));
    		/* SSL的单向认证 所以此处没有Client证书递交过程,这里是显示一下收到的来自服务器的证书部分内容 */
    		ShowCerts(ssl);
    	}
    
    	/* 对readFdSet的操作和普通select一样 */
    	FD_ZERO(&readFdSet);
    	while(1)
    	{
    		FD_SET(fileno(fp),&readFdSet);
    	    FD_SET(sockfd,&readFdSet);
    		/* select函数调用 */
    		if(select(sockfd+1,&readFdSet,NULL,NULL,NULL)>0)
    		{
    			if(FD_ISSET(sockfd,&readFdSet))
    			{
    				bzero(buffer, MAXBUF + 1);
    				/* SSL read 参数是和recevfrom函数个数不同 */
    				len = SSL_read(ssl, buffer, MAXBUF);
    				if (len > 0)
    				    printf("接收消息成功:'%s',共%d个字节的数据
    ",buffer, len);
    				else 
    				{
    					printf("消息接收失败!错误代码是%d,错误信息是'%s'
    ",errno, strerror(errno));
    					/* 关闭连接 */
    					SSL_shutdown(ssl);
    					SSL_free(ssl);
    					close(sockfd);
    					SSL_CTX_free(ctx);
    					return 0;
    				}
    			}
    			else if(FD_ISSET(fileno(fp),&readFdSet))
    			{
    				memset(buf,0,sizeof(buf));
    				if((n = readline(fileno(fp),buf,MAXLINE)) == 0)
    				{
    					shutdown(sockfd,SHUT_WR);
    					FD_CLR(fileno(fp),&rset);
    					INFO_PRINT("nothing input!");
    					continue;
    				}
    				else if(n >0)
    				{
    					/* do nothing */
    				}
    				else
    				{
    					ERR_EXIT("some error occurred ");
    				}
    				printf("FD_ISSET(fileno(fp),&rset)----%d--%s
    ",n,buf);
    				SSL_writen(ssl,buf,n);
    			}
    			else
    			{
    				printf("消息接收失败!错误代码是%d,错误信息是'%s'
    ",errno, strerror(errno));
    			}
    		}
    		else
    		{
    			printf("消息接收失败!错误代码是%d,错误信息是'%s'
    ",errno, strerror(errno));
    		}
    	}
    	/* 关闭连接 */
    	SSL_shutdown(ssl);
    	SSL_free(ssl);
    	close(sockfd);
    	SSL_CTX_free(ctx);
    	return 1;
    }
    
    

    makefile

    all:ss
    	@echo ""
    	@echo "This is Client compile......."
    	@echo ""
    ss:client.c 
    	gcc -g -o Client client.c -lssl -lcrypto
    	gcc -g -o Server server.c -lssl -lcrypto
    clean :
    	-rm Client Server
    
  • 相关阅读:
    对JAVA集合进行遍历删除时务必要用迭代器
    【javascript的那些事】等待加载完js后执行方法
    【微信H5】 Redirect_uri参数错误解决方法
    关于Java优质代码的那些事
    H5页面微信分享和手Q分享设置
    LVS+keepalived-DR模式
    Linux文件误删恢复
    MySQL常用语句
    sudo权限配置
    Rsync同步部署web服务端配置
  • 原文地址:https://www.cnblogs.com/y-c-y/p/12133303.html
Copyright © 2011-2022 走看看