一、环境

需要提前准备好服务端和客户端的证书和私钥,以及CA的证书。

OpenSSL 1.1.1f  31 Mar 2020
built on: Wed Nov 24 13:20:48 2021 UTC
platform: debian-amd64
options:  bn(64,64) rc4(16x,int) des(int) blowfish(ptr)

Thread model: posix
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)

二、实现

2.1 简要步骤

SSL客户端和服务端的实现流程大体一致,只是在多线程处理时不一样,服务器端多了线程管理,从而保证线程安全。

客户端:

SSL_library_init();
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_use_certificate_file();
SSL_CTX_use_PrivateKey_file();
SSL_CTX_check_private_key(ctx);
SSL_CTX_load_verify_locations();
server = connect_server(ADDR, PORT);
ssl    = SSL_new(ctx);
SSL_set_fd(ssl, server);
SSL_connect(ssl);
SSL_write(ssl, req, strlen(req));
SSL_free(ssl);
close(server);
SSL_CTX_free(ctx);

服务端:

SSL_library_init();
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_use_certificate_file();
SSL_CTX_use_PrivateKey_file();
SSL_CTX_check_private_key(ctx);
SSL_CTX_load_verify_locations();
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); /* verify client cert */
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CA_CERT));
server = port_listen(PORT); /* create server socket */
ssl    = SSL_new(ctx);
SSL_set_fd(ssl, server);
SSL_accept(ssl);
SSL_write(ssl, req, strlen(req));
SSL_read(ssl, buf, sizeof(buf) - 1);
SSL_free(ssl);
close(server);
SSL_CTX_free(ctx);

2.1 客户端实现

OpenSSL 1.1.1f版本的ssl接口是支持多线程的,因此直接使用pthread编程即可。

以下源码,ssl读写是两个线程,客户端不验证服务端的证书,增加了失败重连机制。

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/bio.h>
#include <stdbool.h>
#include <signal.h> /* sigaction */
#define FAIL                -1
#define PORT                7383
#define SSL_RETRY_DELAY_SEC 300
#define ADDR                "127.0.0.1"
#define CA_CERT             "./cert/ca.crt"
#define CLIENT_CA           "./cert/cert.crt"
#define CLIENT_KEY          "./cert/pri.pem"static bool isServerDisconnect = true;
int connect_server(const char* hostname, int port)
{int sd;struct hostent* host;struct sockaddr_in addr;if ((host = gethostbyname(hostname)) == NULL) {perror(hostname);abort();}sd = socket(PF_INET, SOCK_STREAM, 0);bzero(&addr, sizeof(addr));addr.sin_family      = AF_INET;addr.sin_port        = htons(port);addr.sin_addr.s_addr = *(long*)(host->h_addr);if (connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {close(sd);perror(hostname);abort();}return sd;
}
SSL_CTX* init_ctx(void)
{SSL_CTX* ctx;OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */ERR_load_BIO_strings();ERR_load_crypto_strings();SSL_load_error_strings();                  /* Bring in and register error messages */ctx = SSL_CTX_new(SSLv23_client_method()); /* Create new context *///ctx = SSL_CTX_new(TLSv1_client_method()); /* Create new context */if (ctx == NULL) {ERR_print_errors_fp(stderr);abort();}return ctx;
}
void show_cert_file(const char* certFile)
{BIO* certBio = NULL;X509* cert   = NULL;char* line;//create BIO object to read certificatecertBio = BIO_new(BIO_s_file());//Read certificate into BIOif (!(BIO_read_filename(certBio, certFile))) {printf("reading certificate error in %s\r\n", certFile);BIO_free_all(certBio);exit(EXIT_FAILURE);}if (!(cert = PEM_read_bio_X509(certBio, NULL, 0, NULL))) {fprintf(stderr, "loading certificate error in %s\r\n", certFile);BIO_free_all(certBio);exit(EXIT_FAILURE);}line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);printf("%s\n", line);free(line);      /* free the malloc'ed string */X509_free(cert); /* free the malloc'ed certificate copy */BIO_free_all(certBio);
}
void load_cert_key_ca_file(SSL_CTX* ctx, const char* certFile, const char* keyFile, const char* caFile)
{if (!ctx) {abort();}// load certif (certFile && SSL_CTX_use_certificate_file(ctx, certFile, SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stderr);abort();}// load private keyif (keyFile && SSL_CTX_use_PrivateKey_file(ctx, keyFile, SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stderr);abort();}// check private keyif (certFile && keyFile && !SSL_CTX_check_private_key(ctx)) {ERR_print_errors_fp(stderr);abort();}// load ca fileif (caFile && !SSL_CTX_load_verify_locations(ctx, caFile, 0)) {ERR_print_errors_fp(stderr);abort();}return;
}/*! signal handling variables */
volatile bool exit_sig = false;
volatile bool quit_sig = false;
void sig_handler(int sigio)
{if (sigio == SIGQUIT) {quit_sig = true;} else if ((sigio == SIGINT) || (sigio == SIGTERM)) {exit_sig = true;}return;
}
void thread_recv(void* arg)
{char buf[1024];int err;SSL* ssl = (SSL*)arg;while (1) {memset(buf, 0, 1024);err = SSL_read(ssl, buf, sizeof(buf) - 1);if (err < 0) {printf("SSL read error\r\n");} else if (err == 0) {printf("SSL disconnect\r\n");isServerDisconnect = true;return;}printf("SSL recv: %s\r\n", buf);}
}
int main(int count, char* strings[])
{SSL_CTX* ctx;int server;SSL* ssl;char buf[1024];pthread_t pid;int retryTimeSec;struct sigaction sigact;sigemptyset(&sigact.sa_mask);sigact.sa_flags   = 0;sigact.sa_handler = sig_handler;sigaction(SIGQUIT, &sigact, NULL); /* Ctrl-\ */sigaction(SIGINT, &sigact, NULL);  /* Ctrl-C 8*/sigaction(SIGTERM, &sigact, NULL); /* default "kill" command */sigaction(SIGPIPE, &sigact, NULL); /* ignore  SIGPIPE */SSL_library_init();ctx = init_ctx();//SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); /* if need verify server cert */load_cert_key_ca_file(ctx, CLIENT_CA, CLIENT_KEY, CA_CERT);show_cert_file(CLIENT_CA);RETRY:server = connect_server(ADDR, PORT);ssl    = SSL_new(ctx);   /* create new SSL connection state */SSL_set_fd(ssl, server); /* attach the socket descriptor */if (SSL_connect(ssl) == FAIL) { /* perform the connection */printf("SSL handshake error, rerey timeout %ds\r\n", SSL_RETRY_DELAY_SEC);close(server); /* close socket */retryTimeSec = SSL_RETRY_DELAY_SEC;while (retryTimeSec--) {if (!quit_sig && !exit_sig) {SSL_CTX_free(ctx); /* release context */printf("exit client\r\n");}sleep(1);}goto RETRY;} else {printf("SSL handshake success\r\n");const char* req = "jackwang client request";printf("connected with %s encryption\n", SSL_get_cipher(ssl));isServerDisconnect = false;pthread_create(&pid, NULL, (void* (*)(void*))thread_recv, (void*)ssl);while (!quit_sig && !exit_sig && !isServerDisconnect) {/* get any certs */memset(buf, 0, 1024);SSL_write(ssl, req, strlen(req)); /* encrypt & send message */sleep(1);}SSL_free(ssl); /* release connection state */}pthread_cancel(pid);pthread_join(pid, NULL);close(server);     /* close socket */SSL_CTX_free(ctx); /* release context */printf("exit client\r\n");return 0;
}

2.2 服务端

服务端主要多了线程管理和证书校验。以下贴出主程序和线程代码。

void thread_main(void* arg)
{char buf[1024];int clientPort;char clientAddr[PEER_IP_LENGTH];SSL* ssl    = (SSL*)arg;int sock_fd = SSL_get_fd(ssl);int err     = SSL_accept(ssl);clientPort  = getpeer_information(sock_fd, clientAddr);if (err < 0) {printf("%s:%d SSL handshake error\r\n", clientAddr, clientPort);close(sock_fd);SSL_free(ssl);pthread_exit((void*)-1);} else {printf("%s:%d SSL handshake success\r\n", clientAddr, clientPort);threadpool_add_one(pthread_self(), ssl);//printf("total client num:%d\r\n", threadpool_num());}printf("%s:%d SSL connection using %s\n", clientAddr, clientPort, SSL_get_cipher(ssl));show_cert_ssl(ssl);while (1) {memset(buf, 0, 1024);err = SSL_read(ssl, buf, sizeof(buf) - 1);if (err < 0) {printf("%s:%d SSL read error\r\n", clientAddr, clientPort);goto FINISH;} else if (err == 0) {printf("%s:%d SSL disconnect\r\n", clientAddr, clientPort);goto FINISH;}printf("%s:%d SSL recv: %s\n", clientAddr, clientPort, buf);}FINISH:threadpool_remove_one(pthread_self(), ssl);
}int main(int count, char* Argc[])
{SSL_CTX* ctx;int server;pthread_t pid;int ret;//Only root user have the permsion to run the serverif (!is_root()) {printf("This program must be run as root/sudo user!!\r\n");exit(0);}struct sigaction sigact;sigemptyset(&sigact.sa_mask);sigact.sa_flags   = 0;sigact.sa_handler = sig_handler;sigaction(SIGQUIT, &sigact, NULL); /* Ctrl-\ */sigaction(SIGINT, &sigact, NULL);  /* Ctrl-C 8*/sigaction(SIGTERM, &sigact, NULL); /* default "kill" command */sigaction(SIGPIPE, &sigact, NULL); /* ignore  SIGPIPE */threadpool_init(); /* init threadpool */// Initialize the SSL librarySSL_library_init();ctx = init_ctx(); /* initialize SSL */load_cert_key_ca_file(ctx, SERVER_CA, SERVER_KEY, CA_CERT);SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); /* verify client cert */SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CA_CERT));server = port_listen(PORT); /* create server socket */printf("server listening (%d)\r\n", PORT);while (!sigQuit && !sigExit) {struct sockaddr_in addr;socklen_t len = sizeof(addr);SSL* ssl;int client = accept(server, (struct sockaddr*)&addr, &len); /* accept connection as usual */if (client > 0) {                                           //printf("connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));ssl = SSL_new(ctx);                                     /* get new SSL state with context */ret = SSL_set_fd(ssl, client);                          /* set connection socket to SSL state */if (ret > 0) {pthread_create(&pid, NULL, (void* (*)(void*))thread_main, (void*)ssl);} else {continue;}}}threadpool_remove_all();close(server);     /* close server socket */SSL_CTX_free(ctx); /* release context */ERR_free_strings();
}

三、编译

客户端:gcc -Wall -o client ssl_client.c -L/usr/lib -lssl -lcrypto -lpthread

服务端:gcc -Wall -o server ssl_server.c -L/usr/lib -lssl -lcrypto -lpthread

若没有ssl和crypto库,则需要安装:apt-get install libssl-dev

四、公开代码仓库

EiRi_jackmaster/pthread_ssl

等空了维护。

SSL 多线程通信 linux openSSL C API编程相关推荐

  1. linux启动程序api编程,Linux编程中关于API函数与系统调用间关系

    用户态xyz()函数,内核最终一般会调用形如sys_xyz()的服务例程来处理(不过也有一些例外,这里暂时不考虑) 函数xyz()是直接提供给用户编程使用的.图中"SYSCALL" ...

  2. Linux下的C编程实战(开发平台搭建,文件系统编程,进程控制与进程通信编程,“线程”控制与“线程”通信编程,驱动程序设计,专家问答)

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来,Linux ...

  3. linux下串口多线程通信 ,多串口收发数据错乱问题解决办法

    最近在写AM335x平台的串口测试工具,最开始的时候写的第一版本,测试一直很ok,但是存在一些缺陷,于是就想改进一下,没想到后面在新的板子测试,竟然发现了以个很致命的问题,在旧系统旧内核测试一切正常, ...

  4. Openssl 建立双向认证的 SSL/TLS 通信

    话不多说,直接利用 Openssl 在两台 uBuntu 之间建立双向认证的 SSL/TLS 通信. 笔者使用的 Openssl 版本为 OpenSSL 1.1.1 11 Sep 2018 生成证书 ...

  5. 【云风skynet】详解skynet的多核高并发编程丨actor模型丨游戏开发丨游戏服务端开发丨多线程丨Linux服务器开发丨后端开发

    skynet中多核高并发编程给我们的启发 1. 多核并发编程 2. actor模型详解 3. 手撕一个万人同时在线游戏 视频讲解如下,点击观看: [云风skynet]详解skynet的多核高并发编程丨 ...

  6. C——Linux下的串口编程

    原 C--Linux下的串口编程 2017年06月06日 19:30:50 C_Aya 阅读数:11537 <span class="tags-box artic-tag-box&qu ...

  7. Linux下高级C编程(学习总结)

    Linux下高级C编程 第一章 unix/linux系统的基本概念 第二章 unix/linux系统下的编程基础和开发方式 第三章 unix/linux系统下的内存管理 第四章 unix/linux系 ...

  8. Linux下的C编程实战

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来, Linu ...

  9. Linux下的C编程实战(转载)

    http://www.cnblogs.com/alexusli/archive/2008/10/24/1318736.html Linux下的C编程实战(转载) (转自)http://www.cnbl ...

最新文章

  1. 【基础知识】截长图的方法以及防止截图时下拉框自动收回的方法
  2. java抓取网页标题内容_[Java教程]java 网页页面抓取标题和正文
  3. 【django轻量级框架】一个Github用户检索信息网站(解析和代码)
  4. 左手高仿,右手二奢,这届年轻人的奢侈品消费有点迷
  5. 今日代码(200714)--主客观求指标权重及求城市得分
  6. ios设计登录功能_亲爱的产品设计师,这是iOS 14的新功能
  7. 电商C4D设计素材背景,3D效果是最适合电商产品
  8. unity anim 组件的使用
  9. 数学建模大赛考什么计算机基础知识,华为杯数学建模竞赛
  10. 惠普电脑u盘重装系统步骤_惠普电脑优盘装系统步骤
  11. 智慧酒店客房控制系统开发提高酒店管理效率和服务质量
  12. 承上启下继往开来,Python3上下文管理器(ContextManagers)与With关键字的迷思
  13. 关于webpack登堂入室的必经之路(1)
  14. 在 Linux 上安装 chm 文件阅读器
  15. 华中科技大学计算机考博真题,2010年华中科技大学计算机考博试题
  16. Data Wrangling
  17. 我的老师,“身怀绝技”!
  18. CFD-Post后处理,你真的会做吗?
  19. 什么样的 GitHub 才适合放简历上?
  20. 书籍《FPS关卡设计》笔记(一)

热门文章

  1. SQL实现split函数,自定义分割字符,自定义取出第几个分割字符前的字符串
  2. java activemq jmx_通过JMX 获取Activemq 队列信息
  3. 在unity中设置多种怪物数据_Unity可编程渲染管线(SRP)系列(三)——光照(单通道 正向渲染)...
  4. 常见窗函数的C语言实现及其形状,适用于单片机、DSP作FFT运算
  5. 第二章:图像处理基础
  6. Taro+react开发(95):问答模块02
  7. 前端学习(3259):js高级教程(3)typeof
  8. React工作(1)---export导出
  9. 前端学习(3013):vue+element今日头条管理--表单验证基本使用
  10. [css] 不使用border画出1px高的线,在不同浏览器的标准和怪异模式下都能保持效果一样