在
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 版权协议,转载请附上原文出处链接和本声明。