Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <string.h>
#include <iostream>
using namespace std;
void handleOpenSSLErrors(void)
string decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
unsigned char *iv ) {
unsigned char *plaintexts;
int len;
int plaintext_len;
unsigned char* plaintext = new unsigned char[ciphertext_len];
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleOpenSSLErrors();
/* 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_256_cbc(), NULL, key, iv))
EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH);
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
plaintext_len = len;
/* Finalise the decryption. Further plaintext bytes may be written at
* this stage.
if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleOpenSSLErrors();
plaintext_len += len;
/* Add the null terminator */
plaintext[plaintext_len] = 0;
/* Clean up */
string ret = (char*)plaintext;
delete [] plaintext;
return ret;
void initAES(const string& pass, unsigned char* salt, unsigned char* key, unsigned char* iv )
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, (unsigned char*)pass.c_str(), pass.length(), 1, key, iv);
size_t calcDecodeLength(char* b64input) {
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;
void Base64Decode( char* b64message, unsigned char** buffer, size_t* length) {
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));
int main (void)
// This is the string Hello, World! encrypted using aes-256-cbc with the
// pasword 12345
char* ciphertext_base64 = (char*) "U2FsdGVkX1/E/yWBwY9nW96pYIv2nouyJIFF9BtVaKA=\n";
int decryptedtext_len, ciphertext_len;
size_t cipher_len;
unsigned char* ciphertext;
unsigned char salt[8];
Base64Decode(ciphertext_base64, &ciphertext, &cipher_len);
unsigned char key[32];
unsigned char iv[32];
if (strncmp((const char*)ciphertext,"Salted__",8) == 0) {
ciphertext += 16;
cipher_len -= 16;
initAES("12345", salt, key, iv);
string result = decrypt(ciphertext, cipher_len, key, iv);
cout << result << endl;
// Clean up
return 0;

there is a memory leak in this line 83:
buffer = (unsigned char)malloc(decodeLen + 1);

ajgale commented Oct 25, 2017

In relation to resolving that memory leak mentioned in the above comment:

Because the pointer to the start of ciphertext is moved in line 112:
ciphertext += 16;
you should first get a pointer to the start of the buffer which you can free during cleanup(unless you like segfaults):
unsigned char *ciphertext_start = ciphertext;
...then do strncmp and increment as above...
// Clean up

hmimyou commented Jan 19, 2018

Be careful about line 70 as well. It bite me in the way that sometimes the bytes loaded from ifstream doesn't have \0 in the next position of the char *, and line 70 will get wrong len. This can randomly fail the decryption, with errors:

140735847007112:error:0607A082:digital envelope routines:EVP_CIPHER_CTX_set_key_length:invalid key length:evp_enc.c:595:
140735847007112:error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length:evp_enc.c:520:

The fix can be either explicitly pass the char * length to calcDecodeLength, or explicitly set \0 at the end of char * you pass in to calcDecodeLength.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment