win2000环境下使用openssl-0.9.6i实现通信加密

  • Post author:
  • Post category:其他




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抓包看到的数据是加密之后的。



版权声明:本文为sunkaijie123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。