数据结构
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;
}