C++ program to use OpenSSL lib to generate RSA key pair and use them for encryption/decryption
(Tested on Linux Mint20)
Introduction
The attached code can be used to generate RSA keys pairs, this key pair is used to encrypt plain text. This sample is intended to help OpenSSL library users.
Background
RSA key pairs are used for asymmetric ciphering. Usually, we generate a symmetric cypher key and use AES256 to encrypt the data using this key. This cypher key is encrypted using RSA public key. This is a one way function (you cannot decrypt using the public key, you will need the private key to decrypt). We share the public key only with the peer to communicate securely.
Most of the content in this article is sourced from Google searches and StackOverflow.
Using the Code
As with my previous articles , the code must be referred to at all times. Also, I use Qtcreator
as my IDE and qmake.
The provided code is tested only on Linux Mint 20.
Let's generate our key-pair:
pair<EVP_PKEY*,EVP_PKEY*> GetKeyRSApair()
{
auto bne = BN_new(); auto ret = BN_set_word(bne, RSA_F4);
int bits = 2048;
RSA *r = RSA_new();
RSA_generate_key_ex(r, bits, bne, NULL);
BIO *bp_public = BIO_new(BIO_s_mem());PEM_write_bio_RSAPublicKey (bp_public, r);
BIO *bp_private = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
auto pri_len = BIO_pending(bp_private); auto pub_len = BIO_pending(bp_public);
char *pri_key = (char*) malloc(pri_len + 1);
char *pub_key = (char*) malloc(pub_len + 1);
BIO_read(bp_private, pri_key, pri_len); BIO_read(bp_public, pub_key, pub_len);
pri_key[pri_len] = '\0';
pub_key[pub_len] = '\0';
BIO *pbkeybio = NULL;
pbkeybio=BIO_new_mem_buf((void*) pub_key, pub_len); BIO *prkeybio = NULL;
prkeybio=BIO_new_mem_buf((void*) pri_key, pri_len);
RSA *pb_rsa = NULL;
RSA *p_rsa = NULL;
pb_rsa = PEM_read_bio_RSAPublicKey(pbkeybio, &pb_rsa, NULL, NULL); p_rsa = PEM_read_bio_RSAPrivateKey(prkeybio, &p_rsa, NULL, NULL);
EVP_PKEY *evp_pbkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(evp_pbkey, pb_rsa);
EVP_PKEY *evp_prkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(evp_prkey, p_rsa);
free(pri_key);free(pub_key);
BIO_free_all(bp_public);BIO_free_all(bp_private);
BIO_free(pbkeybio);BIO_free(prkeybio);
BN_free(bne);
RSA_free(r);
return {evp_pbkey,evp_prkey};
}
Remember to free the keys once you are done.
EVP_PKEY_free(keypair.first);
EVP_PKEY_free(keypair.second);
Let's encrypt/decrypt
This code comes straight from https://wiki.openssl.org/index.php/EVP_Asymmetric_Encryption_and_Decryption_of_an_Envelope
vector<unsigned char> envelope_seal
(EVP_PKEY **pub_key, unsigned char *plaintext, int plaintext_len,
unsigned char **encrypted_key, int *encrypted_key_len, unsigned char *iv)
{
EVP_CIPHER_CTX *ctx;
int ciphertext_len;
int len;
ctx = EVP_CIPHER_CTX_new();
EVP_SealInit(ctx, EVP_aes_256_cbc(), encrypted_key,encrypted_key_len, iv, pub_key, 1);
int blocksize=EVP_CIPHER_CTX_block_size(ctx);
vector<unsigned char> cyphered(plaintext_len+blocksize-1);
len=cyphered.size();
EVP_SealUpdate(ctx, &cyphered[0], &len, plaintext, plaintext_len);
ciphertext_len = len;
EVP_SealFinal(ctx, &cyphered[0] + len, &len);
ciphertext_len += len;
EVP_CIPHER_CTX_free(ctx);
cyphered.resize(ciphertext_len);
return cyphered;
}
vector<unsigned char> envelope_open(EVP_PKEY *priv_key, unsigned char *ciphertext,
int ciphertext_len,unsigned char *encrypted_key, int encrypted_key_len, unsigned char *iv)
{
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
ctx = EVP_CIPHER_CTX_new();
EVP_OpenInit(ctx, EVP_aes_256_cbc(), encrypted_key,encrypted_key_len, iv, priv_key);
vector<unsigned char> plaintext(ciphertext_len);
EVP_OpenUpdate(ctx, &plaintext[0], &len, ciphertext, ciphertext_len);
plaintext_len = len;
EVP_OpenFinal(ctx, &plaintext[0] + len, &len);
plaintext_len += len;
EVP_CIPHER_CTX_free(ctx);
plaintext.resize(plaintext_len);
return plaintext;
}
Now to call the functions:
This is the best part:
auto keypair=GetKeyRSApair();
unsigned char str[]=
"I am encrypted4332048230948-2308402934702384-2384092384-0234-20384-2384-2384-234";
unsigned char iv[EVP_MAX_IV_LENGTH]={};
unsigned char *encrypted_key=(unsigned char*)malloc(EVP_PKEY_size(keypair.first));
int encrypted_key_len=EVP_PKEY_size(keypair.first);
vector<unsigned char> cyphered=envelope_seal(&keypair.first,str,strlen((char*)str),
&encrypted_key,&encrypted_key_len,iv);
string cypheredString=GetHex(cyphered);
printf("%s\n",cypheredString.c_str());
vector<unsigned char> cypheredbinary=GetBinary(cypheredString);
vector<unsigned char> plaintext = envelope_open(keypair.second,&cypheredbinary[0],
cypheredbinary.size(),encrypted_key,encrypted_key_len,iv);
printf("orgin text:%s:End\n",str);
printf("plain text:");
for(char c:plaintext)
printf("%c",c);
printf(":End\n");
free(encrypted_key);
EVP_PKEY_free(keypair.first);EVP_PKEY_free(keypair.second);
History
- 19th February, 2022: Initial version