阿里巴巴外贸平台费用,厦门网站优化,成都市住房和建设局官网,永久免费随身wifiRSA算法同样可以用于加密传输#xff0c;但此类加密算法虽然非常安全#xff0c;但通常不会用于大量的数据传输#xff0c;这是因为RSA算法加解密过程涉及大量的数学运算#xff0c;尤其是模幂运算#xff08;即计算大数的幂模运算#xff09;#xff0c;这些运算对于计…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.hextern 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.hextern 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.hextern 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待发送字符串长度填充好以后首先运行服务端接着运行客户端此时数据包将会被加密传输在对端解密并输出如下图所示的结果