基于openssl实现TCP双向认证

文章参考

深入探索 OpenSSL:概念、原理、开发步骤、使用方法、使用场景及代码示例
c++使用OpenSSL基于socket实现tcp双向认证ssl(使用TSL协议)代码实现
SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码)
SSL/CA 证书及其相关证书文件(pem、crt、cer、key、csr)

TCP实现OpenSSL原理(TCP3次握手+OPENSLL四次握手

SSL全称安全套接字协议层,为了通信安全,使用RSA非对称加密交换密钥,密钥交换完成后使用对称密钥进行通信。(为什么将非对称加密切换称对称加密是为了提供通信效率。非对称加密效率低)
TCP openssl双向认证:即在TCP三次握手的基础上增加一次openssl的4次握手。

实现OPENSSL四次握手
  1. 服务端,在已建立的TCP链接基础上调用SSL_accept
  2. 客户端,在已建立的TCP链接基础上调用SSL_connect
TCP SSL 服务端步骤
  1. 初始化ssl并加载证书和密钥
  2. TCP服务收到客户端连接之后调用SSL_accept进行四次握手
  3. SSL握手建立完成之后调用SSL_readSSL_write收发数据
  4. 使用完成之后关闭并释放SSL资源SSL_shutdownSSL_free
#include <iostream>
#include <tchar.h>
#include <winsock.h>
#include "openssl/ssl.h"

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"libcrypto_static.lib")
#pragma comment(lib,"libssl_static.lib")

using namespace std;

int main()
{
    WSADATA wsdata;
    int errcode = WSAStartup(MAKEWORD(2, 2), &wsdata);
    if (errcode != 0)
    {
        std::cout << "WSAStartup failed,errcode:%d" << errcode<<std::endl;
        return 0;
    }

    //初始化ssl
    SSL_library_init();
    //加载ssl算法库
    OpenSSL_add_all_algorithms();
    //加载ssl 错误信息
    SSL_load_error_strings();
    //以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Context
    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_server_method());
    if (ssl_ctx == nullptr)
    {
		std::cout << "SSL_CTX_new failed"<<std::endl;
		return 0;
    }

    //加载数字证书
   if (SSL_CTX_use_certificate_chain_file(ssl_ctx, "ca.crt") <= 0)
    {
		printf("SSL_CTX_use_certificate_chain_file failed\r\n");
		return 0;
    }

    //加载私钥
    if (SSL_CTX_use_PrivateKey_file(ssl_ctx, "ca.key", SSL_FILETYPE_PEM) <= 0)
    {
		printf("SSL_CTX_use_PrivateKey_file failed\r\n");
		return 0;
    }
    // 检查用户私钥是否正确 
    if (!SSL_CTX_check_private_key(ssl_ctx))
    {
        printf("SSL_CTX_check_private_key failed\r\n");
        return 0;
    }
    
    SSL_CTX_set_timeout(ssl_ctx, 100);
    //创建socket->bind->list->accept
    SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock == INVALID_SOCKET)
    {
		printf("socket create failed\r\n");
		return 0;
    }

    //setsocketopt resuse port
    unsigned short port = 32100;
    sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    if (SOCKET_ERROR == bind(listen_sock, (sockaddr*)&sin, sizeof(sin)))
    {
		printf("socket bind failed\r\n");
		return 0;
    }

    if (SOCKET_ERROR == listen(listen_sock, 5))
    {
		printf("socket listen failed\r\n");
		return 0;
    }

    printf("tcp ssl server:%d\r\n", port);
    while (true)
	{
		char szbuf[1024] = "";
        sockaddr_in peer_addr;
        int naddr_len = sizeof(peer_addr);
        printf("wait client...\r\n");
        SOCKET soct_peer = accept(listen_sock, (sockaddr*)&peer_addr, &naddr_len);
        printf("accept client ,socket:%d\r\n", soct_peer);
        //将socket和ssl绑定(ssl握手)
        SSL* ssl_peer = SSL_new(ssl_ctx);
        if (ssl_peer == nullptr)
        {
            printf("socket:%d,SSL_new failed\r\n", soct_peer);
            goto freessl;
        }
        SSL_set_fd(ssl_peer, soct_peer);
        
        //这里会无限阻塞,为了安全应该异步增加一个超时值,如果超时仍然未连接上则应该close
        if (SSL_accept(ssl_peer) < 0)
        {
            printf("socket:%d,SSL_accept failed\r\n", soct_peer);
            goto freessl;
        }

        //开始ssl读写
        SSL_read(ssl_peer, szbuf, 1024);
        printf("socket[%d] read:%s\r\n", soct_peer, szbuf);
        SSL_write(ssl_peer, szbuf, lstrlenA(szbuf));
    freessl:
		shutdown(soct_peer, 0);
		closesocket(soct_peer);
		
        //释放ssl
        if(ssl_peer)
            SSL_free(ssl_peer);

        continue;
    }
    return 0;
}
TCP SSL 客户端端步骤
  1. TCP客户端连接成功之后调用SSL_connect进行四次握手
  2. SSL握手建立完成之后调用SSL_readSSL_write收发数据
  3. 使用完成之后关闭并释放SSL资源SSL_shutdownSSL_free
#include <iostream>
#include <tchar.h>
#include <winsock.h>
#include "openssl/ssl.h"

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"libcrypto_static.lib")
#pragma comment(lib,"libssl_static.lib")

using namespace std;

int main()
{
	WSADATA wsdata;
	int errcode = WSAStartup(MAKEWORD(2, 2), &wsdata);
	if (errcode != 0)
	{
		std::cout << "WSAStartup failed,errcode:%d" << errcode << std::endl;
		return 0;
	}

	//初始化ssl
	SSL_library_init();
	//加载ssl算法库
	OpenSSL_add_all_algorithms();
	//加载ssl 错误信息
	SSL_load_error_strings();
	//以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Context
	SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
	if (ssl_ctx == nullptr)
	{
		std::cout << "SSL_CTX_new failed" << std::endl;
		return 0;
	}
	//SSL_CTX_set_timeout(ssl_ctx, 100);
	//创建socket->connect
	SOCKET cli_sock = socket(AF_INET, SOCK_STREAM, 0);
	if (cli_sock == INVALID_SOCKET)
	{
		printf("socket create failed\r\n");
		return 0;
	}

	//setsocketopt resuse port
	sockaddr_in sin;
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(32100);
	sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (SOCKET_ERROR == connect(cli_sock, (sockaddr*)&sin, sizeof(sin)))
	{
		printf("socket connect failed\r\n");
		return 0;
	}
	printf("connect client ,socket:%d\r\n", cli_sock);
	//将socket和ssl绑定(ssl握手)
	char szbuf[1024] = "Hellow word!!!";
	SSL* ssl_peer = SSL_new(ssl_ctx);
	if (ssl_peer == nullptr)
	{
		printf("socket:%d,SSL_new failed\r\n", cli_sock);
		goto freessl;
	}
	SSL_set_fd(ssl_peer, cli_sock);

	//这里会无限阻塞,为了安全应该异步增加一个超时值,如果超时仍然未连接上则应该close
	if (SSL_connect(ssl_peer) < 0)
	{
		printf("socket:%d,SSL_accept failed\r\n", cli_sock);
		goto freessl;
	}
	printf("ssl connectd ok\r\n");
	SSL_write(ssl_peer, szbuf, lstrlenA(szbuf));
	//开始ssl读写
	memset(szbuf, 0, 1024);
	SSL_read(ssl_peer, szbuf, 1024);
	printf("socket[%d] read:%s\r\n", cli_sock, szbuf);
freessl:
	shutdown(cli_sock, 0);
	closesocket(cli_sock);

	//释放ssl
	if (ssl_peer)
		SSL_free(ssl_peer);

	return 0;
}
NOTE

1. 服务端和客户端初始化SSL_CTX参数不同

//以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Context

//客户端调用openssl客户端的方法
SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());

//服务端调用openssl服务端的方法
SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_server_method());

2. SSL_acceptSSL_connect阻塞函数
在同步调用 SSL_acceptSSL_connect的时候会进行阻塞,需要定时检测是否超时,如果超时则关闭当前socket,防止恶意链接。例如拿非openssl的客户端连接openssl的服务端,此时不会存在四次握手,在未发送数据前会一直阻塞下去,等待握手的完成。

相关推荐

  1. 基于openssl实现TCP双向认证

    2024-04-12 18:42:03       17 阅读
  2. 基于openssl实现AES ECB加解密

    2024-04-12 18:42:03       8 阅读
  3. HTTPS双向认证

    2024-04-12 18:42:03       45 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-12 18:42:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-12 18:42:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-12 18:42:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-12 18:42:03       18 阅读

热门阅读

  1. 去锁寻秘:全方位揭秘MySQL的锁机制

    2024-04-12 18:42:03       19 阅读
  2. MySQL——Linux安装教程

    2024-04-12 18:42:03       15 阅读
  3. 【C语言】:内存函数

    2024-04-12 18:42:03       16 阅读
  4. sipp压测freeswitch第2篇Fs配置

    2024-04-12 18:42:03       14 阅读
  5. c语言中,一些全局变量和局部变量的定义及区别

    2024-04-12 18:42:03       12 阅读
  6. 自动糊测试原理,怎么理解?【UI自动化】

    2024-04-12 18:42:03       15 阅读
  7. Vue3---基础4(响应式数据) Vue2

    2024-04-12 18:42:03       15 阅读
  8. 杨扬小朋友求职

    2024-04-12 18:42:03       15 阅读
  9. 100美元如何做安全防御?

    2024-04-12 18:42:03       14 阅读
  10. MyBatis 执行流程

    2024-04-12 18:42:03       20 阅读
  11. C++中不同变量(内置/类 类型) 的初始化规则

    2024-04-12 18:42:03       16 阅读