Click here to Skip to main content
15,881,852 members
Articles / Security / Cryptography
Tip/Trick

RSA Key Pair via OpenSSL

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
19 Feb 2022CPOL1 min read 17.8K   398   9   8
A simple C++ program to generate RSA key pair
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:

C++
pair<EVP_PKEY*,EVP_PKEY*> GetKeyRSApair()
{
    auto bne = BN_new();         //refer to https://www.openssl.org/docs/man1.0.2/man3/bn.html
    auto ret = BN_set_word(bne, RSA_F4);

    int bits = 2048;
    RSA *r = RSA_new();
    RSA_generate_key_ex(r, bits, bne, NULL);  //here we generate the RSA keys

    //we use a memory BIO to store the keys
    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);   //once the data is written to a 
                                              //memory/file BIO, we get the size
    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);   //now we read the BIO into a buffer
    BIO_read(bp_public, pub_key, pub_len);

    pri_key[pri_len] = '\0';
    pub_key[pub_len] = '\0';

    //printf("\n%s\n:\n%s\n", pri_key, pub_key);fflush(stdout);  //now we print the keys 
    //to stdout (DO NOT PRINT private key in production code, this has to be a secret)

    BIO *pbkeybio = NULL;
    pbkeybio=BIO_new_mem_buf((void*) pub_key, pub_len);  //we create a buffer BIO 
                                     //(this is different from the memory BIO created earlier)
    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);  //now we read the 
                                                                   //BIO to get the RSA key
    p_rsa = PEM_read_bio_RSAPrivateKey(prkeybio, &p_rsa, NULL, NULL);

    EVP_PKEY *evp_pbkey = EVP_PKEY_new();  //we want EVP keys , openssl libraries 
                         //work best with this type, https://wiki.openssl.org/index.php/EVP
    EVP_PKEY_assign_RSA(evp_pbkey, pb_rsa);

    EVP_PKEY *evp_prkey = EVP_PKEY_new();
    EVP_PKEY_assign_RSA(evp_prkey, p_rsa);

    //clean up
    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.

C++
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

C++
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;

    /* Create and initialise the context */
    ctx = EVP_CIPHER_CTX_new();

    /* Initialise the envelope seal operation. This operation generates
     * a key for the provided cipher, and then encrypts that key a number
     * of times (one for each public key provided in the pub_key array). In
     * this example the array size is just one. This operation also
     * generates an IV and places it in iv. */
    EVP_SealInit(ctx, EVP_aes_256_cbc(), encrypted_key,encrypted_key_len, iv, pub_key, 1);

    int blocksize=EVP_CIPHER_CTX_block_size(ctx);
    /* Provide the message to be encrypted, and obtain the encrypted output.
     * EVP_SealUpdate can be called multiple times if necessary
     */
    vector<unsigned char> cyphered(plaintext_len+blocksize-1);//https://www.openssl.org/docs/man1.1.1/man3/EVP_EncryptInit.html  
    //The amount of data written depends on the block alignment of the encrypted data. For most ciphers and modes, the amount of data written can be anything from zero bytes to (inl + cipher_block_size - 1) bytes.

    len=cyphered.size();
    EVP_SealUpdate(ctx, &cyphered[0], &len, plaintext, plaintext_len);
    ciphertext_len = len;

    /* Finalise the encryption. Further ciphertext bytes may be written at
     * this stage.
     */
    EVP_SealFinal(ctx, &cyphered[0] + len, &len);
    ciphertext_len += len;

    /* Clean up */
    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;

    /* Create and initialise the context */
    ctx = EVP_CIPHER_CTX_new();

    /* Initialise the decryption operation. The asymmetric private key is
     * provided and priv_key, whilst the encrypted session key is held in
     * encrypted_key */
    EVP_OpenInit(ctx, EVP_aes_256_cbc(), encrypted_key,encrypted_key_len, iv, priv_key);

    vector<unsigned char> plaintext(ciphertext_len);
    /* Provide the message to be decrypted, and obtain the plaintext output.
     * EVP_OpenUpdate can be called multiple times if necessary
     */
    EVP_OpenUpdate(ctx, &plaintext[0], &len, ciphertext, ciphertext_len);
    plaintext_len = len;

    /* Finalise the decryption. Further plaintext bytes may be written at
     * this stage.
     */
    EVP_OpenFinal(ctx, &plaintext[0] + len, &len);
    plaintext_len += len;

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);
    plaintext.resize(plaintext_len);
    return plaintext;
}

Now to call the functions:

This is the best part:

C++
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)); 
                     //https://www.openssl.org/docs/man1.1.1/man3/EVP_SealInit.html
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer
India India
Hi,
I have been working with computers since my eight grade, programming the ZX Spectrum. I have always had an interest in assembly language and computer theory (and is still the reason for taking tons of online courses), actively code using C/C++ on Windows (using VS) and Linux (using QT).

I also provide training on data structures, algorithms, parallel patterns library , Graphics (DX11), GPGPUs (DX11-CS,AMP) and programming for performance on x86.
Feel free to call me at 0091-9823018914 (UTC +5:30)



(All views expressed here do not reflect the views of my employer).

Comments and Discussions

 
PraiseThank god Pin
Febri Bayu Nurcahyo5-Jun-23 20:01
Febri Bayu Nurcahyo5-Jun-23 20:01 
QuestionOpenSSL 3.0.0 Pin
Richard Kartas8-Mar-23 21:42
Richard Kartas8-Mar-23 21:42 
AnswerRe: OpenSSL 3.0.0 Pin
Asif Bahrainwala 20229-Mar-23 1:43
Asif Bahrainwala 20229-Mar-23 1:43 
GeneralRe: OpenSSL 3.0.0 Pin
Asif Bahrainwala9-Mar-23 7:42
Asif Bahrainwala9-Mar-23 7:42 
QuestionQuery the block size Pin
Asif Bahrainwala22-Mar-22 23:31
Asif Bahrainwala22-Mar-22 23:31 
SuggestionLess casting required Pin
Chad3F24-Feb-22 9:46
Chad3F24-Feb-22 9:46 
QuestionQt's openSSL Pin
E. Papulovskiy19-Feb-22 19:38
E. Papulovskiy19-Feb-22 19:38 
AnswerRe: Qt's openSSL Pin
Asif Bahrainwala20-Feb-22 4:42
Asif Bahrainwala20-Feb-22 4:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.