linux网络编程-tls客户端(持续更新)

数据结构

SSL_CTX

主要用于SSL握手前的环境准备,设置CA文件和目录、设置SSL握手中的证书文件和私钥、设置协议版本以及其他一些SSL握手时的选项,多个连接可共用一个SSL_CTX对象

SSL

主要用于SSL握手以及传送应用数据

SSL_SESSION

保存了主密钥、session id、读写加解密钥、读写MAC密钥等信息

三者关系

SSL_CTX中缓存了所有SSL_SESSION信息,SSL中包含SSL_CTX。一般SSL_CTX的初始化在程序最开始调用,然后再生成SSL数据结构。由于SSL_CTX中缓存了所有的SESSION,新生成的SSL结构又包含SSL_CTX数据,所以通过SSL数据结构能查找以前用过的SESSION id,实现SESSION重用

函数说明

SSL_CTX相关函数(初始化密码列表、会话缓存设置、回调、密钥和证书以及它们默认值的选项)

SSL_CTX_new

/****************************************************************************
函 数 名  : SSL_CTX_new
功能描述  : 创建SSL_CTX对象
输入参数  : const SSL_METHOD *method	连接方法
返 回 值  : SSL_CTX * CTX对象 NULL 失败
**************************************************************************/
SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);

SSL_CTX_new 连接方法

 1. 客户端服务器协商后选SSLv3、TLSv1、TLSv1.1、TLSv1.2和TLSv1.3的最高版本
    TLS_method(), TLS_server_method(), TLS_client_method()
 2. 不建议使用这些函数。它们已分别被上述TLS_method()、TLS_server_method()和TLS_client_method()所取代
    SSLv23_method(), SSLv23_server_method(), SSLv23_client_method()
 3. TLSv1.2协议
    TLSv1_2_method(), TLSv1_2_server_method(), TLSv1_2_client_method()
 4. TLSv1.1协议
    TLSv1_1_method(), TLSv1_1_server_method(), TLSv1_1_client_method()
 5. TLSv1协议
    TLSv1_method(), TLSv1_server_method(), TLSv1_client_method()
 6. SSLv3协议SSLv3协议已弃用,不应使用
	SSLv3_method(), SSLv3_server_method(), SSLv3_client_method()
 7. DTLS 1.0和DTLS 1.2
	DTLS_method(), DTLS_server_method(), DTLS_client_method()
 8. DTLS 1.2
	DTLSv1_2_method(), DTLSv1_2_server_method(), DTLSv1_2_client_method()
 9. DTLS 1.0
	DTLSv1_method(), DTLSv1_server_method(), DTLSv1_client_method()

证书相关函数

SSL_CTX_load_verify_locations

/****************************************************************************
函 数 名  : SSL_CTX_load_verify_locations
功能描述  : 加载CA证书
输入参数  : SSL_CTX *ctx       SSL_CTX对象
		  const char *CAfile  证书名称
          const char *CApath  证书路径
返 回 值  : int 0 失败(找不到证书) 1 成功
OpenSSL库将首先搜索CAfile中的证书,然后是CApath中的证书
**************************************************************************/
int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
                                   const char *CApath);

SSL_CTX_set_default_verify_paths

 int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx);

SSL_CTX_set_default_verify_paths

 int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx);

SSL_CTX_set_default_verify_file

 int SSL_CTX_set_default_verify_file(SSL_CTX *ctx);

SSL相关函数

SSL_new

/*创建一个新的SSL结构,该结构用于保存TLS/SSL连接的数据。新结构继承了底层上下文ctx的设置:连接方法、选项、验证设置、超时设置。SSL结构被引用计数。第一次创建SSL结构会增加引用计数。释放它(使用SSL free)会减少它。当引用计数降至零时,将释放分配给SSL结构的任何内存或资源*/
/****************************************************************************
函 数 名  : SSL_new
功能描述  : 创建SSL对象
输入参数  : SSL_CTX *ctx CTX对象
返 回 值  : SSL * 成功 SSL对象 失败 NULL
**************************************************************************/
SSL *SSL_new(SSL_CTX *ctx)

SSL_free

/****************************************************************************
函 数 名  : SSL_free
功能描述  : 释放SSL对象
输入参数  : SSL *ssl SSL对象
返 回 值  : 无
**************************************************************************/
void SSL_free(SSL *ssl);

SSL_set_fd

/****************************************************************************
函 数 名  : SSL_set_fd
功能描述  : 将套接字与ssl对象关联
输入参数  : SSL *ssl SSL对象
		   int fd 套接字
返 回 值  : int 0 失败 1 成功
**************************************************************************/
int SSL_set_fd(SSL *ssl, int fd);

SSL_set_rfd

/****************************************************************************
函 数 名  : SSL_set_rfd
功能描述  : 将套接字的读通道与ssl对象关联
输入参数  : SSL *ssl SSL对象
		   int fd 套接字
返 回 值  : int 0 失败 1 成功
**************************************************************************/
int SSL_set_rfd(SSL *ssl, int fd);

SSL_set_rfd

/****************************************************************************
函 数 名  : SSL_set_wfd
功能描述  : 将套接字的写通道与ssl对象关联
输入参数  : SSL *ssl SSL对象
		   int fd 套接字
返 回 值  : int 0 失败 1 成功
**************************************************************************/
int SSL_set_wfd(SSL *ssl, int fd);

SSL_connect

/****************************************************************************
函 数 名  : SSL_connect
功能描述  : 将套接字的写通道与ssl对象关联
输入参数  : SSL *ssl SSL对象
返 回 值  : int <=0 失败 1 成功
SSL_get_error()可查找原因
**************************************************************************/
int SSL_connect(SSL *ssl);

SSL_get_error

/*SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read_ex(),
SSL_read(), SSL_peek_ex(), SSL_peek(), SSL_write_ex() or SSL_write() 失败可用*/
/****************************************************************************
函 数 名  : SSL_get_error
功能描述  : 根据失败的返回值获取错误码
输入参数  : const SSL *ssl ssl对象
           int ret 上述函数失败的返回值
返 回 值  : int 错误码
**************************************************************************/
int SSL_get_error(const SSL *ssl, int ret);
SSL_get_error错误码
SSL_ERROR_NONE  0
SSL_ERROR_ZERO_RETURN  6
SSL_ERROR_WANT_READ 2, SSL_ERROR_WANT_WRITE 3
SSL_ERROR_WANT_CONNECT 7, SSL_ERROR_WANT_ACCEPT 8
SSL_ERROR_WANT_X509_LOOKUP 4
SSL_ERROR_WANT_ASYNC
SSL_ERROR_WANT_ASYNC_JOB
SSL_ERROR_WANT_CLIENT_HELLO_CB
SSL_ERROR_SYSCALL 5
SSL_ERROR_SSL 1

SSL_connect

/****************************************************************************
函 数 名  : SSL_connect
功能描述  : 启动与服务器的TLS/SSL握手
输入参数  : const SSL *ssl ssl对象
返 回 值  : int <=0 失败 1 成功
**************************************************************************/
int SSL_connect(SSL *ssl);

SSL_write

/****************************************************************************
函 数 名  : SSL_write
功能描述  : 发送数据
输入参数  : SSL *ssl SSL对象 
		  const void *buf 待发送数据
		  int num		  待发送数据长度
返 回 值  : int <=0 失败 >0 成功写入的数据长度
**************************************************************************/
 int SSL_write(SSL *ssl, const void *buf, int num);

SSL_read

/****************************************************************************
函 数 名  : SSL_read
功能描述  : 读数据
输入参数  : SSL *ssl SSL对象 
		  void *buf 	  读缓存
		  int num		  读取长度
返 回 值  : int <=0 失败 >0 成功读取的数据长度
**************************************************************************/
int SSL_read(SSL *ssl, void *buf, int num);

实例代码

编译链接库 -lssl -lcrypto

#include <openssl/ssl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> 
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <openssl/err.h>

#define CA_FILE "/mnt/nfs/ca.crt"
#define SERVER_PORT 8018
#define SOKCETLIST_MAX    2
#define TIMEOUT_SEC       5
#define TIMEOUT_USEC      0

typedef struct
{
    int use;
    int sock;
}SocketList_t;

SocketList_t g_sock_list[SOKCETLIST_MAX];

int socketlist_init(void)
{
    int i = 0;

    for (i = 0; i < SOKCETLIST_MAX; i++)
    {
        g_sock_list[i].use  = 0;
        g_sock_list[i].sock = -1;
    }

}

int socketlist_add(int sock)
{
    int i = 0;

    for (i = 0; i < SOKCETLIST_MAX; i++)
    {
        if (g_sock_list[i].use)
        {
            continue;
        }

        g_sock_list[i].use  = 1;
        g_sock_list[i].sock = sock;

        return 0;
    }

    return -1;
}

void socketlist_del(int sock)
{
    int i = 0;

    for (i = 0; i < SOKCETLIST_MAX; i++)
    {
        if (g_sock_list[i].use && g_sock_list[i].sock == sock)
        {
            g_sock_list[i].use  = 0;
            g_sock_list[i].sock = -1;
            return;
        }
    }
}

int socketlist_fdmax_get(void)
{
    int i      = 0;
    int maxfd = -1;

    for (i = 0; i < SOKCETLIST_MAX; i++)
    {
        if (!g_sock_list[i].use)
        {
            continue;
        }

        maxfd = g_sock_list[i].sock >= maxfd ? g_sock_list[i].sock : maxfd;
    }

    return maxfd;
}

void sock_init_before_select(fd_set *readset, fd_set *writeset, fd_set *errorfds)
{
    int i = 0;

    if (readset)
    {
        FD_ZERO(readset);
    }
    if (writeset)
    {
        FD_ZERO(writeset);
    }
    if (errorfds)
    {
        FD_ZERO(errorfds);
    }

    for (i = 0; i < SOKCETLIST_MAX; i++)
    {
        if (!g_sock_list[i].use)
        {
            continue;
        }

        if (readset)
        {
            FD_SET(g_sock_list[i].sock, readset);
        }

        if (writeset)
        {
            FD_SET(g_sock_list[i].sock, writeset);
        }

        if (errorfds)
        {
            FD_SET(g_sock_list[i].sock, errorfds);
        }
    }
}

void printf_array(unsigned char *array, int size)
{
    int i;

    printf("HEX(%d): ",size);
    for(i=0;i<size;i++)
    {
        printf("%02x",array[i]);
    }
    printf("\n");
}

void tls_begin(void)
{
    SSL_library_init();         //SSL库初始化
    SSL_load_error_strings();   //加载错误码的描述字符串
    OpenSSL_add_all_algorithms();
}

int main(void *arg, char *argv[])
{
    int i = 0;
    int ret = 0;
    int sock = 0;
    int max_fd = 0;
    SSL_CTX *ctx = NULL;
    SSL *ssl     = NULL;
    struct sockaddr_in serveraddr;
    socklen_t serveraddr_len = 0;
    fd_set writeset;
    fd_set readset;
    fd_set errorfds;
    struct timeval timeout = {TIMEOUT_SEC, TIMEOUT_USEC};
    unsigned char recv_buf[512];
    unsigned char snd_buf[512] = {0xFD,0xFD,0x15,0x00,0x7B,0x09,0x22,0x6F,
                                    0x70,0x22,0x3A,0x20,0x32,0x2C,0x0A,0x09,
                                    0x22,0x73,0x6E,0x22,0x3A,0x20,0x32,0x0A,0x7D,0x16};

    tls_begin();

    ctx = SSL_CTX_new(TLS_client_method());
    if (!ctx)
    {
        return -1;
    }

    ret = SSL_CTX_load_verify_locations(ctx, CA_FILE, NULL);        //加载CA证书,服务器提供
    if (0 == ret)
    {
        return -1;
    }

    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (-1 == sock)
    {
        return -1;
    }

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(SERVER_PORT);
    serveraddr.sin_addr.s_addr = inet_addr("192.168.2.220");
    serveraddr_len = sizeof(serveraddr);

    ret = connect(sock, (struct sockaddr *)&serveraddr, serveraddr_len);
    if (-1 == ret)
    {
        return -1;
    }

    ssl = SSL_new(ctx);                                            //ssl句柄
    if (!ssl)
    {
        printf("Error SSL_new\n");
        return -1;
    }

    socketlist_add(sock);

    SSL_set_fd(ssl, sock);                                         //将套接字与ssl对象关联
    if ((ret = SSL_connect(ssl)) != 1)                              //握手
    {
        printf("Handshake Error %d\n", SSL_get_error(ssl, ret));
        return -1;
    }
#if 0
    int offset = 0;
    short start_flg = 0xFDFD;
    char heartbeat[] = "{\"sId\":3004, \"sn\":1}";
    short length = strlen(heartbeat);

    memcpy(snd_buf+offset, &start_flg, sizeof(start_flg));
    offset += sizeof(start_flg);
    memcpy(snd_buf+offset, &length, sizeof(length));
    offset += sizeof(length);
    memcpy(snd_buf+offset, heartbeat, strlen(heartbeat));
    offset += strlen(heartbeat);
    snd_buf[offset] = 0x16;
    offset += 1;
    SSL_write(ssl, snd_buf, offset);
#endif
    SSL_write(ssl, "一个养猫的程序员", strlen("一个养猫的程序员"));

    while (1)
    {
        max_fd = socketlist_fdmax_get();
        sock_init_before_select(&readset, NULL, NULL);
        printf("time sec:%ld,usec:%ld\r\n", timeout.tv_sec, timeout.tv_usec);
        timeout.tv_sec  = TIMEOUT_SEC;
        timeout.tv_usec = TIMEOUT_USEC;
        ret = select(max_fd+1, &readset, NULL, NULL, &timeout);
        if (-1 == ret)
        {
            printf("select error:%d,%s\r\n", errno, strerror(errno));
        }
        else if (0 == ret)
        {
            printf("select time out\r\n");
        }
        else
        {
            for (i = 0; i < SOKCETLIST_MAX; i++)
            {
                if (g_sock_list[i].use && FD_ISSET(g_sock_list[i].sock, &readset))
                {
                    ret = SSL_read(ssl, recv_buf, sizeof(recv_buf));
                    if (ret <= 0)
                    {
                        printf("SSL_read Error %d, %ld\n", SSL_get_error(ssl, ret), ERR_get_error());
                        
                        goto end;
                    }
                    printf_array(recv_buf , ret);
                }
            }
        }
    }

end:
    SSL_shutdown(ssl);
    socketlist_del(sock);
    SSL_free(ssl);
    SSL_CTX_free(ctx);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值