基于openssl v3搭建ssl安全加固的c++ tcpserver

2024-01-25 05:12

本文主要是介绍基于openssl v3搭建ssl安全加固的c++ tcpserver,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 概述

tcp server和tcp client同时使用openssl库,可对通信双方流通的字节序列进行加解密,保障通信的安全。本文以c++编写的tcp server和tcp client为例子,openssl的版本为v3。

2 安装openssl v3

2.1 安装 perl-IPC-Cmd

openssl项目中的config脚本需要用到perl-IPC-Cmd工具。

yum -y install perl-IPC-Cmd

2.2 安装openssl v3

安装的结果放在目录/opt/openssl中。

mkdir -p /opt
cd /opt
wget --no-check-certificate https://www.openssl.org/source/openssl-3.1.0.tar.gz
tar xf openssl-3.1.0.tar.gz
cd openssl-3.1.0
./config --prefix=/opt/openssl
make -j8
make install

安装完毕后,结果如下图所示:
在这里插入图片描述


3 生成SSL私钥和证书

3.1 创建自签CA:

以下将生成一个名称为ca.key的key文件和一个名称为ca.crt的证书文件,CA的common name叫EXAMPLE Cert Authority。

openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt \
-days 3650 \
-nodes -subj '/CN=EXAMPLE Cert Authority'

3.2 生成key文件和csr文件

key文件名称为server.key,csr文件名称为server.csr,common name为apigateway.example.com。

openssl req -new -newkey rsa:4096 -keyout server.key -out server.csr -nodes \
-subj '/CN=apigateway.example.com'

3.2 编写csr扩展文件extfile

SAN就在subjectAltName字段中指定,可以写多个域名和IP,例如域名为apigateway,IP为192.168.16.128。

cat > extfile.cnf << EOF
[ v3_req ]
# Extensions to add to a certificate request
extendedKeyUsage = serverAuth,clientAuth
subjectAltName = DNS:apigateway.example.com,DNS:localhost,IP:192.168.16.128,IP:127.0.0.1
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
EOF

3.3 生成证书文件

依据key文件、csr文件和csr扩展文件,生成证书文件(名称为server.crt)

openssl x509 -req -sha256 -days 3650 \
-extfile extfile.cnf \
-extensions v3_req \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-out server.crt \
-CAcreateserial

3.4 检验证书文件是否由指定CA签署

openssl verify -CAfile ca.crt -verbose server.crt

在这里插入图片描述


4 编写服务端代码

代码来自博客:https://blog.csdn.net/lf940841989/article/details/16881331

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>#define MAXBUF 1024int main(int argc, char **argv)
{int sockfd, new_fd;socklen_t len;struct sockaddr_in my_addr, their_addr;unsigned int myport, lisnum;char buf[MAXBUF + 1];SSL_CTX *ctx;printf("Usage: %s port listenno(1) ip publickey privaterkey\n", argv[0]);if(argc != 6) {printf("parameter error\n");exit(0);}if (argv[1])myport = atoi(argv[1]);elsemyport = 7838;if (argv[2])lisnum = atoi(argv[2]);elselisnum = 2;/* SSL 库初始化*/SSL_library_init();/* 载入所有SSL 算法*/OpenSSL_add_all_algorithms();/* 载入所有SSL 错误消息*/SSL_load_error_strings();/* 以SSL V2 和V3 标准兼容方式产生一个SSL_CTX ,即SSL Content Text */ctx = SSL_CTX_new(SSLv23_server_method());//ctx = SSL_CTX_new(SSLv3_server_method());/* 也可以用SSLv2_server_method() 或SSLv3_server_method() 单独表示V2 或V3* 标准*/if (ctx == NULL) {ERR_print_errors_fp(stdout);exit(1);}/* 载入用户的数字证书, 此证书用来发送给客户端。证书里包含有公钥*/if (SSL_CTX_use_certificate_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stdout);exit(1);}/* 载入用户私钥*/if (SSL_CTX_use_PrivateKey_file(ctx, argv[5], SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stdout);exit(1);}/* 检查用户私钥是否正确*/if (!SSL_CTX_check_private_key(ctx)) {ERR_print_errors_fp(stdout);exit(1);}/* 开启一个socket 监听*/if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {perror("socket");exit(1);} elseprintf("socket created\n");bzero(&my_addr, sizeof(my_addr));my_addr.sin_family = PF_INET;my_addr.sin_port = htons(myport);if (argv[3])my_addr.sin_addr.s_addr = inet_addr(argv[3]);elsemy_addr.sin_addr.s_addr = INADDR_ANY;if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) {perror("bind");exit(1);} elseprintf("binded\n");if (listen(sockfd, lisnum) == -1) {perror("listen");exit(1);} elseprintf("begin listen\n");while (1) {SSL *ssl;len = sizeof(struct sockaddr);/* 等待客户端连上来*/if ((new_fd =accept(sockfd, (struct sockaddr *) &their_addr,&len)) == -1) {perror("accept");exit(errno);} elseprintf("server: got connection from %s, port %d, socket %d\n",inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port), new_fd);/* 基于ctx 产生一个新的SSL */ssl = SSL_new(ctx);/* 将连接用户的socket 加入到SSL */SSL_set_fd(ssl, new_fd);/* 建立SSL 连接*/if (SSL_accept(ssl) == -1) {perror("accept");close(new_fd);break;}/* 开始处理每个新连接上的数据收发*/bzero(buf, MAXBUF + 1);strcpy(buf, "server->client");/* 发消息给客户端*/len = SSL_write(ssl, buf, strlen(buf));if (len <= 0) {printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",buf, errno, strerror(errno));goto finish;} elseprintf("消息'%s'发送成功,共发送了%d 个字节!\n",buf, len);bzero(buf, MAXBUF + 1);/* 接收客户端的消息*/len = SSL_read(ssl, buf, MAXBUF);if (len > 0)printf("接收消息成功:'%s',共%d 个字节的数据\n",buf, len);elseprintf("消息接收失败!错误代码是%d,错误信息是'%s'\n",errno, strerror(errno));/* 处理每个新连接上的数据收发结束*/finish:/* 关闭SSL 连接*/SSL_shutdown(ssl);/* 释放SSL */SSL_free(ssl);/* 关闭socket */close(new_fd);}/* 关闭监听的socket */close(sockfd);/* 释放CTX */SSL_CTX_free(ctx);return 0;
}

5 编写客户端代码

代码来自博客:https://blog.csdn.net/lf940841989/article/details/16881331

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>#define MAXBUF 1024void ShowCerts(SSL * ssl)
{X509 *cert;char *line;cert = SSL_get_peer_certificate(ssl);if (cert != NULL) {printf("数字证书信息:\n");line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);printf("证书: %s\n", line);free(line);line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);printf("颁发者: %s\n", line);free(line);X509_free(cert);} elseprintf("无证书信息!\n");
}int main(int argc, char **argv)
{int sockfd, len;struct sockaddr_in dest;char buffer[MAXBUF + 1];SSL_CTX *ctx;SSL *ssl;printf("Usage: %s server_ip server_port\n", argv[0]);if (argc != 3) {printf("参数格式错误!正确用法,比如: %s 127.0.0.1 80\n""此程序用来从某个IP 地址的服务器某个端口接收最多MAXBUF 个字节的消息\n",argv[0]);exit(0);}/* SSL 库初始化,参看ssl-server.c 代码*/SSL_library_init();OpenSSL_add_all_algorithms();SSL_load_error_strings();ctx = SSL_CTX_new(SSLv23_client_method());// ctx = SSL_CTX_new(SSLv3_client_method());if (ctx == NULL) {ERR_print_errors_fp(stdout);exit(1);}/* 创建一个socket 用于tcp 通信*/if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("Socket");exit(errno);}printf("socket created\n");/* 初始化服务器端(对方)的地址和端口信息*/bzero(&dest, sizeof(dest));dest.sin_family = AF_INET;dest.sin_port = htons(atoi(argv[2]));if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {perror(argv[1]);exit(errno);}printf("address created\n");/* 连接服务器*/if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {perror("Connect ");exit(errno);}printf("server connected\n");/* 基于ctx 产生一个新的SSL */ssl = SSL_new(ctx);SSL_set_fd(ssl, sockfd);/* 建立SSL 连接*/if (SSL_connect(ssl) == -1)ERR_print_errors_fp(stderr);else {printf("Connected with %s encryption\n", SSL_get_cipher(ssl));ShowCerts(ssl);}/* 接收对方发过来的消息,最多接收MAXBUF 个字节*/bzero(buffer, MAXBUF + 1);/* 接收服务器来的消息*/len = SSL_read(ssl, buffer, MAXBUF);if (len > 0)printf("接收消息成功:'%s',共%d 个字节的数据\n",buffer, len);else {printf("消息接收失败!错误代码是%d,错误信息是'%s'\n",errno, strerror(errno));goto finish;}bzero(buffer, MAXBUF + 1);strcpy(buffer, "from client->server");/* 发消息给服务器*/len = SSL_write(ssl, buffer, strlen(buffer));if (len < 0)printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",buffer, errno, strerror(errno));elseprintf("消息'%s'发送成功,共发送了%d 个字节!\n",buffer, len);finish:/* 关闭连接*/SSL_shutdown(ssl);SSL_free(ssl);close(sockfd);SSL_CTX_free(ctx);return 0;
}

6 编译服务端代码并运行

g++ -o server server_ssl.c -Wl,-rpath,/opt/openssl/lib64 -L/opt/openssl/lib64 -lssl -lcrypto -I/opt/openssl/include
./server 8888 1 127.0.0.1 server.crt server.key

在这里插入图片描述


7 编译客户端代码

g++ -o client client_ssl.c -L/opt/openssl/lib64 -lssl -lcrypto -I/opt/openssl/include -Wl,-rpath,/opt/openssl/lib64

在这里插入图片描述


8 运行客户端

./client 127.0.0.1 8888

客户端的输出如下图:
在这里插入图片描述

客户端会打印服务端传输过来的ssl证书,CA和CN正是本文在创建证书阶段所填写的信息,说明在TCP握手之后,进行SSL握手也是成功的。握手成功后,往服务端发送信息"from client->server"。

此时服务端的输出如下图:
在这里插入图片描述
服务端在SSL握手成功后,通过SSL_read()方法接收来自客户端发送过来的加密信息并自动解密(明文信息为"from client->server")。


9 小结

tcp握手成功后,服务端和客户端代码使用openssl库能加解密tcp中传输的字节序列,保障通信的安全。在代码编写套路上,服务端和客户端代码在拿到fd之后,将fd塞进ssl上下文对象中,使用格式如SSL_XXX的方法来进行SSL握手、读写数据。

这篇关于基于openssl v3搭建ssl安全加固的c++ tcpserver的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/642186

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

浅谈主机加固,六种有效的主机加固方法

在数字化时代,数据的价值不言而喻,但随之而来的安全威胁也日益严峻。从勒索病毒到内部泄露,企业的数据安全面临着前所未有的挑战。为了应对这些挑战,一种全新的主机加固解决方案应运而生。 MCK主机加固解决方案,采用先进的安全容器中间件技术,构建起一套内核级的纵深立体防护体系。这一体系突破了传统安全防护的局限,即使在管理员权限被恶意利用的情况下,也能确保服务器的安全稳定运行。 普适主机加固措施:

搭建Kafka+zookeeper集群调度

前言 硬件环境 172.18.0.5        kafkazk1        Kafka+zookeeper                Kafka Broker集群 172.18.0.6        kafkazk2        Kafka+zookeeper                Kafka Broker集群 172.18.0.7        kafkazk3

客户案例:安全海外中继助力知名家电企业化解海外通邮困境

1、客户背景 广东格兰仕集团有限公司(以下简称“格兰仕”),成立于1978年,是中国家电行业的领军企业之一。作为全球最大的微波炉生产基地,格兰仕拥有多项国际领先的家电制造技术,连续多年位列中国家电出口前列。格兰仕不仅注重业务的全球拓展,更重视业务流程的高效与顺畅,以确保在国际舞台上的竞争力。 2、需求痛点 随着格兰仕全球化战略的深入实施,其海外业务快速增长,电子邮件成为了关键的沟通工具。

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)