OpenSSL AES 256 CBC encryption in C

To compile the following code you have to link OpenSSL libeay32.lib with your project. To build OpenSSL for Windows please read this post.

We are going to encrypt a message using AES 256 CBC algorithm and the result from the encryption will be encoded using base 64.

Includes and Definitions

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <stdbool.h>
 
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include <openssl/rand.h>
 
#define AES_ROUNDS		5
#define AES_KEY_LEN		32
 
bool GenerateKeys(const unsigned char *password, int plen, unsigned char *aesSalt, unsigned char *aesKey, unsigned char *aesIV);
int Encrypt(char **cipher, const char *plain, int plen, unsigned char *aesKey, unsigned char *aesIV);
int Decrypt(char **plain, const char *cipher, int clen, unsigned char *aesKey, unsigned char *aesIV);
 
int Base64Encode(char **dest, const char *src, unsigned int slen);
int Base64Decode(char **dest, const char *src);
unsigned int countDecodedLength(const char *encoded);

Encryption – Decryption

bool GenerateKeys(const unsigned char *password, int plen, unsigned char *aesSalt, unsigned char *aesKey, unsigned char *aesIV){
 
	if (RAND_bytes(aesSalt, 8) == 0) return false;
 
	aesSalt[PKCS5_SALT_LEN] = '\0';
 
	if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), aesSalt, password, plen, AES_ROUNDS, aesKey, aesIV)) return false;
 
	return true;
}
 
int Encrypt(char **cipher, const char *plain, int plen, unsigned char *aesKey, unsigned char *aesIV){
 
	EVP_CIPHER_CTX *ctx;
	unsigned char *cipher_tmp = { 0 };
	int len = 0, cipherTextLen = 0, retvalue = 0;
 
	if (!(ctx = EVP_CIPHER_CTX_new())) {
		return 0;
	}
 
	if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, aesKey, aesIV)) {
		if (ctx) EVP_CIPHER_CTX_free(ctx);
		return 0;
	}
 
	cipher_tmp = (unsigned char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, plen + 16);
	if (cipher_tmp == NULL) return 0;
 
	if (1 != EVP_EncryptUpdate(ctx, cipher_tmp, &len, plain, plen - 1)) {
		if (ctx) EVP_CIPHER_CTX_free(ctx);
		if (cipher_tmp) {
			HeapFree(GetProcessHeap(), 0, cipher_tmp);
			cipher_tmp = NULL;
		}
		return 0;
	}
 
	cipherTextLen = len;
 
	if (1 != EVP_EncryptFinal_ex(ctx, cipher_tmp + len, &len)) {
		if (ctx) EVP_CIPHER_CTX_free(ctx);
		if (cipher_tmp) {
			HeapFree(GetProcessHeap(), 0, cipher_tmp);
			cipher_tmp = NULL;
		}
		return 0;
	}
 
	cipherTextLen += len;
 
	if (ctx) EVP_CIPHER_CTX_free(ctx);
 
	retvalue = cipherTextLen;
 
	cipher_tmp[cipherTextLen] = '\0';
 
	if (cipherTextLen > 0)
		retvalue = Base64Encode(cipher, cipher_tmp, cipherTextLen + 1);
 
	if (cipher_tmp) {
		HeapFree(GetProcessHeap(), 0, cipher_tmp);
		cipher_tmp = NULL;
	}
 
	return retvalue;
}
 
int Decrypt(char **plain, const char *cipher, int clen, unsigned char *aesKey, unsigned char *aesIV){
 
	EVP_CIPHER_CTX *ctx;
	int len = 0, b64DecodedLen = 0, plainTextLen = 0;
	unsigned char *plain_tmp = { 0 };
 
	b64DecodedLen = Base64Decode(&plain_tmp, cipher);
	if (b64DecodedLen == 0) return 0;
 
	if (!(ctx = EVP_CIPHER_CTX_new())) {
		if (plain_tmp) {
			HeapFree(GetProcessHeap(), 0, plain_tmp);
			plain_tmp = NULL;
		}
		return 0;
	}
 
	if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, aesKey, aesIV)){
		if (ctx) EVP_CIPHER_CTX_free(ctx);
		if (plain_tmp) {
			HeapFree(GetProcessHeap(), 0, plain_tmp);
			plain_tmp = NULL;
		}
		return 0;
	}
 
	*plain = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, b64DecodedLen);
	if (*plain == NULL) return 0;
 
	if (1 != EVP_DecryptUpdate(ctx, *plain, &len, plain_tmp, b64DecodedLen - 1)){
		if (ctx) EVP_CIPHER_CTX_free(ctx);
		if (plain_tmp) {
			HeapFree(GetProcessHeap(), 0, plain_tmp);
			plain_tmp = NULL;
		}
		if (plain) {
			HeapFree(GetProcessHeap(), 0, plain);
			plain = NULL;
		}
		return 0;
	}
 
	if (plain_tmp) {
		HeapFree(GetProcessHeap(), 0, plain_tmp);
		plain_tmp = NULL;
	}
 
	plainTextLen = len;
 
	if (1 != EVP_DecryptFinal_ex(ctx, *plain + len, &len)){
		if (ctx) EVP_CIPHER_CTX_free(ctx);
		if (plain) {
			HeapFree(GetProcessHeap(), 0, plain);
			plain = NULL;
		}
		return 0;
	}
 
	plainTextLen += len;
	*(*plain + plainTextLen) = '\0';
 
	if (ctx) EVP_CIPHER_CTX_free(ctx);
 
	return plainTextLen;
}

Base 64 Encoding – Decoding

int Base64Encode(char **dest, const char *src, unsigned int slen){
 
	BIO *bio, *b64;
	BUF_MEM *bufferPtr;
	int numBytesEncoded = 0;
 
	b64 = BIO_new(BIO_f_base64());
	if (!b64) return 0;
 
	bio = BIO_new(BIO_s_mem());
	if (!bio) return 0;
 
	bio = BIO_push(b64, bio);
	if (!bio) return 0;
 
	BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
 
	if (BIO_write(bio, src, slen - 1) <= 0){
		if (bio) BIO_free_all(bio);
		return 0;
	}
 
	if (1 != BIO_flush(bio)) {
		if (bio) BIO_free_all(bio);
		return 0;
	}
 
	BIO_get_mem_ptr(bio, &bufferPtr);
	BIO_set_close(bio, BIO_NOCLOSE);
 
	if (bio) BIO_free_all(bio);
 
	*dest = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (*bufferPtr).length + 1);
	if (*dest == NULL) return false;
 
	(*bufferPtr).data[(*bufferPtr).length] = '\0';
	strncpy_s(*dest, (*bufferPtr).length + 1, (*bufferPtr).data, (*bufferPtr).length);
 
	numBytesEncoded = (*bufferPtr).length + 1;
 
	if (bufferPtr) {
		free(bufferPtr);
		bufferPtr = NULL;
	}
 
	return numBytesEncoded;
}
 
int Base64Decode(char **dest, const char *src){
 
	unsigned int dlen = 0;
	BIO *bio, *b64;
 
	unsigned int decode_length = countDecodedLength(src);
 
	*dest = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, decode_length + 1);
	if (*dest == NULL) return false;
 
	bio = BIO_new_mem_buf((char*)src, -1);
	if (!bio) return 0;
 
	b64 = BIO_new(BIO_f_base64());
	if (!b64) return 0;
 
	bio = BIO_push(b64, bio);
	if (!bio) return 0;
 
	BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
 
	dlen = BIO_read(bio, *dest, strlen(src));
 
	if (dlen != decode_length){
		if (dest){
			HeapFree(GetProcessHeap(), 0, dest);
			dest = NULL;
		}
		if (bio) BIO_free_all(bio);
		return false;
	}
 
	if (bio) BIO_free_all(bio);
 
	*(*dest + decode_length) = '\0';
 
	return decode_length + 1;
}
 
unsigned int countDecodedLength(const char *encoded) {
 
	unsigned int len = strlen(encoded), padding = 0;
 
	if (encoded[len - 1] == '=' && encoded[len - 2] == '=')
		padding = 2;
	else if (encoded[len - 1] == '=')
		padding = 1;
 
	return (len * 3) / 4 - padding;
}

Usage

int main(void){
	const unsigned char *password = "TEST_PASS_TEST_PASS_TEST_PASS_TEST_PASS\0";
	unsigned char aesSalt[PKCS5_SALT_LEN + 1] = { 0 };
	unsigned char aesKey[AES_KEY_LEN] = { 0 };
	unsigned char aesIV[EVP_MAX_IV_LENGTH] = { 0 };
 
	const char plain[] = "Hello, World!\0";
	int cipherTextLength = 0;
	char *ciphertext = { 0 };
	char *decryptedtext = { 0 };
 
	if (GenerateKeys(password, strlen(password) + 1, aesSalt, aesKey, aesIV)){
		cipherTextLength = Encrypt(&ciphertext, plain, strlen(plain) + 1, aesKey, aesIV);
		if (cipherTextLength > 0){
			printf("Encrypted text: %s\n\n", ciphertext);
 
			if (Decrypt(&decryptedtext, ciphertext, cipherTextLength, aesKey, aesIV) > 0){
				printf("Decrypted text: %s\n\n", decryptedtext);
 
				if (decryptedtext){
					HeapFree(GetProcessHeap(), 0, decryptedtext);
					decryptedtext = NULL;
				}
			}
 
			if (ciphertext){
				HeapFree(GetProcessHeap(), 0, ciphertext);
				ciphertext = NULL;
			}
		}
	}
 
	return EXIT_SUCCESS;
}
Categories: C Tags: , , ,
%d bloggers like this: