SSL 多线程通信 linux openSSL C API编程

一、环境

需要提前准备好服务端和客户端的证书和私钥,以及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

等空了维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/404571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享一个CSS3和jQuery实现的模糊显示效果 - 帮助你的访问用户更好的阅读内容

为什么80%的码农都做不了架构师&#xff1f;>>> 日期&#xff1a;2011/12/20 来源&#xff1a;GBin1.com 在线演示 本地下载 这 个教程将实现一个页面部分内容突出显示效果&#xff0c; 大家可能经常见到一些页面组成内容比较多&#xff0c;可能会分散用户的…

[react] 说说你是怎么理解react的业务组件和技术组件的?

[react] 说说你是怎么理解react的业务组件和技术组件的&#xff1f; 业务组件即代码逻辑紧贴业务&#xff0c;如ajax请求&#xff0c;一些特定的需求特性等&#xff1b; 技术组件即使比较通用性的组件&#xff0c;如对loading的处理、ajax的hoc等 个人简介 我是歌谣&#xf…

查看静态库(.lib)和动态库(.dll)的导出函数的信息

From&#xff1a; http://blog.csdn.net/sunliangyuan/article/details/8075808 一般情况下&#xff0c;我们需要查看一个DLL或EXE中的包含的函数或是依赖的函数之类的信息&#xff0c;可以使用VS自带的工具dumpbin&#xff1b; 可以直接在命令行下输入dumpbin就可以查看他的…

Cracked me --1--Acid_burn

结合ida和od结合效果最好。 打开程序&#xff0c;首先是一个nag窗口&#xff08;去除nag&#xff09; 常用方法&#xff0c;一寻找字符串&#xff0c;发现和以往的 不同&#xff0c;不是对话框句柄的形式。只能第二种方法&#xff0c;单步找到对话框出现的位置。0042fd97-->…

QT 开发openSSL CSR证书请求工具

1、环境 QT 5.15.2 MinGW8.1.0 64Bit 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) 本机的OpenSSL是随QT安装的。 2、QT使用openSSL 2.1 项目文件添加以下依赖 …

Twitter Storm 序列化

序列化这篇文章是关于序列化方法在storm 0.6.0版及之前版本中是如何工作的。0.6.0版之前&#xff0c;storm使用一种不同的序列化方法&#xff0c;参见 Serialization (prior to 0.6.0)。 元组可由任何一种类型的对象组成。由于storm是一个分布式系统&#xff0c;当对象在任务之…

VC 打开目录对话框

From: http://blog.sina.com.cn/s/blog_790bb7190100yxm7.html 对于文件选择对话框和目录选择对话框。在VC中打开文件选择对话框比较简单&#xff0c;使用CFileDialog就可以。打开目录选择对话框就稍微麻烦一些。需要使用API函数SHBrowseForFolder来实现。 该函数原型为: …

[react] 说说你对Error Boundaries的理解

[react] 说说你对Error Boundaries的理解 错误边界是React16新推出的一种错误处理的方式&#xff0c;在v16之前&#xff0c;React的抛错会导致页面显示的错误&#xff0c;v16修改这种方式成了组件如果产生了错误&#xff0c;那么从这个组件到根组件都会因为错误而崩溃掉&#…

pwd命令

Linux中用 pwd 命令来查看”当前工作目录“的完整路径。 简单得说&#xff0c;每当你在终端进行操作时&#xff0c;你都会有一个当前工作目录。 在不太确定当前位置时&#xff0c;就会使用pwd来判定当前目录在文件系统内的确切位置。 1&#xff0e;命令格式&#xff1a; pwd […

raspberry OS buster InRelease: The following signatures couldn‘t be verified

系统换源后更新&#xff0c;出现以下问题&#xff1a; Get:1 http://mirror.tuna.tsinghua.edu.cn/raspberrypi buster InRelease [32.6 kB] Get:2 http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian buster InRelease [15.0 kB] Get:3 http://mirror.tuna.tsinghua.edu…

Windos下用setx.exe命令行模式下永久设置系统环境变量

From: http://www.cnblogs.com/soli/archive/2007/08/30/876283.html setx.exe不是系统默认自带的命令&#xff0c;但可以从微软的网站(2K,XP)上下载到&#xff0c;是官方的咚咚&#xff0c;所以可以放心使用。这里是我从2K里的安装文件中抽取出来的exe文件&#xff0c;不想…

[react] props.children.map和js的map有什么区别?为什么优先选择react的?

[react] props.children.map和js的map有什么区别&#xff1f;为什么优先选择react的&#xff1f; React.Children.map 能够处理未知数据类型&#xff0c;即使 React.children 是 null 和 undefined 也能够正确处理。 React.Children.forEach 一样的原理。 个人简介 我是歌谣…

AC自动机-HDU2222-模板题

http://acm.hdu.edu.cn/showproblem.php?pid2222 一个AC自动机的模板题。用的kuangbin的模板&#xff0c;静态建Trie树。可能遇到MLE的情况要转动态建树。 AC自动机的讲解看这里 http://blog.csdn.net/niushuai666/article/details/7002823 http://blog.csdn.net/mobius_strip…

2012网页设计趋势(下)

CSS3 技术 不确定你可以利用CSS3吗&#xff1f;再想想。网络先锋如Andy Clarke 和 Jeremy Keith一直提倡网页设计要“逐步加强”。逐步增强是指在网站设计师时要兼顾到旧版浏览器的可用性&#xff0c;而“增强”则是针对喜欢最新技术的用户。在这样的设计阵营&#xff0c;你利用…

树莓派CM4 装系统和换源

一、系统下载 官网&#xff1a;Operating system images – Raspberry Pi 最新的版本删除了默认账号pi。考虑以前的使用习惯&#xff0c;此处下载以前的buster debian版本。CM4是arm64架构&#xff0c;根据板子的配置容量不同可选相应的版本&#xff0c;我这里选择lite版本。…

关于 not enough actual parameters for macro ...

From: http://blog.csdn.net/liyelun/article/details/5416253 今天修改工程&#xff0c;为了提高编译速度&#xff0c;将#inclue<dshow.h>放到了stdafx.h中&#xff0c;然后重新编译工程。结果有两个文件编译没通过&#xff0c;并且导致的编译错误近百个。 觉得奇怪&…

[react] 有用过react的Fragment吗?它的运用场景是什么

[react] 有用过react的Fragment吗&#xff1f;它的运用场景是什么 代替div作为外层&#xff0c;可做不可见的包裹元素。 个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端…

宝贝女儿半岁啦

亲爱的赵曦涵宝宝&#xff0c;祝贺你今天已经整整半岁了(如今28日00时不到10分) &#xff01;不知道你什么时候才可以看懂老妈写给你的这第一封信&#xff0c;或许要3年&#xff0c;5年&#xff0c;或者更久吧。今天是你姥爷的54周岁生日&#xff0c;我定了蛋糕&#xff0c;给你…

sharedPreferences的用法

做软件开发应该都知道&#xff0c;很多软件会有配置文件&#xff0c;里面存放这程序运行当中的各个属性值&#xff0c;由于其配置信息并不多&#xff0c;如果采用数据库来存放并不划算&#xff0c;因为数据库连 接跟操作等耗时大大影响了程序的效率&#xff0c;因此我们使用键值…

VC6中使用内存DC加载并显示JPG图片的注意事项

From: http://blog.csdn.net/boythl/article/details/3137446 今天在VC6中显示JPG图片&#xff0c;采用了内存DC缓存的方法刷新&#xff0c;但死活刷不出来&#xff0c;查了一下午才明白&#xff08;没办法&#xff0c;很少用GDI&#xff0c;我小菜一个:( &#xff09;&…