20.6 OpenSSL 套接字分发RSA公钥

通过上一节的学习读者应该能够更好的理解RSA加密算法在套接字传输中的使用技巧,但上述代码其实并不算完美的,因为我们的公钥和私钥都必须存储在本地文本中且公钥与私钥是固定的无法做到更好的保护效果,而一旦公钥与私钥泄密则整个传输流程都将会变得不安全,最好的保护效果是RSA密钥在每次通信时都进行变换,依次来实现随机密钥对的功能。

20.6.1 RSA算法封装

要实现这个效果我们就需要封装一套可以在内存中生成密钥对的函数,当需要传输数据时动态的生成密钥对,并将公钥部分通过套接字传输给对应的客户端,当客户端收到公钥后则可以使用该公钥进行通信,此时公钥与私钥全程不会存储为文件,这能极大的提升RSA算法的安全性。

要实现内存传输则首先需要封装实现RSA内存生成密钥对函数GenerateMemoryRSAKeys,以及rsa_encrypt加密函数,rsa_decrypt解密函数,读者可自行理解并使用如下代码片段。

#include <iostream>
#include <Windows.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/crypto.h>extern "C"
{
#include <openssl/applink.c>
}#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"libssl_static.lib")
#pragma comment(lib,"libcrypto.lib")// 生成RSA需要的公钥和私钥
BOOL GenerateMemoryRSAKeys(char** private_key, char** public_key, int key_length)
{// 生成Key函数RSA* keypair = RSA_generate_key(key_length, 3, NULL, NULL);BIO* pri = BIO_new(BIO_s_mem());BIO* pub = BIO_new(BIO_s_mem());// 生成写出私钥if (!PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL)){return FALSE;}// 生成写出公钥if (!PEM_write_bio_RSAPublicKey(pub, keypair)){return FALSE;}size_t pri_len = BIO_pending(pri);size_t pub_len = BIO_pending(pub);// 分配内存存储公钥与私钥char* prikey = (char*)malloc(pri_len + 1);char* pubkey = (char*)malloc(pub_len + 1);if (prikey == NULL && pubkey == NULL){return FALSE;}// 将公钥与私钥读入到堆中BIO_read(pri, prikey, pri_len);BIO_read(pub, pubkey, pub_len);*private_key = prikey;*public_key = pubkey;RSA_free(keypair);BIO_free_all(pri);BIO_free_all(pub);return TRUE;
}// RSA 加密函数
// type=public 使用公钥加密 type=private 使用私钥加密
BOOL rsa_encrypt(char* pub_key, char* msg, char** encrypt, int* encrypt_len, char *type)
{RSA* rsa = NULL;BIO* keybio = BIO_new_mem_buf((void*)pub_key, -1);char* err = (char*)malloc(130);if (keybio == NULL){return FALSE;}// 如果是public则使用公钥加密/如果是private则使用私钥if (strcmp(type, "public") == 0){// PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL)if (!(rsa = PEM_read_bio_RSAPublicKey(keybio, NULL, NULL, NULL))){return FALSE;}}else if (strcmp(type, "private") == 0){// 读取私钥文件if (!(rsa = PEM_read_bio_RSAPrivateKey(keybio, NULL, NULL, NULL))){return FALSE;}}*encrypt_len = RSA_size(rsa);*encrypt = (char*)malloc(4096);if (strcmp(type, "public") == 0){// 使用公钥加密if ((RSA_public_encrypt(strlen(msg) + 1, (unsigned char*)msg, (unsigned char*)*encrypt, rsa, RSA_PKCS1_PADDING)) == -1){return FALSE;}}else if (strcmp(type, "private") == 0){// 使用私钥加密if ((RSA_private_encrypt(strlen(msg) + 1, (unsigned char*)msg, (unsigned char*)*encrypt, rsa, RSA_PKCS1_PADDING)) == -1){return FALSE;}}RSA_free(rsa);free(err);BIO_free_all(keybio);return TRUE;
}// RSA 解密函数
// type=public 使用公钥解密 type=private 使用私钥解密
BOOL rsa_decrypt(char* pri_key, char* msg, char** decrypt, int encrypt_len, char *type)
{RSA* rsa = NULL;BIO* keybio = BIO_new_mem_buf(pri_key, -1);if (keybio == NULL){return FALSE;}// 如果是public则使用公钥解密/如果是private则使用私钥if (strcmp(type, "public") == 0){// 读入公钥文件if (!(rsa = PEM_read_bio_RSAPublicKey(keybio, NULL, NULL, NULL))){return FALSE;}}else if (strcmp(type, "private") == 0){// PEM_read_bio_RSA_PRIVATE(keybio, NULL, NULL, NULL)if (!(rsa = PEM_read_bio_RSAPrivateKey(keybio, NULL, NULL, NULL))){return FALSE;}}char* err = (char*)malloc(130);*decrypt = (char*)malloc(encrypt_len);if (strcmp(type, "public") == 0){// 使用公钥解密if (RSA_public_decrypt(encrypt_len, (unsigned char*)msg, (unsigned char*)*decrypt, rsa, RSA_PKCS1_PADDING) == -1){return FALSE;}}else if (strcmp(type, "private") == 0){// 私用私钥解密if (RSA_private_decrypt(encrypt_len, (unsigned char*)msg, (unsigned char*)*decrypt, rsa, RSA_PKCS1_PADDING) == -1){return FALSE;}}RSA_free(rsa);free(err);BIO_free_all(keybio);return TRUE;
}int main(int argc, char *argv)
{// 生成内存RSA密钥对char *private_key, *public_key;if (GenerateMemoryRSAKeys(&private_key, &public_key, 2048)){std::cout << "生成私钥: " << private_key << std::endl;std::cout << "生成公钥: " << public_key << std::endl;}char *encrypt, *decrypt;int encrypt_length;BOOL flag;// 公钥加密flag = rsa_encrypt(public_key, (char*)"hello lyshark", &encrypt, &encrypt_length, (char *)"public");if (flag == TRUE){std::cout << "[公钥加密] 公钥加密字节: " << strlen(encrypt) << std::endl;}// 私钥解密flag = rsa_decrypt(private_key, encrypt, &decrypt, encrypt_length, (char *)"private");if (flag == TRUE){std::cout << "[私钥解密] 私钥解密字节: " << decrypt << std::endl;}// 私钥加密flag = rsa_encrypt(private_key, (char*)"hello lyshark", &encrypt, &encrypt_length, (char*)"private");if (flag == TRUE){std::cout << "[私钥加密] 私钥加密字节: " << strlen(encrypt) << std::endl;}// 公钥解密flag = rsa_decrypt(public_key, encrypt, &decrypt, encrypt_length, (char*)"public");if (flag == TRUE){std::cout << "[公钥解密] 公钥解密字节: " << decrypt << std::endl;}system("pause");return 0;
}

读者可自行编译上述代码并运行,此时该代码将通过GenerateMemoryRSAKeys函数生成内存密钥对,并调用rsa_encryptrsa_decrypt两个函数实现对特定字符串的加解密功能,输出效果图如下;

20.6.2 公钥动态配对

有了上述内存生成RSA密钥对的方法,那么实现密钥对远程分发将变得很容易实现,首先我们来看客户端的实现方式,当客户端成功连接到了服务端则首先接收服务端传来的公钥,当收到服务器传来的公钥后通过使用rsa_encrypt函数并用公钥对待发送字符串进行加密,加密后调用send将加密数据发送给服务端,解密动作与加密保持一致,同样使用公钥进行解密,这段客户端代码如下所示;

int main(int argc, char* argv[])
{char buf[256] = "The National Aeronautics and Space Administration";WSADATA WSAData;// 初始化套接字库if (WSAStartup(MAKEWORD(2, 0), &WSAData)){return 0;}// 创建套接字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 public_key[1024] = { 0 };int recv_key_flag = recv(client_socket, public_key, 1024, 0);if (recv_key_flag > 0){std::cout << "接收公钥字节: " << public_key << std::endl;}// 公钥加密并发送数据char* encrypt = nullptr;int encrypt_length = 0;rsa_encrypt(public_key, buf, &encrypt, &encrypt_length, (char*)"public");std::cout << "[服务端发送] 公钥加密字节: " << strlen(encrypt) << std::endl;send(client_socket, encrypt, encrypt_length, 0);// 公钥接收并解密数据char* decrypt = nullptr;memset(buf, 0, 256);recv(client_socket, buf, 256, 0);rsa_decrypt(public_key, buf, &decrypt, 256, (char *)"public");std::cout << "[服务端返回] 原始数据包: " << decrypt << std::endl;closesocket(client_socket);WSACleanup();}system("pause");return 0;
}

与客户端相比,服务端在执行时只是多出来了执行GenerateMemoryRSAKeys函数的功能,通过执行该函数我们可以得到一个动态的内存加密密钥对,有了密钥对则我们就可以使用私钥对数据进行加密与解密操作,如下是服务端核心实现代码;

int main(int argc, char* argv[])
{WSADATA WSAData;// 初始化套接字库if (WSAStartup(MAKEWORD(2, 0), &WSAData)){return 0;}// 创建套接字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);// 生成RSA密钥对char* private_key, *public_key;BOOL gen_flag = GenerateMemoryRSAKeys(&private_key, &public_key, 2048);if (gen_flag == TRUE){// std::cout << "生成私钥: " << private_key << std::endl;// std::cout << "生成公钥: " << public_key << std::endl;std::cout << "[+] 已生成RSA密钥对" << std::endl;}// 接收请求SOCKET message_socket;if ((message_socket = accept(server_socket, (LPSOCKADDR)0, (int*)0)) != INVALID_SOCKET){// 发送公钥给客户端int put_key_flag = send(message_socket, public_key, strlen(public_key), 0);if (put_key_flag > 0){std::cout << "本地私钥字节: " << private_key << std::endl;std::cout << "发送公钥字节: " << public_key << std::endl;}// 私钥解密: 接收并解密char recv_message[256] = { 0 };recv(message_socket, recv_message, 256, 0);char* decrypt = nullptr;rsa_decrypt(private_key, recv_message, &decrypt, 256, (char*)"private");std::cout << "[客户端返回] 原始数据包: " << decrypt << std::endl;// 私钥加密: 加密并发送char send_message[256] = "hello lyshark";char* encrypt = nullptr;int encrypt_length = 0;rsa_encrypt(private_key, send_message, &encrypt, &encrypt_length, (char*)"private");send(message_socket, encrypt, encrypt_length, 0);}closesocket(server_socket);WSACleanup();system("pause");return 0;
}

读者可自行编译并运行上述代码,首先运行服务端接着运行客户端,读者则可看到如下图所示的输出信息;

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

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

相关文章

Java 8 新特性 Stream 的使用场景(不定期更新)

方便在写代码的过程中直接使用&#xff0c;好记性不如好文章&#xff0c;直接 CV 改了直接用。提高 办&#xff08;摸&#xff09;公&#xff08;鱼&#xff09;效&#xff08;时&#xff09;率&#xff08;间&#xff09;&#xff0c; 不然就直接问 GPT 也不是说不行。 只符合…

数据专业融合型人才:未来职场新宠,发展良机不容错过

数据技术相关 概述数据价值好书推荐《数据要素安全流通》《Python数据挖掘&#xff1a;入门、进阶与实用案例分析》《数据保护&#xff1a;工作负载的可恢复性 》《Data Mesh权威指南》《分布式统一大数据虚拟文件系统 Alluxio原理、技术与实践》《云原生数据中台&#xff1a;架…

使用Dockerfile生成docker自定义镜像

Dockerfile常用指令 • FROM 构建镜像基于哪个镜像 • MAINTAINER 镜像维护者姓名或邮箱地址 • RUN 构建镜像时运行的指令,执行一条RUN镜像就会叠加一层&#xff0c;因此RUN尽可能一条写完 • ADD 拷贝文件或目录到容器中&#xff0c;如果是URL或压缩包便会自动下载或自动解压…

缺陷之灵魂操作bug

一、前言 正常来说&#xff0c;我们在测试缺陷的时候都是按照case来测试的&#xff0c;但是有些场景&#xff0c;例如说发散思维这种场景&#xff0c;就会找到一些比较不太正常、不好复现的缺陷&#xff0c;然后如果要辅助研发修复&#xff0c;就会极为痛苦。 二、场景描述 大…

加速软件开发和交付的革命性方法-DevOps

“ 随着信息技术的快速发展&#xff0c;现代软件开发和交付已经经历了巨大的变革。DevOps&#xff08;Development和Operations的结合&#xff09;已经成为这一变革的关键推动力&#xff0c;让开发团队和运维团队之间的界限变得模糊&#xff0c;以加速软件的开发、测试和部署过…

京东数据平台:2023年Q3季度黄金市场数据分析

继9月国内黄金市场持续上涨后&#xff0c;进入10月中下旬后&#xff0c;黄金行情再度反转&#xff0c;多家品牌金饰价格再次突破600元/克&#xff0c;达到611元/克。 今年以来&#xff0c;黄金行情不断走俏&#xff0c;销售市场也有明显增长。根据鲸参谋平台的数据显示&#xf…

算法随想录算法训练营第四十七天| 647. 回文子串 516.最长回文子序列

647. 回文子串 题目&#xff1a;给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字…

一、技术选型(从零开始撸斗地主)

将心沉下来&#xff0c;构建一个自己属于自己的城堡。 最近准备搞一个能承载上万人同时在线的斗地主游戏。 技术选型 客户端&#xff1a;Unity3D 目前有俩个选项&#xff0c;unity3d,cocosCreator 思考了很久&#xff0c;最终决定选用自己擅长的框架来搞。 服务器&#xff…

安装docker可视化工具:Portainer

文章目录 前言一、安装Portainer&#xff08;docker可视化工具&#xff09;1.拉镜像2. 启动容器3.查看4.访问 总结 前言 一、安装Portainer&#xff08;docker可视化工具&#xff09; 1.拉镜像 docker pull portainer/portainer2. 启动容器 docker run -d -p 8100:8000 -p 9…

全功能测试框架 - Carina

Carina是一个基于Java的测试自动化框架&#xff0c;它将所有测试层结合在一起&#xff1a;移动应用程序&#xff08;web、本机、混合&#xff09;、web应用程序、REST服务和数据库。 github&#xff1a;GitHub - zebrunner/carina: Carina automation framework (TestNG): Web…

循环链表(单循环、双循环)(数据结构与算法)

循环链表&#xff1a;循环单链表、循环双链表 1. 循环单链表 循环单链表&#xff08;Circular Singly Linked List&#xff09;是一种特殊类型的单链表&#xff0c;其中最后一个节点的指针指向头节点&#xff0c;形成一个循环。 循环单链表与普通单链表的主要区别在于&#xf…

我用 ChatGPT 的探索与实践

本文主要介绍在实际的开发过程当中&#xff0c;如何使用GPT帮助开发&#xff0c;优化流程&#xff1b;本文所有代码和脚本均是利用GPT生成。 技术交流群 建了技术答疑、交流群&#xff01;想要进交流群、资料的同学&#xff0c;可以直接加微信号&#xff1a;mlc2060。加的时候…