zoukankan      html  css  js  c++  java
  • 安全通信系统OpenSSL服务器和客户端

    1. 环境配置
    2. SSL的初始化

                       这一步主要使用OpenSSL提供的函数设置算法、证书等,一般步骤都固定了的,初始化流程示意图如下所示:

                                                                

                                                                                 SSL初始化流程图

                        主要代码为:

    SSL_load_error_strings(); /*为打印调试信息作准备*/
    OpenSSL_add_ssl_algorithms(); /*初始化*/
    
    //meth = TLSv1_server_method(); /*采用什么协议(SSLv2/SSLv3/TLSv1)在此指定*/
    //注意这里是server,客户端那里会是client
    ctx = SSL_CTX_new (TLSv1_server_method()); 
    CHK_NULL(ctx);
    
    SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /*验证与否*/
    SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若验证,则放置CA证书*/
    
    if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
    	ERR_print_errors_fp(stderr);
    	getchar();
    	exit(3);
    }
    
    if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
    	ERR_print_errors_fp(stdout);
    	getchar();
    	exit(4);
    }
    
    if (!SSL_CTX_check_private_key(ctx)) {
    	printf("Private key does not match the certificate public keyn");
    	getchar();
    	exit(5);
    }
    
    
    SSL_CTX_set_cipher_list(ctx,"RC4-MD5"); 

              3.SSL连接通信

                  建立TCP套接字,并且进入监听,当接收到客户端连接请求后,使用accept建立和客户端通信的套接字s,从初始化的SSL_CTX创建一个SSL,将该SSL和与客户端通信的套接字s绑定,然后使用SSL_accept建立和客户端的SSL连接,如果建立成功就可以检查客户端的证书并获取客户端的证书信息,实现相互认证。之后,可以使用异步通信机制处理该SSL连接的消息。原理图如下图所示:

               

    控制台的非异步通信的SSL通信代码如下所示。(非异步通信的代码原理看起来原理更好理解)

    服务器端代码
    //创建监听套接字
    sListen = socket (AF_INET, SOCK_STREAM, 0); 
    if(sListen == INVALID_SOCKET)
    {
    	cout << "监听套接字创建失败" << endl;
    	exit(0);
    }
    //创建地址
    sockaddr_in sin = {0};
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons (PORT); 
    
    //套接字绑定
    err = bind(sListen, (struct sockaddr*) &sin,sizeof(sin)); 
    if(err == -1)
    {
    	cout << "监听套接字绑定失败" << endl;
    	exit(0);
    }
    
    /*接受TCP链接*/
    err = listen (sListen, 5); 
    if(err == -1)
    {
    	cout << "监听套接字开启监听失败" << endl;
    	exit(0);
    }
    
    //接受客户端连接
    SOCKET sd = accept(sListen, (struct sockaddr *)&sa_cli, (socklen_t*)&client_len);
    
    if(err == -1)
    {
    	cout << "监听套接字accept失败" << endl;
    	exit(0);
    }
    closesocket (sListen);
    
    
    /*TCP连接已建立,进行服务端的SSL过程. */
    cout << "TCP连接建立,创建SSL连接中....\n" << endl;
    //从初始化的ctx新建SSL
    ssl = SSL_new (ctx); 
    if(ssl == NULL)
    {
    	cout << "SSL创建失败" << endl;
    	exit(0);
    }
    //套接字和SSL绑定
    SSL_set_fd (ssl, sd);
    //SSL连接建立
    err = SSL_accept (ssl);
    if(err == -1)
    {
    	cout << "创建SSL连接失败" << endl;
    	exit(0);
    }
    else
    {
    	cout << "创建SSL连接成功" << endl;
    	exit(0);
    }
    
    /*打印所有加密算法的信息(可选)*/
    cout << "SSL连接算法信息:" << SL_get_cipher (ssl) << endl;
    
    /*得到服务端的证书并打印些信息(可选) */
    client_cert = SSL_get_peer_certificate (ssl);
    if (client_cert != NULL) {
    	cout << "客户端证书:" << endl;
    	
    	cout << "subject:" << X509_get_subject_name (client_cert), 0, 0) << endl;
    	
    
    	cout << "issuer:" << X509_NAME_oneline (X509_get_issuer_name (client_cert),0,0) << endl;
    	CHK_NULL(str);
    	X509_free (client_cert);/*如不再需要,需将证书释放 */
    }
    else cout << "客户端没有证书信息!" << endl; //客户端认证失败
    
    //SSL通信,用SSL_write,SSL_read代替send和recv
    while(true)
    {	
    	//接收消息
    	err = SSL_read (ssl, buf, sizeof(buf) - 1); 
    	if(err == -1)
    	{
    		cout << "SSL_read接收消息失败" << endl;
    		exit(0);
    	}
    	buf[err] = '\0';
    	cout << "【客户端】:" << buf << endl;
    
    	cin >> buf;
    	//发送消息
    	err = SSL_write(ssl, buf, strlen(ssl));
    	if(err == -1)
    	{
    		cout << "SSL_write发送消息失败" << endl;
    		exit(0);
    	}
    	cout << "【服务器】:" << buf << endl;
    }
    
    //关闭套接字和ssl
    shutdown (sd,2);
    SSL_free (ssl);
    SSL_CTX_free (ctx);
    

    客户端代码

    //
    #include "openssl/rsa.h" 
    #include "openssl/crypto.h"
    #include "openssl/x509.h"
    #include "openssl/pem.h"
    #include "openssl/ssl.h"
    #include "openssl/err.h"
    #include "openssl/rand.h"
    
    #pragma comment(lib, "libeay32.lib")
    #pragma comment(lib, "ssleay32.lib")
    
    /*所有需要的参数信息都在此处以#define的形式提供*/
    
    #define CERTF "C:\\client.crt" /*服务端的证书(需经CA签名)*/
    #define KEYF "C:\\client.key" /*服务端的私钥(建议加密存储)*/
    #define CACERT "C:\\ca.crt" /*CA 的证书*/
    #define PORT 7758 /*服务端的端口*/
    
    int main ()
    {
    	int err;
    
    	int sd;
    	struct sockaddr_in sa;
    	SSL_CTX* ctx;
    	SSL* ssl;
    	X509* server_cert;
    	char* str;
    	char buf [4096] = {0};
    	char szMsg[4096] = {0}; 
    	//SSL_METHOD *meth;
    	int seed_int[100]; /*存放随机序列*/
    
    	WSADATA wsaData;
    
    	if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
    		printf("WSAStartup()fail:%d\n",GetLastError());
    		return -1;
    	} 
    
    	OpenSSL_add_ssl_algorithms(); /*初始化*/
    	SSL_load_error_strings(); /*为打印调试信息作准备*/
    
    	//注意这里是client和和服务器不同
    	ctx = SSL_CTX_new (TLSv1_client_method()); 
    	CHK_NULL(ctx);
    
    	SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /*验证与否*/
    	SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若验证,则放置CA证书*/
    
    
    	if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) 
    	{
    		cout << "客户端证书检查失败!" << endl;
    		exit(0);
    	}
    
    	if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) 
    	{
    		cout << "客户端key检查失败!" << endl;
    		exit(0);
    	}
    
    	if (!SSL_CTX_check_private_key(ctx)) 
    	{
    		cout << "客户端证书和key不匹配!" << endl;
    		exit(0);
    	}
    
    	/*构建随机数生成机制,WIN32平台必需*/
    	srand( (unsigned)time( NULL ) );
    	for( int i = 0; i < 100;i++ ) seed_int[i] = rand();
    
    	RAND_seed(seed_int, sizeof(seed_int));
    	
    	//创建TCP连接请求
    	sd = socket (AF_INET, SOCK_STREAM, 0); 
    	if(sd == INVALID_SOCKET)
    	{
    		cout << "套接字创建失败!" << endl;
    		exit(0);
    	}
    	memset(&sa,'\0', sizeof(sa));
    	sa.sin_family = AF_INET;
    	sa.sin_addr.s_addr = inet_addr ("127.0.0.1"); 
    	sa.sin_port = htons (PORT); /* Server Port number */
    
    	//TCP连接
    	err = connect(sd, (struct sockaddr*) &sa,
    	if(err == -1)
    	{
    		cout << "TCP连接失败!" << endl;
    		exit(0);
    	}
    	else
    	{
    		cout << "SSL连接成功!" << endl;
    		exit(0);
    	}
    	//SSL连接
    	//新建SSL
    	ssl = SSL_new (ctx); 
    	if(ssl == NULL)
    	{
    		cout << "新建SSL失败!" << endl;
    		exit(0);
    	}
    	//套接字和SSL绑定
    	SSL_set_fd (ssl, sd);
    	//SLL连接
    	err = SSL_connect (ssl);
    	if(err == -1)
    	{
    		cout << "SSL连接失败" << endl;
    		exit(0);
    	}
    	else
    	{
    		cout << "SSL连接成功" << endl;
    		exit(0);
    	}
    	//打印连接信息
    	cout << "SSL连接算法信息:" << SSL_get_cipher (ssl) << endl;
    
    	/*得到服务端的证书并打印些信息(可选) */
    	server_cert = SSL_get_peer_certificate (ssl);
    	if (server_cert != NULL) {
    		cout << "服务器证书:" << endl;
    		
    		cout << "subject:" << X509_get_subject_name (server_cert), 0, 0) << endl;
    		
    
    		cout << "issuer:" << X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0) << endl;
    		CHK_NULL(str);
    		X509_free (server_cert);/*如不再需要,需将证书释放 */
    	}
    	else cout << "服务器没有证书信息!" << endl; //服务器端认证失败
    
    	//SSL通信,用SSL_write,SSL_read代替send和recv
    	while(true)
    	{	
    		cout << "请输入要发送的消息:";
    		cin >> buf;
    		//发送消息
    		err = SSL_write(ssl, buf, strlen(ssl));
    		if(err == -1)
    		{
    			cout << "SSL_write发送消息失败" << endl;
    			exit(0);
    		}
    		cout << "【客户端】:" << buf << endl;
    		//接收消息
    		err = SSL_read (ssl, buf, sizeof(buf) - 1); 
    		if(err == -1)
    		{
    			cout << "SSL_read接收消息失败" << endl;
    			exit(0);
    		}
    		buf[err] = '\0';
    		cout << "【服务器】:" << buf << endl;
    	}
    
    	/* 收尾工作 */
    	SSL_shutdown (ssl);
    	shutdown (sd,2);
    	SSL_free (ssl);
    	SSL_CTX_free (ctx);
    
    	return 0;
    }
    
    




               
  • 相关阅读:
    了解数据库的三级模式和二级映像
    Oracle函数
    SQL基本语法
    Oracle数据库之初步接触
    Java学习之正则表达式
    Java关于条件判断练习--统计一个src文件下的所有.java文件内的代码行数(注释行、空白行不统计在内)
    Java 关于循环的练习--和为n的正数序列
    Java学习之流Stream理解(一)
    Java学习之理解递归
    python+requests接口自动化测试框架实例详解教程
  • 原文地址:https://www.cnblogs.com/arbboter/p/4225212.html
Copyright © 2011-2022 走看看