当前位置:   article > 正文

基于openssl实现TCP双向认证

基于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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
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());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/431124
推荐阅读
相关标签
  

闽ICP备14008679号