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指定密钥对长度1024
pRSA = 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待发送字符串长度,填充好以后首先运行服务端,接着运行客户端,此时数据包将会被加密传输,在对端解密并输出如下图所示的结果;

20.5 OpenSSL 套接字RSA加密传输的更多相关文章
- RSA加密传输代码示例
RSA加密传输代码示例 涉及敏感数据的传输,双方最好约定使用加密解密.那RSA非对称加密就大有作为了.服务端可以保留自己的私钥,发给客户端对应的公钥.这样就可以互相加解密了.php中rsa加解密实现: ...
- php RSA加密传输代码示例(轉)
原文地址:http://www.cnblogs.com/firstForEver/p/5803940.html 涉及敏感数据的传输,双方最好约定使用加密解密.那RSA非对称加密就大有作为了. 服务端可 ...
- php RSA加密传输代码示例
涉及敏感数据的传输,双方最好约定使用加密解密.那RSA非对称加密就大有作为了. 服务端可以保留自己的私钥,发给客户端对应的公钥.这样就可以互相加解密了.php中rsa加解密实现: 首先要生成一对公钥私 ...
- iOS动态部署之RSA加密传输Patch补丁
概要:这一篇博客主要说明下iOS客户端动态部署方案中,patch(补丁)是如何比较安全的加载到客户端中. 在整个过程中,需要使用RSA来加密(你可以选择其它的非对称加密算法),MD5来做校验(同样,你 ...
- linux网络编程-(socket套接字编程UDP传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
- socket套接字TCP协议传输-案例测试
术语: 套接字接口:socket,是一个IP地址和一个端口号的组合,套接字可以唯一标识整个Internet中的一个网络进程. TCP连接:一对套接字接口(一个用于接收,一个用于发送)可定义面向连接的协 ...
- openssl pem密钥文件rsa加密解密例子
准备工作 命令行加密解密,用与比对代码中的算法和命令行的算法是否一致 C:\openssl_test>openssl rsautl -encrypt -in data.txt -inkey pu ...
- Js参数RSA加密传输,jsencrypt.js的使用
注意几点: 1.参数传递的+号处理,在传输时会把+变成空格,不处理后端就报错了. 1.前段代码 <!DOCTYPE html> <html> <head> < ...
- thrift之TTransport层的堵塞的套接字I/O传输类TSocket
本节将介绍第一个实现具体传输功能的类TSocket,这个类是基于TCP socket实现TTransport的接口.下面具体介绍这个类的相关函数功能实现. 1.构造函数 分析一个类的功能首先看它的定义 ...
- python基础--基于套接字进行文件传输、异常处理、socketserver模块
异常处理: 什么是异常处理: 程序在运行过程中出现了不可预知的错误,并且该错误没有对应的处理机制,那么就会以异常的形式表现出来,造成的影响就是整个程序无法再正常运行 异常的结构: 异常的类型.异常的信 ...
随机推荐
- Python最佳实践书籍《Python 工匠》摘要
<Python工匠>是一本案例.技巧与工程实践的指导书,该书不是python基础语法的教程,而是python中最佳实践的教程,属于python进阶类的书籍.可以将本书当做PEP8编程规范的 ...
- C#9.0:Top-Level Programs
我们称之为顶级层序 用 C# 编写一个简单的程序需要大量的样板代码,引用,类.方法.结构体等: 1 class Program 2 { 3 static void Main(string[] args ...
- Codeforce:723A. The New Year: Meeting Friends (水题)
题意:有三个好朋友的家都住在x轴的不同坐标,问新年的时候三个朋友之间问候走的最短距离 max{(a,b,c)} - min{(a,b,c)} 即可 编译器由 VS2017 切换到VScode使用,纪念 ...
- jdk(jvm)调式工具
JConsole 可视化工具介绍一. JConsole介绍1.1 JConsole描述Jconsole (Java Monitoring and Management Console),一种基于JMX ...
- 【JavaWeb】JSP基础和应用
JSP基础 JSP简介 JSP全称是Java Server Page,它和Servlet一样,也是sun公司推出的一套开发动态web资源的技术,称为JSP/Servlet规范.JSP的本质其实就是一个 ...
- JavaScript 对象和 JSON 的区别
参考原文:https://blog.csdn.net/jiaojiao772992/article/details/77871785/ 2.1 对象和 JSON 的区别 JSON 就是 JavaScr ...
- 2023年度总结,互联网寒冬是躺平OR寻找风口
一.前言 又到了年底,这一年过的真的好快,犹如白驹过隙. 身体快跃过去了,灵魂还没有. 拿起键盘却迟迟无法下手,经过三天终于完成了! 这是很颓很丧的一年,很难看到自己的成长,就像登山卡在半山腰,开车堵 ...
- [转帖]SQL Server 性能调优
性能调优2:CPU 关系型数据库严重依赖底层的硬件资源,CPU是服务器的大脑,当CPU开销很高时,内存和硬盘系统都会产生不必需要的压力.CPU的性能问题,直观来看,就是任务管理器中看到的CPU ...
- [转帖]使用 goofys 挂载 S3 bucket 为文件系统
https://xie.infoq.cn/article/7f178e0a1315f758d77c6c2bb 背景 公司的 gitlab 目前都是直接存储在物理盘上,为了确保数据不会丢失,需要重复多次 ...
- [转帖]Nginx 使用与异常处理
http://jartto.wang/2017/04/15/nginx-exception-handling/ 以前总是偷懒使用 Http-Server 来启动一个本地服务,后来花时间学习了一下 Ng ...