The RSA algorithm can also be used for encrypted transmission, but although this type of encryption algorithm is very secure, it is usually not used for large amounts of data transmission. This is because the encryption and decryption process of the RSA
algorithm involves a large number of mathematical operations. Especially modular exponentiation (that is, calculating the modular exponentiation of large numbers), these operations are very time-consuming for computers.
Secondly, in the RSA
algorithm, the length of the encrypted data cannot exceed the key length minus a certain padding length. Generally speaking, when the RSA key length is 1024
bits, the encryption length can be 128
bytes, and when the key length is 2048
bits , the encrypted length is 245
bytes; when the key length is 3072
bits, the encrypted length is 371
bytes. Therefore, if the length of the data that needs to be encrypted exceeds the allowed range of the key length, segmented encryption can be used. We can cut the packet into 128
characters each, so that we can transmit a large number of strings in a loop.
20.5.1 Encryption and decryption algorithm encapsulation
In the previous chapters, we used the command line to manually generate the key pair file. In fact, in OpenSSL
we can use the function provided by SDK
to automatically generate the corresponding key pair file. Encryption key pair file, in the following code, CreateRSAPEM
is a function that generates a key pair. Pass a public key, private key, and data length to the function to get two RSA files.
#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") // Generate RSA public and private key files BOOL CreateRSAPEM(char *PublicKey, char *PrivateKey, int KeySize) {<!-- --> BIO* bpub, *bpri; //Create new key pair files separately bpub = BIO_new_file(PublicKey, "w"); bpri = BIO_new_file(PrivateKey, "w"); if (!bpub || !bpri) {<!-- --> return FALSE; } RSA* pRSA = 0; // Generate a key pair KeySize and specify the key pair length 1024 pRSA = RSA_generate_key(KeySize, RSA_F4, NULL, NULL); if (pRSA != NULL) {<!-- --> // write the public key if (!PEM_write_bio_RSAPublicKey(bpub, pRSA)) {<!-- --> return FALSE; } //Write the private key 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[]) {<!-- --> // Generate public and private keys BOOL flag = CreateRSAPEM("public.rsa","private.rsa",1024); if (flag == TRUE) {<!-- --> printf("[*] Key pair generated\ "); } system("pause"); return 0; }
After the code is run, two files, public.rsa
public key and private.rsa
private key, will be generated in the current directory, as shown in the figure below;
Next is the encapsulation implementation of the encryption and decryption functions. In order to better realize network transmission, the following are the four encapsulated functions, among which public_rsa_encrypt
is used to encrypt strings using the public key, The private_rsa_decrypt
function uses the private key to decrypt the string, private_rsa_encrypt
uses the private key to encrypt, and public_rsa_decrypt
uses the public key to decrypt. Readers can choose different ones according to their actual needs. encryption and decryption functions.
//Use public key encryption 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; } // Decrypt using private key 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; } // Encrypt using private key 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; } // Decrypt using public key 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; }
When we need to use public key encryption, we can call the public_rsa_encrypt
function and pass in the pre-encrypted string, the public key path and the encrypted storage location. When we need to decrypt, call 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 }; // Public key encryption if (public_rsa_encrypt(text, public_key, encry)) {<!-- --> printf("[Public key encryption] Encryption length: %d \ ", strlen((char*)encry)); } //Private key decryption if (private_rsa_decrypt(encry, private_key, decry)) {<!-- --> printf("[Private key decryption] Decryption length: %d \ ", strlen((char*)decry)); } printf("Decrypted data: %s \ ", decry); system("pause"); return 0; }
Readers can compile and run the above code by themselves to see the encrypted and decrypted data output, as shown in the figure below;
Use this process in reverse, use the private key to encrypt the data, and use the public key to decrypt the data. The code is as follows;
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 }; //Private key encryption if (private_rsa_encrypt(text, private_key, encry)) {<!-- --> printf("[Private key encryption] Encryption length: %d \ ", strlen((char*)encry)); } // Public key decryption if (public_rsa_decrypt(encry, public_key, decry)) {<!-- --> printf("[Public key decryption] Decryption length: %d \ ", strlen((char*)decry)); } printf("Decrypted data: %s \ ", decry); system("pause"); return 0; }
Private key encryption and public key decryption, the output rendering is as shown below;
20.5.2 Encrypted transmission string
After having the above encryption and decryption function implementation process, we can then implement the encrypted transmission function for strings. Because we use a 1024
bit key, we can only transmit 128
characters. In order to transmit a large number of characters, the characters need to be divided into blocks. The CutSplit()
function is used to cut the string every 100
characters. , and then use the public key to encrypt it in the client. After encryption, it is enough to transmit a batch of encrypted data in blocks until the complete string is sent.
#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") // Use public key encryption 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; } // Implement cutting at the specified position of the string char* Cut(char* buffer, int offset, int length) {<!-- --> char Split[100] = {<!-- --> 0 }; memset(Split, 0, 100); strncpy(Split, buffer + offset, length); return Split; } // Loop to cut string int CutSplit(char* buf, char len, OUT char Split[][1024]) {<!-- --> int count = 0; //Cut the len size each time 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; //Initialize the socket library if (WSAStartup(MAKEWORD(2, 0), & amp;WSAData)) {<!-- --> return 0; } // Create Socket 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"); // Connect to the server if (connect(client_socket, (LPSOCKADDR) & amp;ClientAddr, sizeof(ClientAddr)) != SOCKET_ERROR) {<!-- --> char SplitArray[100][1024] = {<!-- --> 0 }; //Cut the string, once every 100 characters int count = CutSplit(buf, 100, SplitArray); // Number of times to send packets std::cout << "Number of packets sent: " << count << std::endl; char send_count[1024] = {<!-- --> 0 }; sprintf(send_count, "%d", count); send(client_socket, send_count, strlen(send_count), 0); // Send data packets in a loop for (int x = 0; x < count; x + + ) {<!-- --> std::cout << "Original packet: " << SplitArray[x] << std::endl; char public_key[] = "d://public.rsa"; char encry[1024] = {<!-- --> 0 }; // Public key encryption if (public_rsa_encrypt(SplitArray[x], public_key, encry)) {<!-- --> std::cout << "RSA encryption length: " << strlen((char*)encry) << std::endl; } //Send encrypted data packet send(client_socket, encry, 1024, 0); memset(buf, 0, sizeof(buf)); memset(encry, 0, sizeof(encry)); } closesocket(client_socket); WSACleanup(); } system("pause"); return 0; }
As for the server
code implementation part, it needs to be consistent with the client. The client receives as many times as the server sends. First, the server receives the number of data packets it needs to receive, and uses this as a loop. Conditional use, accept data packets through an uninterrupted loop, and call private_rsa_decrypt
to complete the decryption of the data packets, and finally splice the data packets into recv_message_all
and output the complete packet.
#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") // Decrypt using private key 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; //Initialize the socket library if (WSAStartup(MAKEWORD(2, 0), & amp;WSAData)) {<!-- --> return 0; } // Create Socket 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 socket bind(server_socket, (LPSOCKADDR) & amp;ServerAddr, sizeof(ServerAddr)); listen(server_socket, 10); SOCKET message_socket; //Receive and splice data char recv_message_all[8109] = {<!-- --> 0 }; if ((message_socket = accept(server_socket, (LPSOCKADDR)0, (int*)0)) != INVALID_SOCKET) {<!-- --> //Receive the number of times you need to get char recv_count[1024] = {<!-- --> 0 }; recv(message_socket, recv_count, 1024, 0); std::cout << "Number of packets received: " << recv_count << std::endl; for (int x = 0; x < atoi(recv_count); x + + ) {<!-- --> //Receive encrypted data packets char buf[1024] = {<!-- --> 0 }; recv(message_socket, buf, 1024, 0); //Private key decryption char private_key[] = "d://private.rsa"; char decry[1024] = {<!-- --> 0 }; // Call the decryption function if (private_rsa_decrypt(buf, private_key, decry)) {<!-- --> std::cout << "RSA decryption length: " << strlen((char*)decry) << std::endl; } std::cout << "RSA decrypted packet: " << decry << std::endl; // Combine data packets strCut(recv_message_all, decry); memset(buf, 0, sizeof(buf)); memset(decry, 0, sizeof(decry)); } closesocket(message_socket); // Output the final data packet std::cout << std::endl; std::cout << "Combined packet: " << recv_message_all << std::endl; } closesocket(server_socket); WSACleanup(); system("pause"); return 0; }
Readers can fill in the length of the buf
string to be sent in the client by themselves. After filling, run the server first, and then run the client. At this time, the data packet will be encrypted and transmitted, decrypted and processed at the opposite end. The output is as shown in the figure below;