RSA algorithm in C

Nrupesh Surya · April 17, 2021

RSA algorithm is a widely used encryption tool. The algorithm was invented by Ron Rivest, Adi Shamir and Leonard Adleman in 1977 and is still in production. RSA is extensively used in various data-sensitive applications such as HTTPS protocol(SSL uses the RSA algorithm), e-Banking, and Bluetooth among many more core applications.

The RSA algorithm is an asymmetric algorithm which needs two keys for communication. Suppose A wants to send a message to B. For this, A must first have the public key of B which can be shared through a public channel on the internet without any problems. The reasoning behind it is, the message which is encrypted by the public key of B, can only be decrypted by using the private key of B which is assumed to be under their private possession. To summarise, A encrypts the message they want to send using the public key of B and sends it to B who decrypts it using their private key.

Generating private and public keys

To proceed to the program, we must first acquire public and private keys. This is done using the OpenSSL command-line utility.

The private key can be generated by the following command. Here 2048 refers to the length of the private key.

openssl genrsa -out private.pem 2048

The corresponding public key for the above-mentioned private key can be generated in the following manner:

openssl rsa -in private.pem -outform PEM -pubout -out public.pem

Libraries in C

You’ll need the following libraries before proceeding further. You must also install the OpenSSL API for C before going ahead

sudo apt-get install libssl-dev

Importing the required libraries in C:

#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/rsa.h>

Other standard C libraries which we’ll be using:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

Ingesting the public/private

We must ingest the public/private key and create an RSA object to proceed with the encryption/decryption step

RSA * ingestPrivateKey(FILE *fp)
{
    if(fp == NULL)
    {
        printf("Unable to open private key file \n");
        return NULL;    
    }
    RSA *rsa= RSA_new() ;
 
    rsa = PEM_read_RSAPrivateKey(fp, &rsa,NULL, NULL); 
    //PEM_read_RSAPublicKey for public key
 
    return rsa;
}

Encryption/Decryption step

After making the RSA object we can use this object to encrypt/decrypt according to our requirement.

void decryption(RSA* rsa,unsigned char decrypted[10000], int padding, char *file_contents, int lSize)
{
    int padding = RSA_PKCS1_PADDING;    
    //randomized padding which improves strength
    int decrypted_length = RSA_private_decrypt((int)lSize,file_contents,decrypted,rsa,padding); 
    //lSize is the size of the file to be decrypted
    //file_contents is the file contents in a string
    //RSA_public_encrypt is the symmetric function for encryption
    if(decrypted_length == -1)
    {
        printf("Private Decryption failed. Please run again\n");
        exit(0);
    }
    printf("Decrypted length is %d\n",decrypted_length);
    printf("Decrypted data is : \n %s\n,decrypted);
}

Sample program for Encryption

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/rsa.h>

int padding = RSA_PKCS1_PADDING;

RSA* ingestPublicKey(FILE *fp)
{
 
    if(fp == NULL)
    {
        printf("Unable to open public key file \n");
        return NULL;    
    }
    RSA* rsa= RSA_new() ;
 
    rsa = PEM_read_RSA_PUBKEY(fp, &rsa,NULL, NULL);
    
    return rsa;
}
 
int main(int argc, char* argv[])
{
    if(argc<4)
    {
        printf("Not enough arguements");
        exit(0);
    }

    FILE* filename = fopen(argv[1],"rb");
    long lSize;
    fseek( filename , 0L , SEEK_END);
    lSize = ftell( filename );
    rewind( filename );
    char *file_contents;
    file_contents = (char*)calloc(1,lSize+1);
    fread(file_contents,lSize,1,filename);

    unsigned char  encrypted[10000]={};

    FILE* public = fopen(argv[2],"rb");
    RSA* rsa = ingestPublicKey(public);  

    int encrypted_length = RSA_public_encrypt((int)strlen(file_contents),file_contents,encrypted,rsa,padding);

    if(encrypted_length == -1)
    {
        printf("Public Encryption failed");
        exit(0);
    }
    printf("Encrypted length is %d\n",encrypted_length);

    FILE* out_filename = fopen(argv[3],"wb");
    fwrite(encrypted, encrypted_length, 1, out_filename);
    // fprintf(out_filename,"%s", encrypted);
    fclose(out_filename);
    fclose(public);
    fclose(filename);
    free(file_contents);
    return 0; 
}

Sample program for decryption

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/rsa.h>
 
int padding = RSA_PKCS1_PADDING;

RSA * ingestPrivateKey(FILE *fp)
{
    if(fp == NULL)
    {
        printf("Unable to open private key file \n");
        return NULL;    
    }
    RSA *rsa= RSA_new() ;
 
    rsa = PEM_read_RSAPrivateKey(fp, &rsa,NULL, NULL);
 
    return rsa;
}
 
int main(int argc, char* argv[])
{

    if(argc<4)
    {
        printf("Not enough arguements");
        exit(0);
    }

    FILE* filename = fopen(argv[1],"rb");
    long lSize;
    fseek( filename , 0L , SEEK_END);
    lSize = ftell( filename );
    rewind( filename );
    unsigned char *file_contents;
    file_contents = (unsigned char*)malloc(sizeof(char)*lSize);
    fread(file_contents,lSize,1,filename);

    unsigned char decrypted[10000]={};

    FILE* private = fopen(argv[2],"rb");
    RSA * rsa = ingestPrivateKey(private);   

    int decrypted_length = RSA_private_decrypt((int)lSize,file_contents,decrypted,rsa,padding);

    if(decrypted_length == -1)
    {
        printf("Private Decryption failed. Please run again\n");
        exit(0);
    }
    printf("Decrypted length is %d\n",decrypted_length);

    FILE* out_filename = fopen(argv[3],"wb");
    fprintf(out_filename,"%s", decrypted);
    
    return 0; 
}

Makefile

We need to pass in the command line arguements for input file, public key and private key. This has been handled in the Makefile for a specific example. You can modify to suit your needs

all: encrypt decrypt input.txt private.pem public.pem 
	./encrypt input.txt public.pem encrypt.bin
	./decrypt encrypt.bin private.pem decrypt.txt

encrypt: encrypt.c 
	gcc encrypt.c -o encrypt -lcrypto -lssl

decrypt: decrypt.c 
	gcc decrypt.c -o decrypt -lcrypto -lssl

clean:
	rm -rf encrypt decrypt decrypt.txt encrypt.bin

Running the files

make encrypt #for generating the encrypt binary file
make decrypt #for generating the decrypt binary file
make #for running the file with the above-mentioned testcase 

Footnotes

Thanks for making it to this point. You can check out the full repository here. Feel free to contact me for any suggestions/improvements.

Twitter, Facebook