目录
1、ssl服务初始化
void ssl_service_init(void)
{
//ssl库初始化
SSL_library_init();
//载入ssl算法
OpenSSL_add_all_algorithms();
//载入ssl错误消息
SSL_load_error_strings();
}
2、服务端初始化
SSL_CTX *ssl_server_init(int verify_type, const char *ca, const char *user_cert, const char *user_key)
{
//创建SSL_CTX Content Text),兼容 ssl v2 和 v3 版本
//SSLv2_server_method() v2, SSLv3_server_method() v3
SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL)
{
ERR_print_errors_fp(stderr);
return NULL;
}
if (verify_type == SSL_VERIFY_NONE)
{
//单向认证:SSL_VERIFY_NONE 不认证对方
SSL_CTX_set_verify(ctx,SSL_VERIFY_NONE,NULL);
}
else if (verify_type == SSL_VERIFY_PEER)
{
//双向验证
//SSL_VERIFY_PEER:要求对证书进行认证,没有证书也会放行
//SSL_VERIFY_FAIL_IF_NO_PEER_CERT:要求客户端需要提供证书,但验证发现单独使用没有证书也会放行
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
//设置信任根证书
if (SSL_CTX_load_verify_locations(ctx, ca, NULL) <= 0)
{
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return NULL;
}
}
else
{
SSL_CTX_free(ctx);
return NULL;
}
//载入用户的数字证书,此证书用来发送给客户端,证书里包含有公钥
if (SSL_CTX_use_certificate_file(ctx, user_cert, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return NULL;
}
//载入用户私钥
if (SSL_CTX_use_PrivateKey_file(ctx, user_key, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return NULL;
}
//检查用户私钥是否正确
if (!SSL_CTX_check_private_key(ctx))
{
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return NULL;
}
return ctx;
}
3、客户端初始化
SSL_CTX *ssl_client_init(int verify_type, const char *ca, const char *user_cert, const char *user_key)
{
SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
return NULL;
}
if (verify_type == SSL_VERIFY_NONE)
{
SSL_CTX_set_verify(ctx,SSL_VERIFY_NONE,NULL);
}
else if (verify_type == SSL_VERIFY_PEER)
{
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
if (SSL_CTX_load_verify_locations(ctx, ca,NULL) <= 0)
{
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return NULL;
}
if (SSL_CTX_use_certificate_file(ctx, user_cert, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return NULL;
}
if (SSL_CTX_use_PrivateKey_file(ctx, user_key, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return NULL;
}
if (!SSL_CTX_check_private_key(ctx))
{
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return NULL;
}
}
else
{
SSL_CTX_free(ctx);
return NULL;
}
return ctx;
}
4、封装ssl握手函数
/*
handshake_type: 1 connect 状态
2 accept 状态
*/
SSL *sslDoHandshake(int handshake_type, SSL_CTX *ctx, int fd, int *err_code)
{
if (ctx == NULL)
{
return NULL;
}
//基于 ctx 创建一个新的 SSL 句柄
SSL *ssl = SSL_new(ctx);
if (!ssl)
{
ssl = NULL;
return NULL;
}
//将 socket 加入到 SSL
if (!SSL_set_fd(ssl, fd))
{
SSL_free(ssl);
ssl = NULL;
return NULL;
}
if (handshake_type == 1)
{
//设置 SSL 为 connect 状态
SSL_set_connect_state(ssl);
}
else if (handshake_type == 2)
{
//设置 SSL 为 accept 状态
SSL_set_accept_state(ssl);
}
else
{
SSL_free(ssl);
ssl = NULL;
return NULL;
}
int ret, err, cnt = 0;
while ((ret = SSL_do_handshake(ssl)) != 1)
{
err = SSL_get_error(ssl, ret);
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ)
{
usleep(100*1000);
cnt++;
if (cnt == 10) //防止 SSL_do_handshake 一直失败,无法跳出循环
{
SSL_free(ssl);
ssl = NULL;
break;
}
continue;
}
else
{
*err_code = err;
//err == 5 SSL_ERROR_SYSCALL 关闭相关资源,重新连接
printf("SSL_do_handshake failed, err_code : %d\n", err);
ERR_print_errors_fp(stderr);
SSL_shutdown(ssl);
SSL_free(ssl);
ssl = NULL;
break;
}
}
printf("SSL_do_handshake, ret : %d\n", ret);
return ssl;
}
5、封装SSL_read函数
/*
SSL_read(SSL *ssl,char *buf,int num)
正常:返回1到num个字节数
错误:返回0或负数
*/
int sslRead(SSL *ssl, int fd, char *buf, int len, int timeout)
{
if (ssl == NULL)
{
return -1;
}
int err, res, ret, bytes = 0, cnt = 0;
fd_set rfds;
struct timeval tv;
if (timeout > 0)
{
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
}
do
{
if (timeout > 0)
{
tv.tv_sec = timeout/1000;
tv.tv_usec = timeout%1000;
ret = select(fd + 1, &rfds, NULL, NULL, &tv);
if (ret == 0)
{
printf("time out\n");
return -2;
}
else if (ret < 0)
{
return -1;
}
if(!FD_ISSET(fd,&rfds))
{
return -1;
}
}
res = SSL_read(ssl, buf + bytes, len);
if (res >= 1)
{
cnt = 0;
bytes += res;
len -= res;
}
else
{
err = SSL_get_error(ssl, res);
if (err == SSL_ERROR_WANT_READ)
{
usleep(100*1000);
cnt++;
if (cnt == 5)
{
return -2;
}
continue;
}
else
{
printf("ssl read failed...\n");
ERR_print_errors_fp(stderr);
if (err == SSL_ERROR_SYSCALL) //可能是对方已经关闭连接,因此要关闭相关资源,重新连接
{
return -5;
}
return -1;
}
}
}
while(len > 0 && SSL_pending(ssl)); //SSL_pending(ssl)返回可读的ssl数据字节数
return bytes;
}
注:当返回0时,可能是SSL_pending的问题导致未进入循环,应再次调用sslRead
6、封装SSL_write函数
/*
SSL_write(SSL *ssl,const char *buf,int num)
正常:返回1到num个字节数
错误:返回0或负数
*/
int sslWrite(SSL *ssl, char *buf, int len)
{
if (ssl == NULL)
{
return -1;
}
int err, res, bytes = 0, cnt = 0;
while(len > 0)
{
res = SSL_write(ssl, buf + bytes, len);
if (res >= 1)
{
cnt = 0;
bytes += res;
len -= res;
}
else
{
err = SSL_get_error(ssl, res);
if (err == SSL_ERROR_WANT_WRITE)
{
usleep(100*1000);
cnt++;
if (cnt == 5)
{
return -2;
}
continue;
}
else
{
printf("ssl write failed...\n");
ERR_print_errors_fp(stderr);
if (err == SSL_ERROR_SYSCALL) //可能是对方已经关闭连接,因此要关闭相关资源,重新连接
{
return -5;
}
return -1;
}
}
}
printf("ssl write, bytes:%d\n", bytes);
return bytes;
}
7、demo示例
int main(int argc, char const *argv[])
{
//客户端demo流程
/*
ssl_service_init
ssl_client_init
创建socket
sslDoHandshake
sslRead或sslWrite
*/
//服务端demo流程
/*
ssl_service_init
ssl_server_init
创建socket
sslDoHandshake
sslRead或sslWrite
*/
return 0;
}
8、总结与说明
8.1、openssl官方函数:https://www.ibm.com/docs/en/ztpf/2021?topic=services-ztpf-cc-apis
8.2、如果移植到设备上运行时出现未知错误,可能是编译openssl时存在问题
8.3、如果出现 SSL_ERROR_SYSCALL,没有重新初始化ssl,将会出现以下问题,导致CPU异常偏高
[ 1954.054545] [drm:rot_client_get_device] *ERROR* wait2start timeout. timeout=5jif, ret=0
[ 1954.074862] [drm:rot_client_exec_fb_sync] *ERROR* wait4finish timeout: 5jif --> 0
——————————————
仅供学习与参考