Skip to content

Instantly share code, notes, and snippets.

@irbull
Last active July 18, 2023 13:02
Show Gist options
  • Save irbull/c76a8c60e049a9fcba1116aa81771253 to your computer and use it in GitHub Desktop.
Save irbull/c76a8c60e049a9fcba1116aa81771253 to your computer and use it in GitHub Desktop.
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <string.h>
#include <iostream>
using namespace std;
void handleOpenSSLErrors(void)
{
ERR_print_errors_fp(stderr);
abort();
}
string decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
unsigned char *iv ) {
EVP_CIPHER_CTX *ctx;
unsigned char *plaintexts;
int len;
int plaintext_len;
unsigned char* plaintext = new unsigned char[ciphertext_len];
bzero(plaintext,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))
handleOpenSSLErrors();
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))
handleOpenSSLErrors();
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 */
EVP_CIPHER_CTX_free(ctx);
string ret = (char*)plaintext;
delete [] plaintext;
return ret;
}
void initAES(const string& pass, unsigned char* salt, unsigned char* key, unsigned char* iv )
{
bzero(key,sizeof(key));
bzero(iv,sizeof(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));
BIO_free_all(bio);
}
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];
ERR_load_crypto_strings();
Base64Decode(ciphertext_base64, &ciphertext, &cipher_len);
unsigned char key[32];
unsigned char iv[32];
if (strncmp((const char*)ciphertext,"Salted__",8) == 0) {
memcpy(salt,&ciphertext[8],8);
ciphertext += 16;
cipher_len -= 16;
}
initAES("12345", salt, key, iv);
string result = decrypt(ciphertext, cipher_len, key, iv);
cout << result << endl;
// Clean up
EVP_cleanup();
ERR_free_strings();
return 0;
}
@tahazayed
Copy link

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

@ajgale
Copy link

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
free(ciphertext_start);

@hmimyou
Copy link

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.

@IoanDascal
Copy link

I am trying to use this example in Visual Studio 2017 and I have error: identifier "bzero" is undefined

@chiraggirdhar95
Copy link

Can anyone help how to implement multi-threaded implementation of decryption as shown above in AES CTR mode?

@ivan-kovtun
Copy link

ivan-kovtun commented Jul 5, 2020

It is something wrong with initAES, when I use a password with only digits it works fine. But if it contains alphabetic symbols the key do not coincide with one I get in the openssl console.

openssl> openssl enc -aes-256-cbc -salt -S 5916B816382EF103 -md sha1 -P -k passwd
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
salt=5916B816382EF103
key=75CCC0851F84CD60CD45C57842280EB3B7A16F50D6868B4CDF38193CF545B947
iv =CFE8EF5C363C4C6B26773B7EE13D19B0

		TEST_METHOD(GetKeyForAlphabeticPwd)
		{
			aes256cbc* obj = new aes256cbc();
			unsigned char* salt = NULL;
			size_t salt_len = 0;
			unsigned char* key = new unsigned char[AES_KEY_SIZE + 1];
			unsigned char* iv = new unsigned char[AES_KEY_SIZE + 1];
			obj->Base64Decode("5916B816382EF103\n", &salt, &salt_len);
			obj->initAES(std::string("passwd"), salt, key, iv);
			unsigned char* etalon = NULL;
			size_t etalon_len = 0;
			obj->Base64Decode("75CCC0851F84CD60CD45C57842280EB3B7A16F50D6868B4CDF38193CF545B947\n", &etalon, &etalon_len);
			char* keyData = OPENSSL_buf2hexstr(key, AES_KEY_SIZE);  // can see hex in debuger
			int res = std::string((char*)key)
				.compare((char*)etalon);
			Assert::AreEqual(res, 0);
			delete[] key;
			delete[] iv;
			delete obj;

			OPENSSL_free(keyData);
		}

Can somebody help? :)

@faffner1
Copy link

I need help how to decrypt key from my wallet.dat,with cpp files,from bitcoin library,I have several wallet.dat,write PM please

@th30c0der
Copy link

I am trying to use this example in Visual Studio 2017 and I have error: identifier "bzero" is undefined

You can use memset(var , 0 , sizeof(var));

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