-
-
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; | |
} |
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);
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
.
I am trying to use this example in Visual Studio 2017 and I have error: identifier "bzero" is undefined
Can anyone help how to implement multi-threaded implementation of decryption as shown above in AES CTR mode?
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? :)
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
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));
there is a memory leak in this line 83:
buffer = (unsigned char)malloc(decodeLen + 1);