IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> win2000环境下使用openssl-0.9.6i实现通信加密 -> 正文阅读

[网络协议]win2000环境下使用openssl-0.9.6i实现通信加密

? ? ? ? 在Openssl库生成数字证书总结(适配win2000)一文中,我们已经实现了数字证书的生成,接下来就具体说下win2000环境下怎么使用openssl-0.9.6i库实现通信加密。

? ? ? ? 首先来说下服务端的代码实现,主要有以下几个步骤:

? ? ? ? 初始化ssl库-->添加验证-->载入数字证书-->载入用户私钥-->校验私钥-->添加加密算法-->创建tcp网络连接-->建立ssl连接-->数据收发-->关闭ssl连接-->关闭tcp网络连接

? ? ? ? 具体代码如下:

int ServerTcp::CreateSSLServer(const string strLocalIp, unsigned int iSvrPort)
{
	SOCKET sockfd, new_fd;
	int len;
	char buf[MAX_BUF + 1]; 
	struct sockaddr_in my_addr, their_addr;

	SSL_CTX *pCtx = NULL;
	unsigned int myport = iSvrPort, lisnum = 2;
	// SSL库初始化
	SSL_library_init();
	// 载入所有SSL算法
	OpenSSL_add_all_algorithms();
	// 载入所有SSL错误消息
	SSL_load_error_strings();
	// 以V2和V3标准兼容方式产生一个SSL_CTX,即SSL Content Text
	pCtx = SSL_CTX_new(SSLv23_server_method());
	if (pCtx == NULL)
	{
		ERR_print_errors_fp(stdout);
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_new error, pCtx == NULL !!");
		return -1;
	}

	// 验证与否
	SSL_CTX_set_verify(pCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);   
	SSL_CTX_load_verify_locations(pCtx, CACERT, NULL); 
	SSL_CTX_set_default_verify_paths(pCtx);

	// 载入数字证书
	if (SSL_CTX_use_certificate_file(pCtx, CERTF, SSL_FILETYPE_PEM) <= 0)
	{
		//ERR_print_errors_fp(stdout);
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_use_certificate_file error !!");
		return -1;
	}

#if 1		
	// 设置私钥的解锁密码
	SSL_CTX_set_default_passwd_cb_userdata(pCtx, "123456");
#endif

	// 载入用户私钥
	if (SSL_CTX_use_PrivateKey_file(pCtx, KEYF, SSL_FILETYPE_PEM) <= 0)
	{
		printf("SSL_CTX_use_PrivateKey_file failed\n");
		//ERR_print_errors_fp(stdout);
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_use_PrivateKey_file error !!");
		return -1;
	}
		
	// 校验用户私钥是否正确
	if (SSL_CTX_check_private_key(pCtx) <= 0)
	{
		//ERR_print_errors_fp(stdout);
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_check_private_key error !!");
		return -1;
	}

	/*
	EDH-RSA-DES-CBC3-SHA				// 0
	EDH-DSS-DES-CBC3-SHA				// 0
	DES-CBC3-SHA						// 1
	DHE-DSS-RC4-SHA						// 0
	IDEA-CBC-SHA						// 1
	RC4-SHA								// 1
	RC4-MD5								// 1
	EXP1024-DHE-DSS-RC4-SHA				// 0
	EXP1024-RC4-SHA						// 1
	EXP1024-DHE-DSS-DES-CBC-SHA			// 0
	EXP1024-DES-CBC-SHA					// 1
	EXP1024-RC2-CBC-MD5					// 1
	EXP1024-RC4-MD5						// 1
	EDH-RSA-DES-CBC-SHA					// 0
	EDH-DSS-DES-CBC-SHA					// 0
	DES-CBC-SHA							// 1
	EXP-EDH-RSA-DES-CBC-SHA				// 0
	EXP-EDH-DSS-DES-CBC-SHA				// 0
	EXP-DES-CBC-SHA						// 0
	EXP-RC2-CBC-MD5						// 0
	EXP-RC4-MD5							// 0
	*/
	if (SSL_CTX_set_cipher_list(pCtx, "DES-CBC3-SHA") == 0)
	{
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_set_cipher_list error !!");
	}
	SSL_CTX_set_mode(pCtx, SSL_MODE_AUTO_RETRY);

	//启动网络
	WSADATA wsaData;
	char szName[100] = {0};
	hostent *pHostEntry = NULL;
	//Net Start Up
	int nError=WSAStartup(MAKEWORD(0x02,0x02),&wsaData);
	if(nError!=0)
	{
		nError = WSAGetLastError();	
		g_log.WriteLogPrintW(__FILEW__, __LINE__,_T("WSAStartUp Faild With Error: %d"),nError);		
		return nError;
	}
	//Make Version
	if ( LOBYTE( wsaData.wVersion ) != 2 ||	HIBYTE( wsaData.wVersion ) != 2 )
	{
		WSACleanup( );		
		g_log.WriteLogW(__FILEW__, __LINE__,_T("The Local Net Version Is not 2"));

		return -1;
	}
	if (strLocalIp.empty()==false)
	{
		//创建侦听端口
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
		if(sockfd==-1)
		{
			nError = WSAGetLastError();
			g_log.WriteLogPrintW(__FILEW__, __LINE__,_T("CreateSocket faild with Error: %d"),nError);	
			return nError;
		}
	}

	//绑定到目标地址
	my_addr.sin_family = AF_INET;
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	//my_addr.sin_addr.S_un.S_addr = inet_addr(strLocalIp.c_str());
	my_addr.sin_port = htons(myport);

	int nRet = bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr));
	if( nRet == SOCKET_ERROR )
	{
		nError=GetLastError();
		g_log.WriteLogPrintW(__FILEW__, __LINE__,_T("bind Socket faild with Error: %d"),nError);
		return nError;
	}

	//侦听端口上的连接请求
	if(	listen(sockfd, lisnum)==SOCKET_ERROR)
	{
		nError=GetLastError();
		g_log.WriteLogPrintW(__FILEW__, __LINE__,_T("listen Socket faild with Error: %d"),nError);
		return nError;
	}

	while (1)
	{
		SSL *ssl = NULL;
		len = sizeof(struct sockaddr);
		
		// 等待客户端连接
		new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len);
		if (new_fd < 0)
		{
			g_log.WriteLogW(__FILEW__, __LINE__, L"accept error !!");
			return errno;
		}
		else
		{
			char szValue[1024] = {0};
			sprintf(szValue, "server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
			g_log.WriteLogW(__FILEW__, __LINE__, Utf8ToUnicode(szValue).c_str());
			printf(szValue);

			// 基于pCtx产生一个新的SSL
			ssl = SSL_new(pCtx);
			// 将连接用户的socket加入到ssl
			SSL_set_fd(ssl, new_fd);
			// 建立ssl连接
			int nRet = SSL_accept(ssl);
			if (nRet <= 0)
			{
				int ret_code = 0;//SSL_ERROR_NONE
				int n = SSL_get_error(ssl, ret_code);
				printf("%s\n",ERR_error_string(ERR_get_error(), NULL));

				closesocket(new_fd);
				g_log.WriteLogPrintW(__FILEW__, __LINE__, L"SSL_accept error, %s", Utf8ToUnicode(ERR_error_string(ERR_get_error(), NULL)).c_str());
				//break;

				// 关闭 SSL 连接
				SSL_shutdown(ssl);
				// 释放 SSL 
				SSL_free(ssl);
				// 关闭 socket 
				closesocket(new_fd);

				continue;
			}
			//ServerShowCerts(ssl);

			// 开始处理每个新连接上的数据收发
			memset(buf, 0, MAX_BUF);
			strcpy(buf, "hello client");
			// 发消息给客户端
			len = SSL_write(ssl, buf, strlen(buf));
			if (len <= 0)
			{
				sprintf(szValue, "消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buf, errno, strerror(errno));
				g_log.WriteLogW(__FILEW__, __LINE__, Utf8ToUnicode(szValue).c_str());
				printf(szValue);

				// 关闭 SSL 连接
				SSL_shutdown(ssl);
				// 释放 SSL 
				SSL_free(ssl);
				// 关闭 socket 
				closesocket(new_fd);
				
				continue;
			}
			else
			{
				sprintf(szValue, "消息'%s'发送成功!\n", buf);
				g_log.WriteLogW(__FILEW__, __LINE__, Utf8ToUnicode(szValue).c_str());
				printf(szValue);

				memset(buf, 0, MAX_BUF);
				// 接收客户端消息
				len = SSL_read(ssl, buf, MAX_BUF);
				if (len > 0)
				{
					sprintf(szValue, "接收消息成功:'%s',共%d个字节的数据\n", buf, len);
				}
				else
				{
					sprintf(szValue, "消息接收失败!错误代码是%d\n", len);
				}

				g_log.WriteLogW(__FILEW__, __LINE__, Utf8ToUnicode(szValue).c_str());
				printf(szValue);
			}
		}

		// 关闭 SSL 连接
		SSL_shutdown(ssl);
		// 释放 SSL 
		SSL_free(ssl);
		// 关闭 socket 
		closesocket(new_fd);
	}

	// 关闭监听的 socket
	closesocket(sockfd);
	WSACleanup();
	// 释放 CTX 
	SSL_CTX_free(pCtx);
	return 0;

}

? ? ? ? 需要注意的是,由于适配win2000的openssl-0.9.6i库是比较早期的库,所以有些加密算法是不支持的,具体统计如下:

openssl-0.9.6i是否支持加密算法
不支持EDH-RSA-DES-CBC3-SHA
不支持EDH-DSS-DES-CBC3-SHA
不支持DHE-DSS-RC4-SHA
不支持EXP1024-DHE-DSS-RC4-SHA
不支持EXP1024-DHE-DSS-DES-CBC-SHA
不支持EDH-RSA-DES-CBC-SHA
不支持EDH-DSS-DES-CBC-SHA
不支持EXP-EDH-RSA-DES-CBC-SHA
不支持EXP-EDH-DSS-DES-CBC-SHA
不支持EXP-DES-CBC-SHA
不支持EXP-RC2-CBC-MD5
不支持EXP-RC4-MD5
支持DES-CBC3-SHA
支持IDEA-CBC-SHA
支持RC4-SHA
支持RC4-MD5
支持EXP1024-RC4-SHA
支持EXP1024-DES-CBC-SHA
支持EXP1024-RC2-CBC-MD5
支持EXP1024-RC4-MD5
支持DES-CBC-SHA

? ? ? ? 接下来说下客户端代码的实现,与服务端实现步骤类似,代码如下:

int ClientTcp::CreateSSLClient(const string strLocalIp, unsigned int iSvrPort)
{
	int sockfd, len;
	struct sockaddr_in dest;
	char buffer[MAX_BUF + 1] = { 0 };
	SSL_CTX *ctx;
	SSL *ssl;
	// ssl库初始化
	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);
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_new error !!");
		return 1;
	}

	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);   // 验证与否
	SSL_CTX_load_verify_locations(ctx, CACERT, NULL); // 若验证,则放置CA证书
	SSL_CTX_set_default_verify_paths(ctx);

	// 载入数字证书
	if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0)
	{
		//ERR_print_errors_fp(stdout);
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_use_certificate_file error !!");
		return -1;
	}

#if 1		
	/*设置私钥的解锁密码*/
	SSL_CTX_set_default_passwd_cb_userdata(ctx, "123456");
#endif

	// 载入用户私钥
	if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0)
	{
		printf("SSL_CTX_use_PrivateKey_file failed\n");
		//ERR_print_errors_fp(stdout);
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_use_PrivateKey_file error !!");
		return -1;
	}

	// 校验用户私钥是否正确
	if (SSL_CTX_check_private_key(ctx) <= 0)
	{
		//ERR_print_errors_fp(stdout);
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_check_private_key error !!");
		return -1;
	}

	/*
	EDH-RSA-DES-CBC3-SHA				// 0
	EDH-DSS-DES-CBC3-SHA				// 0
	DES-CBC3-SHA						// 1
	DHE-DSS-RC4-SHA						// 0
	IDEA-CBC-SHA						// 1
	RC4-SHA								// 1
	RC4-MD5								// 1
	EXP1024-DHE-DSS-RC4-SHA				// 0
	EXP1024-RC4-SHA						// 1
	EXP1024-DHE-DSS-DES-CBC-SHA			// 0
	EXP1024-DES-CBC-SHA					// 1
	EXP1024-RC2-CBC-MD5					// 1
	EXP1024-RC4-MD5						// 1
	EDH-RSA-DES-CBC-SHA					// 0
	EDH-DSS-DES-CBC-SHA					// 0
	DES-CBC-SHA							// 1
	EXP-EDH-RSA-DES-CBC-SHA				// 0
	EXP-EDH-DSS-DES-CBC-SHA				// 0
	EXP-DES-CBC-SHA						// 0
	EXP-RC2-CBC-MD5						// 0
	EXP-RC4-MD5							// 0
	*/
	if (SSL_CTX_set_cipher_list(ctx, "DES-CBC3-SHA") == 0)
	{
		g_log.WriteLogW(__FILEW__, __LINE__, L"SSL_CTX_set_cipher_list error !!");
	}
	SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);// 处理握手多次

	// 创建一个socket用于通信
	WORD wVerRequset;
	WSADATA wsaData;
	int error;

	wVerRequset = MAKEWORD(2, 2);
	error = WSAStartup(wVerRequset, &wsaData);
	if (error != 0)
	{
		g_log.WriteLogW(__FILEW__, __LINE__, L"WSAStartup error !!");
		return 0;
	}

	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		g_log.WriteLogW(__FILEW__, __LINE__, L"LOBYTE and HIBYTE error !!");
		return 0;
	}

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
	{
		g_log.WriteLogW(__FILEW__, __LINE__, L"socket error !!");
		return errno;
	}

	// 初始化服务器端(对方)的地址和端口信息 
	dest.sin_family = AF_INET;
	dest.sin_addr.s_addr = inet_addr(strLocalIp.c_str());
	dest.sin_port = htons(iSvrPort);

	/* 连接服务器 */
	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) 
	{
		g_log.WriteLogW(__FILEW__, __LINE__, L"connect error !!");
		return errno;
	}

	// 基于 ctx 产生一个新的 SSL 
	ssl = SSL_new(ctx);
	SSL_set_fd(ssl, sockfd);
	// 建立 SSL 连接 
	if (SSL_connect(ssl) == -1)
	{
		//ERR_print_errors_fp(stderr);
		int ret_code = 0;//SSL_ERROR_NONE
		int n = SSL_get_error(ssl, ret_code);
		printf("%s\n",ERR_error_string(ERR_get_error(), NULL));

		g_log.WriteLogPrintW(__FILEW__, __LINE__, L"SSL_connect error, %s", Utf8ToUnicode(ERR_error_string(ERR_get_error(), NULL)).c_str());

		// 关闭连接 
		SSL_shutdown(ssl);
		SSL_free(ssl);
		closesocket(sockfd);
		SSL_CTX_free(ctx);

		return 0;
	}
	else 
	{
		printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
		g_log.WriteLogPrintW(__FILEW__, __LINE__, L"Connected with %s encryption\n", SSL_get_cipher(ssl));
		//ShowCerts(ssl);
	}

	// 接受对方发过来的消息
	len = SSL_read(ssl, buffer, MAX_BUF);
	if (len > 0)
	{
		char szValue[1024] = {0};
		sprintf(szValue, "接收消息成功:'%s',共%d个字节的数据\n", buffer, len);
		g_log.WriteLogW(__FILEW__, __LINE__, Utf8ToUnicode(szValue).c_str());
		printf(szValue);

		memset(buffer, 0, MAX_BUF);
		strcpy(buffer, "hello server");
		len = SSL_write(ssl, buffer, strlen(buffer));
		if (len <= 0)
		{
			sprintf(szValue, "消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno));
			g_log.WriteLogW(__FILEW__, __LINE__, Utf8ToUnicode(szValue).c_str());
			printf(szValue);

			// 关闭连接 
			SSL_shutdown(ssl);
			SSL_free(ssl);
			closesocket(sockfd);
			SSL_CTX_free(ctx);
			
			return 0;
		}
	}
	else
	{
		char szValue[1024] = {0};
		sprintf(szValue, "消息接收失败!错误代码是%d,错误信息是'%s'\n", buffer, len);
		g_log.WriteLogW(__FILEW__, __LINE__, Utf8ToUnicode(szValue).c_str());
		printf(szValue);

		// 关闭连接 
		SSL_shutdown(ssl);
		SSL_free(ssl);
		closesocket(sockfd);
		SSL_CTX_free(ctx);
		
		return 0;
	}
	
}

? ? ? ? 在win2000上的实际测试结果:

????????

? ? ? ? 测试结果使用DES-CBC3-SHA加密算法可以正常收发数据,并且使用wireshark抓包看到的数据是加密之后的。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-11-12 19:57:15  更:2021-11-12 19:57:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年7日历 -2024/7/3 8:13:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码