20.5 OpenSSL 套接字RSA加密传输

RSA算法同样可以用于加密传输,但此类加密算法虽然非常安全,但通常不会用于大量的数据传输,这是因为RSA算法加解密过程涉及大量的数学运算,尤其是模幂运算(即计算大数的幂模运算),这些运算对于计算机而言是十分耗时。

其次在RSA算法中,加密数据的长度不能超过密钥长度减去一定的填充长度。一般情况下,当RSA密钥长度为1024位时,可以加密长度为128字节,密钥长度为2048位时,可以加密长度为245字节;当密钥长度为3072位时,可以加密长度为371字节。因此,如果需要加密的数据长度超过了密钥长度允许的范围,可以采用分段加密的方法。我们可以将数据包切割为每个128个字符,这样就可以实现循环传输大量字符串。

20.5.1 加解密算法封装

在之前的章节中我们都是使用命令行的方式手动生成密钥对文件,其实在OpenSSL中我们完全可以使用SDK提供的函数自动生成对应的加密密钥对文件,如下一段代码中,CreateRSAPEM则是一个生成密钥对的函数,分别向该函数内传递一个公钥,私钥,以及数据长度,即可得到两个RSA文件。

#include <iostream>
#include <string>
#include <Windows.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/crypto.h>extern "C"
{
#include <openssl/applink.c>
}#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")// 生成RSA公钥和私钥文件
BOOL CreateRSAPEM(char *PublicKey, char *PrivateKey, int KeySize)
{BIO* bpub, *bpri;// 分别新建密钥对文件bpub = BIO_new_file(PublicKey, "w");bpri = BIO_new_file(PrivateKey, "w");if (!bpub || !bpri){return FALSE;}RSA* pRSA = 0;// 生成密钥对KeySize指定密钥对长度1024pRSA = RSA_generate_key(KeySize, RSA_F4, NULL, NULL);if (pRSA != NULL){// 写出公钥if (!PEM_write_bio_RSAPublicKey(bpub, pRSA)){return FALSE;}// 写出私钥if (!PEM_write_bio_RSAPrivateKey(bpri, pRSA, NULL, NULL, 0, NULL, NULL)){return FALSE;}}if (bpub){BIO_free(bpub);}if (bpri){BIO_free(bpri);}if (pRSA){RSA_free(pRSA);}return TRUE;
}int main(int argc, char* argv[])
{// 生成公钥与私钥BOOL flag = CreateRSAPEM("public.rsa","private.rsa",1024);if (flag == TRUE){printf("[*] 已生成密钥对 \n");}system("pause");return 0;
}

代码运行后会分别在当前目录下生成public.rsa公钥及private.rsa私钥两个文件,如下图所示;

接着就是对加解密函数的封装实现,为了能更好的实现网络传输,如下是封装的四个函数,其中public_rsa_encrypt用于使用公钥对字符串进行加密,private_rsa_decrypt函数使用私钥对字符串进行解密,private_rsa_encrypt使用私钥加密,public_rsa_decrypt使用公钥解密,读者可根据自己的实际需求选择不同的加解密函数。

// 使用公钥加密
BOOL public_rsa_encrypt(char* in, char* key_path, char* out)
{RSA* p_rsa;FILE* file;int rsa_len;if ((file = fopen(key_path, "r")) == NULL){return FALSE;}if ((p_rsa = PEM_read_RSAPublicKey(file, NULL, NULL, NULL)) == NULL){ERR_print_errors_fp(stdout);return FALSE;}rsa_len = RSA_size(p_rsa);if (RSA_public_encrypt(rsa_len, (unsigned char*)in, (unsigned char*)out, p_rsa, RSA_NO_PADDING) < 0){return FALSE;}RSA_free(p_rsa);fclose(file);return TRUE;
}// 使用私钥解密
BOOL private_rsa_decrypt(char* in, char* key_path, char* out)
{RSA* p_rsa;FILE* file;int rsa_len;if ((file = fopen(key_path, "r")) == NULL){return FALSE;}if ((p_rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL)) == NULL){ERR_print_errors_fp(stdout);return FALSE;}rsa_len = RSA_size(p_rsa);if (RSA_private_decrypt(rsa_len, (unsigned char*)in, (unsigned char*)out, p_rsa, RSA_NO_PADDING) < 0){return FALSE;}RSA_free(p_rsa);fclose(file);return TRUE;
}// 使用私钥加密
BOOL private_rsa_encrypt(char* in, char* key_path, char* out)
{RSA* p_rsa;FILE* file;int rsa_len;if ((file = fopen(key_path, "r")) == NULL){return FALSE;}if ((p_rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL)) == NULL){ERR_print_errors_fp(stdout);return FALSE;}rsa_len = RSA_size(p_rsa);if (RSA_private_encrypt(rsa_len, (unsigned char*)in, (unsigned char*)out, p_rsa, RSA_NO_PADDING) < 0){return FALSE;}RSA_free(p_rsa);fclose(file);return TRUE;
}// 使用公钥解密
BOOL public_rsa_decrypt(char* in, char* key_path, char* out)
{RSA* p_rsa;FILE* file;int rsa_len;if ((file = fopen(key_path, "r")) == NULL){return FALSE;}if ((p_rsa = PEM_read_RSAPublicKey(file, NULL, NULL, NULL)) == NULL){ERR_print_errors_fp(stdout);return FALSE;}rsa_len = RSA_size(p_rsa);if (RSA_public_decrypt(rsa_len, (unsigned char*)in, (unsigned char*)out, p_rsa, RSA_NO_PADDING) < 0){return FALSE;}RSA_free(p_rsa);fclose(file);return TRUE;
}

当我们需要使用公钥加密时可以调用public_rsa_encrypt函数并依次传入加密前的字符串,公钥路径以及加密后的存储位置,当需要解密时则调用private_rsa_decrypt函数实现对加密字符串的解密操作,使用代码如下所示;

int main(int argc, char* argv[])
{char text[128] = "hello lyshark";char public_key[] = "d://public.rsa";char encry[128] = { 0 };char private_key[] = "d://private.rsa";char decry[128] = { 0 };// 公钥加密if (public_rsa_encrypt(text, public_key, encry)){printf("[公钥加密] 加密长度: %d \n", strlen((char*)encry));}// 私钥解密if (private_rsa_decrypt(encry, private_key, decry)){printf("[私钥解密] 解密长度: %d \n", strlen((char*)decry));}printf("解密数据: %s \n", decry);system("pause");return 0;
}

读者可自行编译并运行上述代码,即可看到加解密数据输出,如下图所示;

将这个流程反过来使用,使用私钥对数据进行加密,使用公钥实现解密,代码如下所示;

int main(int argc, char* argv[])
{char text[128] = "hello lyshark";char public_key[] = "d://public.rsa";char encry[128] = { 0 };char private_key[] = "d://private.rsa";char decry[128] = { 0 };// 私钥加密if (private_rsa_encrypt(text, private_key, encry)){printf("[私钥加密] 加密长度: %d \n", strlen((char*)encry));}// 公钥解密if (public_rsa_decrypt(encry, public_key, decry)){printf("[公钥解密] 解密长度: %d \n", strlen((char*)decry));}printf("解密数据: %s \n", decry);system("pause");return 0;
}

私钥加密公钥解密,输出效果图如下所示;

20.5.2 加密传输字符串

当具备了上述加解密函数实现流程后,接下来就可以实现针对字符串的加密传输功能了,因为我们采用的是1024位的密钥所以每次只能传输128个字符,为了能传输大量字符则需要对字符进行分块,通过CutSplit()函数将字符串每100个字符切割一次,然后在客户端中先使用公钥对其进行加密,加密后分块每次传输一批次的加密数据即可,直到将完整的字符串发送完成为止。

#include <iostream>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/crypto.h>extern "C"
{
#include <openssl/applink.c>
}#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")// 使用公钥加密
int public_rsa_encrypt(char* in, char* key_path, char* out)
{RSA* p_rsa;FILE* file;int rsa_len;if ((file = fopen(key_path, "r")) == NULL){return 0;}if ((p_rsa = PEM_read_RSAPublicKey(file, NULL, NULL, NULL)) == NULL){ERR_print_errors_fp(stdout);return 0;}rsa_len = RSA_size(p_rsa);if (RSA_public_encrypt(rsa_len, (unsigned char*)in, (unsigned char*)out, p_rsa, RSA_NO_PADDING) < 0){return 0;}RSA_free(p_rsa);fclose(file);return 1;
}// 实现对字符串指定位置进行剪切
char* Cut(char* buffer, int offset, int length)
{char Split[100] = { 0 };memset(Split, 0, 100);strncpy(Split, buffer + offset, length);return Split;
}// 循环剪切字符串
int CutSplit(char* buf, char len, OUT char Split[][1024])
{int count = 0;// 每次剪切len大小for (int x = 0; x < strlen(buf); x += len){char* ref = Cut(buf, x, len);strcpy(Split[count], ref);count += 1;}return count;
}int main(int argc, char* argv[])
{char buf[8192] = "The National Aeronautics and Space Administration is America.";WSADATA WSAData;// 初始化套接字库if (WSAStartup(MAKEWORD(2, 0), &WSAData)){return 0;}// 建立Socket套接字SOCKET client_socket;client_socket = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in ClientAddr;ClientAddr.sin_family = AF_INET;ClientAddr.sin_port = htons(9999);ClientAddr.sin_addr.s_addr = inet_addr("127.0.0.1");// 连接到服务端if (connect(client_socket, (LPSOCKADDR)&ClientAddr, sizeof(ClientAddr)) != SOCKET_ERROR){char SplitArray[100][1024] = { 0 };// 切割字符串,每100个字符切割一次int count = CutSplit(buf, 100, SplitArray);// 发送发包次数std::cout << "发包次数: " << count << std::endl;char send_count[1024] = { 0 };sprintf(send_count, "%d", count);send(client_socket, send_count, strlen(send_count), 0);// 循环发送数据包for (int x = 0; x < count; x++){std::cout << "原始数据包: " << SplitArray[x] << std::endl;char public_key[] = "d://public.rsa";char encry[1024] = { 0 };// 公钥加密if (public_rsa_encrypt(SplitArray[x], public_key, encry)){std::cout << "RSA 加密长度: " << strlen((char*)encry) << std::endl;}// 发送加密后的数据包send(client_socket, encry, 1024, 0);memset(buf, 0, sizeof(buf));memset(encry, 0, sizeof(encry));}closesocket(client_socket);WSACleanup();}system("pause");return 0;
}

而对于服务端代码实现部分则需要与客户端保持一致,服务端发送多少次客户端就接收多少次,首先服务端接收需要接收的数据包次数,并以此作为循环条件使用,通过不间断的循环接受数据包,并调用private_rsa_decrypt完成数据包的解密工作,最终将数据包拼接成recv_message_all并输出完整包。

#include <iostream>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/crypto.h>extern "C"
{
#include <openssl/applink.c>
}#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")// 使用私钥解密
int private_rsa_decrypt(char* in, char* key_path, char* out)
{RSA* p_rsa;FILE* file;int rsa_len;if ((file = fopen(key_path, "r")) == NULL){return 0;}if ((p_rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL)) == NULL){ERR_print_errors_fp(stdout);return 0;}rsa_len = RSA_size(p_rsa);if (RSA_private_decrypt(rsa_len, (unsigned char*)in, (unsigned char*)out, p_rsa, RSA_NO_PADDING) < 0){return 0;}RSA_free(p_rsa);fclose(file);return 1;
}int main(int argc, char* argv[])
{WSADATA WSAData;// 初始化套接字库if (WSAStartup(MAKEWORD(2, 0), &WSAData)){return 0;}// 建立Socket套接字SOCKET server_socket;server_socket = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in ServerAddr;ServerAddr.sin_family = AF_INET;ServerAddr.sin_port = htons(9999);ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");// 绑定套接字bind(server_socket, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));listen(server_socket, 10);SOCKET message_socket;// 接收并拼接数据char recv_message_all[8109] = { 0 };if ((message_socket = accept(server_socket, (LPSOCKADDR)0, (int*)0)) != INVALID_SOCKET){// 接收需要获取的次数char recv_count[1024] = { 0 };recv(message_socket, recv_count, 1024, 0);std::cout << "收包次数: " << recv_count << std::endl;for (int x = 0; x < atoi(recv_count); x++){// 接收加密后的数据包char buf[1024] = { 0 };recv(message_socket, buf, 1024, 0);// 私钥解密char private_key[] = "d://private.rsa";char decry[1024] = { 0 };// 调用解密函数if (private_rsa_decrypt(buf, private_key, decry)){std::cout << "RSA 解密长度: " << strlen((char*)decry) << std::endl;}std::cout << "RSA 解密数据包: " << decry << std::endl;// 组合数据包strCut(recv_message_all, decry);memset(buf, 0, sizeof(buf));memset(decry, 0, sizeof(decry));}closesocket(message_socket);// 输出最终数据包std::cout << std::endl;std::cout << "组合数据包: " << recv_message_all << std::endl;}closesocket(server_socket);WSACleanup();system("pause");return 0;
}

读者可自行填充客户端中的buf待发送字符串长度,填充好以后首先运行服务端,接着运行客户端,此时数据包将会被加密传输,在对端解密并输出如下图所示的结果;

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

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

相关文章

Openlayers--自定义修改天地图颜色

自定义修改地图颜色 前言效果图1、给titleLayer设置className2、给class设置样式 前言 本篇文章讲解怎样调整地图颜色 效果图 调整前 调整后 1、给titleLayer设置className const arcGISLayer new TileLayer({className:blueLayer,//增加className属性source: new XYZ(…

LV.12 D16 轮询与中断 学习笔记

一、CPU与硬件的交互方式 轮询 CPU执行程序时不断地询问硬件是否需要其服务&#xff0c;若需要则给予其服务&#xff0c;若不需要一段时间后再次询问&#xff0c;周而复始 中断 CPU执行程序时若硬件需要其服务&#xff0c;对应的硬件给CPU发送中断信号&#xff0c…

订单业务和系统设计(一)

一、背景简介 订单其实很常见&#xff0c;在电商购物、外卖点餐、手机话费充值等生活场景中&#xff0c;都能见到它的影子。那么&#xff0c;一笔订单的交易过程是什么样子的呢&#xff1f;文章尝试从订单业务架构和产品功能流程&#xff0c;描述对订单的理解。 二、订单业务…

python自动化运维——模拟键盘鼠标重复性操作Pyautoui

一、程序样式展示 将程序与cmd.xls文件放在同一文件夹&#xff0c;每一步的截图也放在当前文件夹 通过图片在屏幕上面进行比对&#xff0c;找到点击处进行自动化操作 自动化rpa测试 二、核心点 1.Pyautoui模块&#xff1a;主要针对图片进行定位pyautogui.locateCenterOnScree…

Ubuntu系统HUSTOJ 用 vim 修改php.ini 重启PHP服务

cd / sudo find -name php.ini 输出&#xff1a; ./etc/php/7.4/cli/php.ini ./etc/php/7.4/fpm/php.ini sudo vim /etc/php/7.4/cli/php.ini sudo vim /etc/php/7.4/fpm/php.ini 知识准备&#xff1a; vim的搜索与替换 在正常模式下键入 / &#xff0c;即可进入搜索模式…

竞赛选题 深度学习实现行人重识别 - python opencv yolo Reid

文章目录 0 前言1 课题背景2 效果展示3 行人检测4 行人重识别5 其他工具6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的行人重识别算法研究与实现 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c…

树莓派结合Nginx,轻松搭建内网穿透服务实现远程访问内部站点

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Ngi…

(自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载

(自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载 带后台系统PbootCMS内核开发的网站模板&#xff0c;该模板适用于新闻博客网站、自媒体运营网站等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#…

公会发展计划(GAP):经过实战考验的 Web3 任务模式

2020 年 12 月&#xff0c;Yield Guild Games 踏上了一段征程&#xff0c;以表彰兢兢业业的 Web3 游戏玩家所付出的时间和努力&#xff0c;同时为他们提供利用自己的技能促进个人成长的机会。这一旅程的第一步是于 2022 年 7 月推出的公会发展计划&#xff08;GAP&#xff09;。…

智慧工厂人员定位系统及uwb定位技术在现代工厂安全管理中的应用【uwb定位系统源码】

uwb定位系统源码 智慧工厂人员定位系统源码 定位系统源码 随着经济的高速发展&#xff0c;现代制造业规模不断扩大&#xff0c;生产车间面积广阔&#xff0c;生产设备日益繁多&#xff0c;生产工人数量多且分散作业&#xff0c;难以进行有效管理和实施全方位风险管控。现代工厂…

基于动力学模型的机械臂pid控制

参考资料&#xff1a; 一、如何实现机械臂的控制 在最常见的对机械臂动力学实现控制的问题中&#xff0c;我们会有一段机械臂末端的期望轨迹S&#xff0c;希望通过对机械臂关节处电机转矩的控制实现末端沿期望轨迹的完美运动。控制问题主要分为镇定和跟踪两种&#xff0c;上面…

使用php打印时间精确到毫秒及毫秒转成11位时间戳

在PHP中&#xff0c;可以使用microtime函数来获取当前时间&#xff0c;包括毫秒。以下是示例代码&#xff1a; // 获取当前时间戳&#xff08;秒&#xff09; $time microtime(true); // 将当前时间戳转换为毫秒 $milliseconds round($time * 1000); // 输出当前时间&#…

Android开发知识学习——Kotlin基础

函数声明 声明函数要用用 fun 关键字&#xff0c;就像声明类要用 class 关键字一样 「函数参数」的「参数类型」是在「参数名」的右边 函数的「返回值」在「函数参数」右边使用 : 分隔&#xff0c;没有返回值时可以省略 声明没有返回值的函数&#xff1a; fun main(){println…

怎么测量直线模组的精度?

直线模组是一款比较多变的一种产品&#xff0c;适合多种行业&#xff0c;同时它也适合比较多的环境&#xff0c;是一种比较全面的直线运动的装置。直线模组是非常精密的自动化机械设备&#xff0c;在实践中&#xff0c;应该要严格把握具体精度&#xff0c;并且在必要的时候&…

【嵌入式开发工具】STM32+Keil实现软件工程搭建与开发调试

本篇文章介绍了使用Keil来对STM32F103C8芯片进行初始工程搭建&#xff0c;以及开发与工程调试的完整过程&#xff0c;帮助读者能够在实战中体会到Keil这个开发环境的使用方法&#xff0c;了解一个嵌入式工程从无到有的过程&#xff0c;并且具备快速搭建一个全新芯片对应最小软件…

基于元学习神经网络的类人系统泛化

Nature 上介绍了一个关于AI在语言泛化方面的突破性研究。科学家们创建了一个具有人类般泛化能力的AI神经网络&#xff0c;它可以像人类一样将新学到的词汇融入现有词汇&#xff0c;并在新环境中使用它们。与ChatGPT 相比&#xff0c;该神经网络在系统性泛化测试中表现得更好。 …

17、Flink 之Table API: Table API 支持的操作(1)

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

vite安装Tailwind CSS

安装 - Tailwind CSS 中文网 (nodejs.cn) 这是官网&#xff0c;平常我练习一般会用vite脚手架 我们选择这个vite模块 可选择React和Vue版本的&#xff0c;这里选择react的按照操作&#xff0c;没问题的话就要出问题了 1、在npm run dev的时候我是出现了这么个问题&#xff0c…

234. 回文链表、Leetcode的Python实现

博客主页&#xff1a;&#x1f3c6;看看是李XX还是李歘歘 &#x1f3c6; &#x1f33a;每天分享一些包括但不限于计算机基础、算法等相关的知识点&#x1f33a; &#x1f497;点关注不迷路&#xff0c;总有一些&#x1f4d6;知识点&#x1f4d6;是你想要的&#x1f497; ⛽️今…

竞赛 深度学习疫情社交安全距离检测算法 - python opencv cnn

文章目录 0 前言1 课题背景2 实现效果3 相关技术3.1 YOLOV43.2 基于 DeepSort 算法的行人跟踪 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习疫情社交安全距离检测算法 ** 该项目较为新颖&#xff0c;适合作为竞赛…