C program on encrypting, encoding, decoding and decryption of Password using Openssl library

Theory on base64 encoding and decoding

  • Base64 encoding converts three octets into four encoded characters.

  • 24 bits encoded to 32 bits


  • 3 bytes encoded to 4 bytes
  • 24 bits encoded to 32 bits

  • 16 (3 x 5 + 1) bytes encoded to ( 4 x 5 + (1 + 3extra bytes)) = 24 bytes 

  • The final '==' sequence indicates that the last group contained only one byte, and '=' indicates that it contained two bytes.

Example

neelkanth_surekha#./base64 "neelkanth reddy 12"
Input string: neelkanth reddy 12
encoded Output (base64): bmVlbGthbnRoIHJlZGR5IDEy length: 24       (no equal to padding)
Decoded Output: neelkanth reddy 12 string length: 18

neelkanth_surekha#./base64 "neelkanth reddy 123"
Input string: neelkanth reddy 123
encoded Output (base64): bmVlbGthbnRoIHJlZGR5IDEyMw== length: 28   (1 byte i.e. "=" padding)
Decoded Output: neelkanth reddy 123 string length: 19

neelkanth_surekha#./base64 "neelkanth reddy 1234"
Input string: neelkanth reddy 1234
encoded Output (base64): bmVlbGthbnRoIHJlZGR5IDEyMzQ= length: 28   (2 bytes i.e "==" padding)
Decoded Output: neelkanth reddy 1234 string length: 20


Program:

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <string.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <stdint.h>
#include <assert.h>


void handleErrors(void)
{
    ERR_print_errors_fp(stderr);
    abort();
}

size_t calcDecodeLength(const char* b64input) { //Calculates the length of a decoded string
    size_t len = strlen(b64input),
           padding = 0;

    if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
        padding = 2;
    else if (b64input[len-1] == '=') //last char is =
        padding = 1;

    return (len*3)/4 - padding;
}

int Base64Decode(char* b64message, unsigned char** buffer, size_t* length) { //Decodes a base64 encoded string
    BIO *bio, *b64;

    int decodeLen = calcDecodeLength(b64message);
    *buffer = (unsigned char*)malloc(decodeLen + 1);
    (*buffer)[decodeLen] = '\0';

    bio = BIO_new_mem_buf(b64message, -1);
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_push(b64, bio);

    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
    *length = BIO_read(bio, *buffer, strlen(b64message));
    assert(*length == decodeLen); //length should equal decodeLen, else something went horribly wrong
    BIO_free_all(bio);

    return (0); //success
}

int Base64Encode(const unsigned char* buffer, size_t length, char** b64text) { //Encodes a binary safe base 64 string
BIO *bio, *b64;
BUF_MEM *bufferPtr;

b64 = BIO_new(BIO_f_base64());
bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);

BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
BIO_write(bio, buffer, length);
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bufferPtr);
BIO_set_close(bio, BIO_NOCLOSE);
BIO_free_all(bio);

*b64text=(*bufferPtr).data;
        printf("%s\n", (*bufferPtr).data);

return (0); //success
}

int decrypt(unsigned char* base64DecodeOutput, unsigned char *key,
            unsigned char *iv, unsigned char *plain_pwd_text, size_t length)
{
    EVP_CIPHER_CTX *ctx;
    int len;

    int plain_pwd_text_len;

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

    /* Initialise the decryption operation. IMPORTANT - ensure you use a key
     * and IV size appropriate for your cipher
     * In this example we are using 256 bit AES (i.e. a 256 bit key). The
     * IV size for *most* modes is the same as the block size. For AES this
     * is 128 bits */
    if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
        handleErrors();

    /* Provide the message to be decrypted, and obtain the plain_pwd_text output.
     * EVP_DecryptUpdate can be called multiple times if necessary
     */
    if(1 != EVP_DecryptUpdate(ctx, plain_pwd_text, &len, base64DecodeOutput, length))
        handleErrors();
    plain_pwd_text_len = len;
 


    /* Finalise the decryption. Further plain_pwd_text bytes may be written at
     * this stage.
     */
    if(1 != EVP_DecryptFinal_ex(ctx, plain_pwd_text + len, &len)) handleErrors();
    plain_pwd_text_len += len;

    plain_pwd_text[plain_pwd_text_len] = '\0';
    /* Show the decrypted text */
    printf("Decrypted text (original password) is: %s string length: %d\n",
            plain_pwd_text, plain_pwd_text_len);

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);

    return plain_pwd_text_len;
}

int encrypt(unsigned char *plain_pwd_text, int plain_pwd_text_len, unsigned char *key,
        unsigned char *iv, unsigned char *encryp_pwd_txt)
{
    EVP_CIPHER_CTX *ctx;
    int len;
    int encryp_pwd_txt_len;

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

    /* Initialise the encryption operation. IMPORTANT - ensure you use a key
     * and IV size appropriate for your cipher
     * In this example we are using 128 bit AES (i.e. a 128 bit key). The
     * IV size for *most* modes is the same as the block size. For AES this
     * is 128 bits */
    if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
        handleErrors();

    /* Provide the message to be encrypted, and obtain the encrypted output.
     * EVP_EncryptUpdate can be called multiple times if necessary
     */
    if(1 != EVP_EncryptUpdate(ctx, encryp_pwd_txt, &len, plain_pwd_text, plain_pwd_text_len))
        handleErrors();
    encryp_pwd_txt_len = len;

    /* Finalise the encryption. Further encryp_pwd_txt bytes may be written at
     * this stage.
     */

    if(1 != EVP_EncryptFinal_ex(ctx, encryp_pwd_txt + len, &len)) handleErrors();
    encryp_pwd_txt_len += len;

    encryp_pwd_txt[encryp_pwd_txt_len] = '\0';

    printf("encryp_pwd_txt is: %s Length: encryp_pwd_txt_len: %d \n" , encryp_pwd_txt, encryp_pwd_txt_len);

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);

    return encryp_pwd_txt_len;
}

int main (int argc, char *argv[])
{
    /* A 128 bit = 16 bytes (max) private key  */
    unsigned char key[16] = "12345678";
    /* A 128 bit IV */
    unsigned char iv[16] = {0};
    /* Buffer for encryp_pwd_txt. Ensure the buffer is long enough for the
     * encryp_pwd_txt which may be longer than the plain_pwd_text, dependant on the
     * algorithm and mode
     */
    unsigned char encryp_pwd_txt[128] = {'\0'};
    /* Message to be encrypted */
    unsigned char plain_pwd_text[32] =  {0};
    /* Buffer for the decrypted text */
    unsigned char decryptedtext[128] = {'\0'};
    int decryptedtext_len, encryp_pwd_txt_len;

    /* Command line argument : enter password */
    if (argc != 2) {
        printf("Enter Only 1 argument:  Enter input (password)\n");
        exit(1);
    }

    /* password length cannot exceed 32 bytes */
    if (strlen(argv[1]) > 32)
    {
        printf("Password length cannot exceed beyond 32 bytes\n");
        exit(1);
    }

    /* copy command line arguement into plain text */
    strncpy(plain_pwd_text, argv[1], sizeof(plain_pwd_text)-1);

    char base64EncodeOutput[100] = {'\0'};
    char *base64EncodeOutput_ptr;
    base64EncodeOutput_ptr = base64EncodeOutput;

    unsigned char base64DecodeOutput[100] = {'\0'};
    unsigned char* base64DecodeOutput_ptr;
    base64DecodeOutput_ptr = base64DecodeOutput;

    printf("************************************************************************\n");
    printf("Original Password: %s string length: %ld\n", plain_pwd_text, strlen(plain_pwd_text));   
    printf("************************************************************************\n");

    /* Encrypt the plain_pwd_text */
    printf("************************************************************************\n");
    encryp_pwd_txt_len = encrypt (plain_pwd_text, strlen ((char *)plain_pwd_text),
                                  key, iv,  encryp_pwd_txt);
    printf("************************************************************************\n");

    /* Encode the encrypted text */
    printf("************************************************************************\n");
    Base64Encode(encryp_pwd_txt, encryp_pwd_txt_len, &base64EncodeOutput_ptr);
    printf("Encode Output (base64): %s  string length: %ld\n",
                  base64EncodeOutput_ptr, strlen(base64EncodeOutput_ptr));
    printf("************************************************************************\n");

    /* Decode the encrypted text */
    size_t length;
    Base64Decode(base64EncodeOutput_ptr, &base64DecodeOutput_ptr, &length);
    printf("************************************************************************\n");
    printf("Decode Output: %s string length: %ld\n", base64DecodeOutput_ptr,
                                                  strlen(base64DecodeOutput_ptr));
    printf("************************************************************************\n");

    /* Decrypt the encrypted text back into plain text */
    printf("************************************************************************\n");
    decryptedtext_len = decrypt(base64DecodeOutput_ptr, key, iv,  decryptedtext, length);
    printf("************************************************************************\n");

    return 0;
}


Output

neelkanth_surekha#./a.out 12345
************************************************************************
Original Password: 12345 string length: 5
************************************************************************
************************************************************************
encryp_pwd_txt is: ����˱ꛎ-��� �� Length: encryp_pwd_txt_len: 16
************************************************************************
************************************************************************
rfKc28ux6puOLeGx5BGg3A==
Encode Output (base64): rfKc28ux6puOLeGx5BGg3A==  string length: 24
************************************************************************
************************************************************************
Decode Output: ����˱ꛎ-��� �� string length: 16
************************************************************************
************************************************************************
Decrypted text (original password) is: 12345 string length: 5
************************************************************************

neelkanth_surekha#./a.out 12345fdsafsadfsdfsafsaf
************************************************************************
Original Password: 12345fdsafsadfsdfsafsaf string length: 23
************************************************************************
************************************************************************
encryp_pwd_txt is: ���t]陴p� %d w@}����������f }:K Length: encryp_pwd_txt_len: 32
************************************************************************
************************************************************************
sbmddF3pmbRwiRUlZBJ3QH3Bp7OBn4uS6tPYZhR9Oks=
Encode Output (base64): sbmddF3pmbRwiRUlZBJ3QH3Bp7OBn4uS6tPYZhR9Oks=  string length: 44
************************************************************************
************************************************************************
Decode Output: ���t]陴p� %d w@}����������f }:K string length: 32
************************************************************************
************************************************************************
Decrypted text (original password) is: 12345fdsafsadfsdfsafsaf string length: 23
************************************************************************

neelkanth_surekha#./a.out 12345fdsafsadfsdfsafsafasfsdfsadfasd
Password length cannot exceed beyond 32 bytes