• Linux C/C++实现SSL的应用层VPN (MiniVPN)


    SSL协议和VPN(虚拟私人网络)原理是网络安全领域中的两个重要概念。

    SSL协议,全称安全套接层(Secure Sockets Layer),是一种广泛应用于互联网的安全协议,主要在两个通信端点之间建立安全连接,以保护数据的传输安全。具体来说,SSL通过使用公钥加密算法实现数据的加密和解密,在客户端和服务器之间建立安全的通信通道。它还使用数字证书来验证通信双方的的身份,一旦身份验证成功,SSL就会使用加密算法对通信数据进行加密,确保数据在传输过程中不被篡改或窃取。

    VPN是一种可以在公共网络上建立加密通道的技术,通过这种技术可以使远程用户访问公司内部网络资源时,实现安全的连接和数据传输。VPN通常是通过虚拟专用网络(Virtual Private Network)来实现的,即在公共网络上建立一个虚拟的专用网络,将用户的数据流量加密并隧道化,使得数据在传输过程中无法被窃听和篡改。

    总的来说,SSL协议和VPN原理都是为了实现网络安全而设计的。SSL协议主要保护数据的传输安全,而VPN技术则是在公共网络上建立加密通道,使得数据在传输过程中更加安全。

    OpenSSL库用于实现SSL的应用层VPN

    OpenSSL是一个功能强大的开源SSL库,它提供了丰富的API和工具,可以用于实现SSL/TLS协议、加密算法、证书处理等功能。它还包含了IPSec和L2TP等VPN协议的实现。

    使用OpenSSL库实现SSL的应用层VPN需要用到以下一些函数:

    SSL_CTX_new(const SSL_METHOD *method):创建新的SSL上下文结构体。
    SSL_new(SSL_CTX *ctx):基于SSL上下文创建一个新的SSL结构体。
    SSL_set_fd(SSL *ssl, int fd):将SSL结构体的文件描述符设置为传入的文件描述符。
    SSL_set_connect_state(SSL *ssl):设置SSL结构体为客户端模式。
    SSL_do_handshake(SSL *ssl):执行SSL握手过程,与对方建立安全的连接。
    SSL_write(SSL *ssl, const void *buf, int len):向对方发送数据。
    SSL_read(SSL *ssl, void *buf, int len):从对方接收数据。
    SSL_shutdown(SSL *ssl):关闭SSL连接,发送关闭通知并终止会话。
    SSL_free(SSL *ssl):释放SSL结构体及其相关资源。
    SSL_CTX_free(SSL_CTX *ctx):释放SSL上下文及其相关资源。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这些是OpenSSL库中一些常用的函数,它们用于在C语言中实现SSL的应用层VPN。在实际开发中,你可能还需要查看OpenSSL的文档和示例代码以获得更详细的信息和指导。

    证书颁发机构

    证书颁发机构是SSL协议中非常重要的一个环节,它负责为服务器颁发数字证书,以验证服务器的身份。客户端在和服务器建立连接时,会验证服务器的身份,以确保连接的安全性。

    密钥交换

    SSL协议使用密钥交换协议来协商客户端和服务器之间的加密密钥。密钥交换协议包括RSA密钥交换、Diffie-Hellman密钥交换等,它们都可以用于在客户端和服务器之间建立一个安全的通信通道。

    Linux C/C++实现SSL的应用层VPN(实现MiniVPN)

    • vpn_client:

    要使用OpenSSL命令生成CA证书(ca.crt)、客户端证书(client.crt)、客户端密钥(client.key)以及客户端的证书请求(client.crs),可以按照以下步骤进行操作:
    生成CA证书(ca.crt):

    openssl genrsa -out ca.key 2048
    openssl req -new -x509 -days 365 -key ca.key -sha256 -out ca.crt
    
    • 1
    • 2

    上述命令将生成一个2048位的RSA私钥(ca.key)并使用它创建一个自签名的CA证书(ca.crt)。
    生成客户端证书(client.crt)和客户端密钥(client.key):

    openssl genrsa -out client.key 2048
    openssl req -new -key client.key -out client.csr
    
    • 1
    • 2

    上述命令将生成一个2048位的RSA私钥(client.key)并使用它创建一个证书请求(client.csr)。
    生成客户端证书请求的证书(client.crs):

    openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt
    
    • 1

    上述命令将使用CA证书(ca.crt)和CA私钥(ca.key)对客户端证书请求(client.csr)进行签名,生成客户端证书(client.crt)。
    完成以上步骤后,你将得到以下文件:
    ca.crt:CA证书
    client.crt:客户端证书
    client.key:客户端密钥
    client.crs:客户端证书请求(通常不需要使用,但可以保留作为记录)

    int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx);
    SSL *setupTLSClient(const char *hostname);
    int setupTCPClient(const char *hostname, int port);
    int createTunDevice();
    int try_login(SSL *ssl);
    void sendto_TUN(SSL *ssl, int tunfd);
    void sendto_SSL(SSL *ssl, int tunfd);
    ...
    #define CERTF HOME "client.crt"
    #define KEYF HOME "client.key"
    #define CACERT HOME "ca.crt"
    ...
    int main(int argc, char *argv[])
    {
    ...
    
        /*------ Destination initialization ------*/
        printf(PREFIX "Enter server name:");
        scanf("%s", hostname);
        printf(PREFIX "Enter port:");
        scanf("%d", &port);
        /*------ TLS initialization ------*/
        SSL *ssl = setupTLSClient(hostname);
        /*------ TCP connection ------*/
        int sockfd = setupTCPClient(hostname, port);
        /*------ TLS handshake ------*/
        SSL_set_fd(ssl, sockfd);
        int err = SSL_connect(ssl);
        CHK_SSL(err);
        printf(PREFIX "SSL connected! \n");
        printf(PREFIX "SSL connection using %s\n", SSL_get_cipher(ssl));
        /*------ Authenticating ------*/
        int ret = try_login(ssl);
        //login failed
        if (ret < 0){
            printf(PREFIX"Login failed!\n");
            SSL_shutdown(ssl);
            SSL_free(ssl);
            close(sockfd);
            return 0;
        }
        printf(PREFIX "Login successfully!\n");
        /*------ Allocate IP ------*/
        char client_IP[64] = {0};
        char cmd[100];
        SSL_read(ssl, client_IP, sizeof(client_IP));
        printf(PREFIX "Auto-assigned IP:%s\n", client_IP);
        /*------ Add route ------*/
        int tunfd = createTunDevice();
        sprintf(cmd, "sudo ifconfig tun0 %s/24 up", client_IP);
        system(cmd);
        sprintf(cmd, "sudo route add -net 192.168.60.0/24 tun0");
        system(cmd);
        /*------ Listen sock&tun0 ------*/
        while (1)
        {
            fd_set readFDSet;
            int ret;
            FD_ZERO(&readFDSet);
            FD_SET(sockfd, &readFDSet);
            FD_SET(tunfd, &readFDSet);
            ret = select((sockfd > tunfd ? sockfd : tunfd) + 1, &readFDSet, NULL, NULL, NULL);
            if (FD_ISSET(sockfd, &readFDSet))
            {
                ret = sendto_TUN(ssl, tunfd);
                // 服务端关闭会话
                if (ret == -1)
                {
                    printf(PREFIX "Server disconnected!\n");
                    SSL_shutdown(ssl);
                    SSL_free(ssl);
                    close(sockfd);
                }
            }
            ...
            if (FD_ISSET(tunfd, &readFDSet))
                sendto_SSL(ssl, tunfd);
        }
        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
    • vpn_server:

    您可以使用以下步骤生成CA证书(ca.crt)、服务器证书(server.crt)、服务器密钥(server.key)以及服务器证书请求(server.crs)文件:
    生成CA证书(ca.crt):

    openssl genrsa -out ca.key 2048
    openssl req -new -x509 -days 365 -key ca.key -sha256 -out ca.crt
    
    • 1
    • 2

    这将生成一个2048位的RSA私钥(ca.key),然后使用它创建自签名的CA证书(ca.crt)。
    生成服务器证书(server.crt)和服务器密钥(server.key):

    openssl genrsa -out server.key 2048
    openssl req -new -key server.key -out server.csr
    
    • 1
    • 2

    这将生成一个2048位的RSA私钥(server.key),然后使用它创建一个证书请求(server.csr)。
    生成服务器证书请求的证书(server.crt):

    openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
    
    • 1

    这将使用CA证书(ca.crt)和CA私钥(ca.key)对服务器证书请求(server.csr)进行签名,生成服务器证书(server.crt)。
    完成以上步骤后,您将得到以下文件:
    ca.crt:CA证书
    server.crt:服务器证书
    server.key:服务器密钥
    server.crs:服务器证书请求(通常不需要使用,但可以保留作为记录)

    ...
    typedef struct
    {
        char client_ip[16];
        char virtual_ip[16];
        int socket_fd;
        SSL *ssl_session;
    } session_t;
    
    typedef struct
    {
        int last_byte_IP;
        bool if_valid;
    } last_byte_pool;
    void initialize_IP_POOL();
    int add_session(const char *client_ip, const char *virtual_ip, int socket_fd,SSL* ssl_session);
    session_t *find_session(const char *virtual_ip);
    void remove_session(int client_sock);
    SSL *setupTLSServer();
    int setupTCPServer();
    int createTunDevice();
    int sendto_TUN(SSL *ssl, int client_sock, int tunfd);
    void sendto_SSL(int tunfd);
    int login(char *user, char *passwd);
    int verify_user(SSL *ssl, struct sockaddr_in client_addr, int sock);
    ...
    #define CERTF HOME "server.crt"
    #define KEYF HOME "server.key"
    #define CACERT HOME "ca.crt"
    ...
    int main(int argc, char **argv)
    {
    ...
        /*------ TCP Connect ------*/
        int listen_sock = setupTCPServer();
        if (listen_sock <= 0)
            printf(PREFIX "Create listen_sock failed\n");
        /*------ tunnel init, redirect and forward ------*/
        int tunfd = createTunDevice();
        system("sudo ifconfig tun0 192.168.53.1/24 up");
        system("sudo sysctl net.ipv4.ip_forward=1");
        /*------ Initialize IP pool ------*/
        initialize_IP_POOL();
        /*------ Manage multiple tunnels ------*/
        while (1)
        {
    ...
            for (i = 0; i < MAX_SESSIONS; i++)
            {
                // 将大于0的项加入readfds
                if (session_table[i].socket_fd > 0)
                {
                    FD_SET(session_table[i].socket_fd, &readfds);
                    if (session_table[i].socket_fd > max_fd)
                        max_fd = session_table[i].socket_fd;
                }
            }
            int ret = select(max_fd + 1, &readfds, NULL, NULL, NULL);
            if (ret <= 0)
                printf(PREFIX "Select fds failed\n");
            // 当有新的客户端连接请求
            if (FD_ISSET(listen_sock, &readfds))
            {
                int new_sock = accept(listen_sock, (struct sockaddr *)&sa_client, &client_len);
                CHK_ERR(new_sock, "accept");
                printf(PREFIX "TCP accept successfully! sock:%d\n", new_sock);
                // 连接的客户端数量达到上限
                if (session_count >= MAX_SESSIONS)
                {
                    printf(PREFIX "Client connection full!\n");
                    close(new_sock);
                    continue;
                }
                //为该会话创建一个新的ssl
                SSL *ssl = setupTLSServer();
                ret = SSL_set_fd(ssl, new_sock);
                if (!ret)
                {
                    printf(PREFIX "SSL_set_fd failed\n");
                    exit(1);
                }
                int err = SSL_accept(ssl);
                fprintf(stderr, PREFIX "SSL_accept return %d\n", err);
                CHK_SSL(err);
                printf(PREFIX "SSL connection established!\n");
                int ret = verify_user(ssl, sa_client, new_sock);
                if (ret == -1)
                    printf(PREFIX "Login failed!\n");
            }
            // 从SSL链路接收数据及判断客户端是否断开连接
            for (i = 0; i < MAX_SESSIONS; i++)
            {
                if (session_table[i].socket_fd <= 0)
                    continue;
                if (FD_ISSET(session_table[i].socket_fd, &readfds))
                {
                    int client_sock = session_table[i].socket_fd;
                    SSL* ssl = session_table[i].ssl_session;
                    sendto_TUN(ssl, client_sock, tunfd);
                }
            }
            ...
            // 从tun0接收数据到SSL链路
            if (FD_ISSET(tunfd, &readfds))
            {
                sendto_SSL(tunfd);
            }
        }
        ...
        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

    If you need the complete source code, please add the WeChat number (c17865354792)

    运行结果:

    在这里插入图片描述

    Linux添加虚拟网卡(tunctl添加虚拟网卡TUN):
    在这里插入图片描述可以使用命令"route -n"来查看路由表:
    完成代码后,需要进行调试和测试,以确保VPN和SSL的正常工作,实现SSL的应用层VPN是一项复杂的任务,需要深入了解网络协议和安全原理。

    总结

    在VPN的实现过程中,SSL协议常常被用于对数据进行加密,从而确保数据的传输安全。因此,SSL VPN是使用SSL协议来实现的安全通信通道,提供更高级别的安全性。此外,VPN还可以隐藏用户的真实IP地址,提供匿名性和绕过地理限制。

    Welcome to follow WeChat official account【程序猿编码

  • 相关阅读:
    nginx网络服务配置
    2118. Build the Equation
    yolov5训练与模型量化
    桥接设计模式
    HTML5网站展示:48个潜在的Flash-Killing Demos演示
    (六)centos7案例实战——sonarQube安装及springboot项目集成sonarQube完成代码质量检查
    Socks5 与 HTTP 代理在网络安全中的应用
    中国人造金刚石行业投资战略规划及发展前景预测报告2022~2028年
    【猿创征文】Vue3 企业级优雅实战 - 组件库框架 - 3 搭建组件库开发环境
    web前端-javascript-自增++和自减--(a++和++a,都在自身基础上+1,a--和--a都在自增基础上-1,自增和自减的练习)
  • 原文地址:https://blog.csdn.net/chen1415886044/article/details/132945346