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.rhkb.cn/news/182069.html

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

相关文章

编码器类型说明

目录 光电编码器 磁性编码器 电容式编码器 对比优缺点 编码器在运动控制类产品中比较常见&#xff0c;旋转编码器都是组成运动控制反馈回路的关键元器件&#xff0c;包括工业自动化设备和过程控制、机器人技术、医疗设备、能源、航空航天等。 作为将机械运动转换为电信号的…

❤️ React的安装和使用(实战篇)

React的安装和使用 一、React的安装和使用 reactJs警告提示&#xff1a; This version of tar is no longer supported, and will not receive security updates. Please upgrade asap 翻译&#xff1a;tar2.2.2&#xff1a;此版本的tar不再受支持&#xff0c;将不会收到安全…

新大陆NVH200-AP(U)扫码枪在上位机软件开发中的应用

前言: 由于本次使用的是USB接口的扫码枪 1、先安装Nset软件,使用扫码枪扫描“启动设置条码”,然后扫描“USB CDC串口”条码 2、打开NSet软件,点击“刷新按钮” 就能找到扫码枪设备 3、设置条码后缀 点击“高级设置”,然后点击“数据编辑”,在“后缀”那里设置结束符…

1-3 docker 安装 prometheus

一、环境 1、环境准备 安装Docker 镜像加速 安装 docker 检查版本 安装Docker-compose 二、Docker-compose 安装 Prometheus 1、【方式一】手动创建 docker-compose 和 配置文件 创建prometheus监控的文件夹 创建alertmanager的配置文件 - config.yml 新建grafana的…

windows mysql安装

1、首先去官网下载mysql安装包&#xff0c;官网地址&#xff1a;MySQL :: Download MySQL Community Server 2&#xff1a;把安装包放到你安装mysql的地方&#xff0c;然后进行解压缩&#xff0c;注意&#xff0c;解压后的mysql没有配置文件&#xff0c;我们需要创建配置文件 配…

红黑树——原理刨析

众所周知&#xff0c;红黑树是从AVLTree树中衍变而来的&#xff0c;所以在学红黑树之前还是要好好的理解一下AVLTree树的原理&#xff0c;为理解红黑树减轻理解负担&#xff0c;好了进入正题。 红黑树原理&#xff1a; 由名可知&#xff0c;红黑树——肯定是与颜色有关的一个树…

操作系统——文件在外存中的分配方式(王道视频p61 P62)

1.总体概述&#xff1a; 连续分配 —— 链接分配 —— 索引分配 &#xff08;1&#xff09;对于顺序分配&#xff0c;这种方式 基本不会使用了&#xff0c; 因为 它存在一个 核心的问题就是 没法更新&#xff1b;不过&#xff0c;还是要注意它的 “文件目录”——其中存放了…

强化学习中策略的迭代

一、策略迭代 一旦使用vπ改善了策略π&#xff0c;产生了更好的策略π0&#xff0c;我们就可以计算vπ0并再次对其进行改进&#xff0c;产生更好的π00。因此&#xff0c;我们可以获得一系列单调改善的策略和值函数&#xff1a; 其中E−→表示策略评估&#xff0c;I−→表示策…

企业通配符SSL证书的特点

企业通配符SSL证书是一种数字证书&#xff0c;其可以用于保护多个企业网站&#xff0c;对网站传输信息进行加密服务。这种证书通常适用于拥有多个子域名或二级域名的企事业单位。今天就随SSL盾小编了解企业通配符SSL证书的相关信息。 1. 保护所有域名和子域名&#xff1a;企业通…

linux 启动引导找不到内核修复

问题现象 选中内核按e 看到引导内核信息 挂载ISO映像进入救援模式&#xff0c;查看boot目录 与 引导文件内容不一致 再次重启引导系统&#xff0c;按e 修改内核引导项与boot目录一致&#xff0c; crtl - x 继续执行 登录系统 mount /dev/sdm1 /mnt 挂载vfat 引导目录 纠…

CorelDRAW2024好不好用?怎么下载

cdr是CorelDRAW的简称&#xff0c;一款专注排版和矢量图形编辑的平面设计软件。这款软件的设计界面精微细致、简洁易懂。功能尤其强大&#xff0c;图标设计&#xff0c;印刷排版&#xff0c;服装设计等都可以胜任。还有多种模板使得设计相当的轻松&#xff0c;今天简单介绍一下…

C语言查看各数据类型所占大小

编译器&#xff1a;VC2010 #include<stdio.h> int main() {printf("%d\n",sizeof(char));printf("%d\n",sizeof(short));printf("%d\n",sizeof(int));printf("%d\n",sizeof(long));printf("%d\n",sizeof(long long))…

【Python语言】集合的使用方法总结

目录 1、集合基本知识&#xff1a; 2、定义 2.1 定义集合变量 2.2 定义空集合 3、集合的常用操作 3.1 定义集合 3.2 添加新元素 3.3 移除元素 3.4 从集合中随机取出元素 3.5 清空集合 3.6 取两个集合的差集 3.7 消除两个集合的差集 3.8 两个集合合并 3.9 统计集合…

软件外包开发质量控制方法

在软件外包开发项目中&#xff0c;质量控制是确保交付的软件符合预期质量标准的关键步骤。以下是一些常用的软件外包开发质量控制方法&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 需求明确&#x…

【第28例】IPD体系进阶 | 需求管理:需求实现过程

目录 简介 内容详解 CSDN学院相关推荐 作者简介 简介 继续 IPD 体系中的需求管理相关的专题。 先来看看整个需求管理涉及的过程内容: 需求管理流程主要包含五个阶段: 需求收集; 需求分析; 需求分发/分配;

构建数字孪生的四大挑战

如果不能解决由数字孪生带来的开发难题&#xff0c;那么企业就无法享受这种技术便利。 数字孪生已经成为企业当前面对的一大机遇&#xff0c;其核心在于利用虚拟副本中的分析数据对未来业务事件开展预测。这不仅能够大大降低决策难度&#xff0c;同时也有助于提升决策效果。 然…

“利用义乌购API揭秘跨境贸易商机:一键获取海量优质商品列表!“

义乌购API可以根据关键词取商品列表。通过调用义乌购API的item_search接口&#xff0c;传入关键词参数&#xff0c;可以获取到符合该关键词的商品列表。 以下是使用义乌购API根据关键词取商品列表的步骤&#xff1a; 注册义乌购开发者账号并获取授权码和密钥。在代码中导入义…

redis教程 二 redis客户端Jedis使用

文章目录 Redis的Java客户端-JedisJedis快速入门创建工程&#xff1a;引入依赖&#xff1a;建立连接测试&#xff1a;释放资源Jedis连接池创建Jedis的连接池改造原始代码 Redis的Java客户端-SpringDataRedis快速入门导入pom坐标配置文件测试代码 数据序列化器StringRedisTempla…

【CIO人物展】黄淮学院副CIO周鹏:构建数智化平台赋能学校高质量发展

周鹏 本文由黄淮学院副CIO周鹏投递并参与《2023中国数智化转型升级优秀CIO》榜单/奖项评选。丨推荐企业—锐捷网络 大数据产业创新服务媒体 ——聚焦数据 改变商业 黄淮学院是2004年经教育部批准成立的一所省属全日制普通本科高校。学校位于素有“豫州之腹地、天下之最中”之美…

【Python】批量下载素材酷视频资源

【需求】 做视频精彩需要用到梗图视频等,但是素材酷上面的视频没有搜索功能,每次用起来还要去下载也很麻烦,下载只能一个一个下载也很麻烦,下要搞一个能够批量下载的功能,然后把下载的资源全部放进万兴喵影编辑器的云空间,这样就可以做到随做随查随用了。 【效果】 目…