20.2 OpenSSL asymmetric RSA encryption and decryption algorithm

The RSA algorithm is an asymmetric encryption algorithm jointly invented by three mathematicians Rivest, Shamir and Adleman, named after them. Named with initials. The security of the RSA algorithm is based on the large number decomposition problem, that is, for a very large composite number, it is very difficult to decompose it into the product of two prime numbers.

The RSA algorithm is a commonly used asymmetric encryption algorithm. Different from the symmetric encryption algorithm, the RSA algorithm uses a pair of asymmetric keys, namely the public key and the private key. The public key and the private key are generated in pairs. The public key can The public key is used to encrypt data and verify digital signatures, while the private key must be kept secret and is used to decrypt data and generate digital signatures. Therefore, the usage scenarios of the RSA algorithm are public key encryption and private key decryption, or private key encryption and public key decryption.

The OpenSSL library provides support for such algorithms, but readers need to generate public and private key files by themselves when using them. There is a openssl.exe program in the development tool kit, which is Tool for generating key pairs. When we need to use an asymmetric encryption algorithm, we can use the following command to generate the public key and private key.

  • Generate private key: openssl genrsa -out rsa_private_key.pem 1024
  • Generate public key: openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

After executing the above two commands, readers can get the rsa_private_key.pem private key and the rsa_public_key.pem public key, as shown in the figure below;

When using asymmetric encryption, readers need to import the required header files separately, including rsa.h library for processing encryption algorithms, and pem.hLibraries for handling private keys. These two libraries must be imported when using RSA.

#include <iostream>
#include <string>
#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")

20.2.1 Public key encryption and private key decryption

The RSA public key is used to encrypt data and verify digital signatures, and the private key is used to decrypt data and generate digital signatures. It is usually used in public key encryption and private key decryption scenarios. It has high security, but encryption and decryption speeds are slow. , so a hybrid encryption method is usually used, that is, using the RSA algorithm to encrypt the key in the symmetric encryption algorithm, and then using the symmetric encryption algorithm to encrypt the data to ensure the confidentiality of the data and the efficiency of encryption and decryption.

First, we implement the public key encryption function, as shown below. The Public_RsaEncrypt function accepts two parameters, which are the string that needs to be encrypted and the public key file. In the code, we first pass fopen()Open a public key file, read and initialize the public key file through the PEM_read_RSA_PUBKEY function, and then call RSA_public_encrypt. This function is mainly used to implement public key encryption. When encrypting After success, the encrypted text content is returned, the type is string.

//Public key encryption
std::string Public_RsaEncrypt(const std::string & amp; str, const std::string & amp; path)
{<!-- -->
    RSA* rsa = NULL;
    FILE* file = NULL;
    char* ciphertext = NULL;
    int len = 0;
    int ret = 0;

    file = fopen(path.c_str(), "r");
    if (file == NULL)
    {<!-- -->
        return std::string();
    }

    rsa = PEM_read_RSA_PUBKEY(file, NULL, NULL, NULL);
    if (rsa == NULL)
    {<!-- -->
        ERR_print_errors_fp(stdout);
        fclose(file);
        return std::string();
    }

    len = RSA_size(rsa);
    ciphertext = (char*)malloc(len + 1);
    if (ciphertext == NULL)
    {<!-- -->
        RSA_free(rsa);
        fclose(file);
        return std::string();
    }
    memset(ciphertext, 0, len + 1);

    ret = RSA_public_encrypt(str.length(), (unsigned char*)str.c_str(), (unsigned char*)ciphertext, rsa, RSA_PKCS1_PADDING);
    if (ret < 0)
    {<!-- -->
        ERR_print_errors_fp(stdout);
        free(ciphertext);
        RSA_free(rsa);
        fclose(file);
        return std::string();
    }

    std::string s(ciphertext, ret);
    free(ciphertext);
    RSA_free(rsa);
    fclose(file);
    return s;
}

Similar to the public key encryption method, the Private_RsaDecrypt function is used to decrypt using the private key. The function accepts two parameters. The first parameter is the encrypted string data, and the second parameter is the private key. The specific path of the key, the function uses PEM_read_RSAPrivateKey to initialize the private key, and uses the RSA_private_decrypt function to implement the decryption of a specific string.

//Private key decryption
std::string Private_RsaDecrypt(const std::string & amp; str, const std::string & amp; path)
{<!-- -->
    RSA* rsa = NULL;
    FILE* file = NULL;
    char* plaintext = NULL;
    int len = 0;
    int ret = 0;

    file = fopen(path.c_str(), "r");
    if (file == NULL)
    {<!-- -->
        return std::string();
    }

    rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);
    if (rsa == NULL)
    {<!-- -->
        ERR_print_errors_fp(stdout);
        fclose(file);
        return std::string();
    }

    len = RSA_size(rsa);
    plaintext = (char*)malloc(len + 1);
    if (plaintext == NULL)
    {<!-- -->
        RSA_free(rsa);
        fclose(file);
        return std::string();
    }
    memset(plaintext, 0, len + 1);

    ret = RSA_private_decrypt(str.length(), (unsigned char*)str.c_str(), (unsigned char*)plaintext, rsa, RSA_PKCS1_PADDING);
    if (ret < 0)
    {<!-- -->
        ERR_print_errors_fp(stdout);
        free(plaintext);
        RSA_free(rsa);
        fclose(file);
        return std::string();
    }
    std::string s(plaintext, ret);

    free(plaintext);
    RSA_free(rsa);
    fclose(file);
    return s;
}

It is also very easy to call these two pieces of code. The following code snippets implement the encryption and decryption functions of the text string respectively, using the public key to encrypt and the private key to decrypt.

int main(int argc, char* argv[])
{<!-- -->
  std::string text = "hello lyshark";

  // Public key encryption
  std::string public_path = "d://rsa_public_key.pem";
  std::string encry = Public_RsaEncrypt(text, public_path);
  // std::cout << "Encrypted text: " << encry << std::endl;

  //Private key decryption
  std::string private_path = "d://rsa_private_key.pem";
  std::string decry = Private_RsaDecrypt(encry, private_path);
  std::cout << "Decrypted text: " << decry << std::endl;

  system("pause");
  return 0;
}

The output of this code is as shown below;

20.2.2 Private key encryption and public key decryption

In the RSA algorithm, private key encryption and public key decryption are not a common use because the private key is used for signing rather than encryption. The usual way of using it is to use public key encryption and private key decryption, which can ensure the confidentiality of the data. Only the person with the private key can decrypt the data, but at some point we have to reverse this process and use the private key. Encrypt and decrypt using public key.

The encapsulation code of private key encryption is as follows, where Private_RsaEncrypt is used to implement private key encryption. This function also accepts two parameters, which are the string to be encrypted and the current private key path. The core part of the function Yes RSA_private_encrypt This function can be used to encrypt data using a private key.

//Private key encryption
std::string Private_RsaEncrypt(const std::string & amp; str, const std::string & amp; path)
{<!-- -->
    RSA* rsa = NULL;
    FILE* file = NULL;
    char* ciphertext = NULL;
    int len = 0;
    int ret = 0;

    file = fopen(path.c_str(), "r");
    if (file == NULL)
    {<!-- -->
        return std::string();
    }
    rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);

    if (rsa == NULL)
    {<!-- -->
        ERR_print_errors_fp(stdout);
        fclose(file);
        return std::string();
    }

    len = RSA_size(rsa);
    ciphertext = (char*)malloc(len + 1);
    if (ciphertext == NULL)
    {<!-- -->
        RSA_free(rsa);
        fclose(file);
        return std::string();
    }
    memset(ciphertext, 0, len + 1);

    ret = RSA_private_encrypt(str.length(), (unsigned char*)str.c_str(), (unsigned char*)ciphertext, rsa, RSA_PKCS1_PADDING);
    if (ret < 0)
    {<!-- -->
        ERR_print_errors_fp(stdout);
        free(ciphertext);
        RSA_free(rsa);
        fclose(file);
        return std::string();
    }

    std::string s(ciphertext, ret);
    free(ciphertext);
    RSA_free(rsa);
    fclose(file);
    return s;
}

The implementation method of public key decryption is exactly the same as encryption. The Public_RsaDecrypt function in the code is used to implement public key decryption. The implementation of its core functions depends on the key function RSA_public_decrypt.

//Public key decryption
std::string Public_RsaDecrypt(const std::string & amp; str, const std::string & amp; path)
{<!-- -->
    RSA* rsa = NULL;
    FILE* file = NULL;
    char* plaintext = NULL;
    int len = 0;
    int ret = 0;

    file = fopen(path.c_str(), "r");
    if (file == NULL)
    {<!-- -->
        return std::string();
    }

    rsa = PEM_read_RSA_PUBKEY(file, NULL, NULL, NULL);
    if (rsa == NULL)
    {<!-- -->
        ERR_print_errors_fp(stdout);
        fclose(file);
        return std::string();
    }

    len = RSA_size(rsa);
    plaintext = (char*)malloc(len + 1);
    if (plaintext == NULL)
    {<!-- -->
        RSA_free(rsa);
        fclose(file);
        return std::string();
    }
    memset(plaintext, 0, len + 1);

    ret = RSA_public_decrypt(str.length(), (unsigned char*)str.c_str(), (unsigned char*)plaintext, rsa, RSA_PKCS1_PADDING);
    if (ret < 0)
    {<!-- -->
        ERR_print_errors_fp(stdout);
        free(plaintext);
        RSA_free(rsa);
        fclose(file);
        return std::string();
    }
    std::string s(plaintext, ret);

    free(plaintext);
    RSA_free(rsa);
    fclose(file);
    return s;
}

With the above method, it becomes easy to call the code. As shown below, we encrypt the text string using the private key and decrypt it using the public key.

int main(int argc, char* argv[])
{<!-- -->
  std::string text = "hello lyshark";

  //Private key encryption
  std::string private_path = "d://rsa_private_key.pem";
  std::string encry = Private_RsaEncrypt(text, private_path);
  // std::cout << "Encrypted text: " << encry << std::endl;

  // Public key decryption
  std::string public_path = "d://rsa_public_key.pem";
  std::string decry = Public_RsaDecrypt(encry, public_path);
  std::cout << "Decrypted text:" << decry << std::endl;

  system("pause");
  return 0;
}

The output of this code is as shown below;