How to encrypt a message to Blowfish using OpenSSL? - c++

I need to get the Blowfish encryption with OpenSSL library. But something does not work.
What am I doing wrong? I'm trying to do it this way:
#include <iostream>
#include <openssl/blowfish.h>
#include "OpenSSL_Base64.h"
#include "Base64.h"
using namespace std;
int main()
{
unsigned char ciphertext[BF_BLOCK];
unsigned char plaintext[BF_BLOCK];
// blowfish key
const unsigned char *key = (const unsigned char*)"topsecret";
//unsigned char key_data[10] = "topsecret";
BF_KEY bfKey;
BF_set_key(&bfKey, 10, key);
/* Open SSL's Blowfish ECB encrypt/decrypt function only handles 8 bytes of data */
char a_str[] = "8 Bytes";//{8, ,B,y,t,e,s,\0}
char *arr_ptr = &a_str[0];
//unsigned char* data_to_encrypt = (unsigned char*)"8 Bytes"; // 7 + \0
BF_ecb_encrypt((unsigned char*)arr_ptr, ciphertext, &bfKey, BF_ENCRYPT);
unsigned char* ret = new unsigned char[BF_BLOCK + 1];
strcpy((char*)ret, (char*)ciphertext);
ret[BF_BLOCK + 1] = '\0';
char* base_enc = OpenSSL_Base64::Base64Encode((char*)ret, strlen((char*)ret));
cout << base_enc << endl;
cin.get();
return 0;
}
But I get the wrong output:
fy7maf+FhmbM
I checked with it:
http://sladex.org/blowfish.js/
It should be: fEcC5/EKDVY=
Base64:
http://pastebin.com/wNLZQxQT

The problem is that ret may contain a null byte, encryption is 8-bit byte based, not character based and will contain values fromthe full range 0-255. strlen will terminate on the first null byte it finds giving a length that is smaller then the full length of the encrypted data.
Note: When using encryption pay strice attention to providing the exact correct length parameters and data, do not rely on padding. (The exception is input data to encryption functions that support data padding such as PKCS#7 (née PKCS#5) padding.

Related

C++ OpenSSL API : How to compute CLI default IV from PBKDF2 derivation

I'm trying to implement AES decryption into one of my C++ program. The idea would be to use the following openSSL command line to generate the ciphered text (but to use the C++ API to decipher) :
openssl enc -aes-256-cbc -in plaintext.txt -base64 -md sha512 -pbkdf2 -pass pass:<passwd>
As the official doc is a bit too complicated I based my implementation on this tutorial to implement the decryption : https://eclipsesource.com/blogs/2017/01/17/tutorial-aes-encryption-and-decryption-with-openssl/
It does works well, but uses a deprecated key-derivation algorithm which I wanna replace with PBKDF2.
As far as I understand I should then use PKCS5_PBKDF2_HMAC() rather than the EVP_BytesToKey() suggested in the tutorial. My problem is that EVP_BytesToKey was able to derivate both key and IV from salt and password, where PKCS5_PBKDF2_HMAC only seems to derivate one at a time.
I couldn't find any more information/tutorial on how to get both key and IV, and tried several implementations, but couldn't find how the openSSL CLI generates the IV.
I'd really like to avoid to write the IV in either the CLI or the payload, the implementation of the tutorial was really convenient for that.
Could someone help me ?
Thanks, best regards
I realize the question is about a month old by now but I came across it in my search of information on doing something similar. Given the lack of answers here I went to the source for answers.
TL;DR (direct answer)
PKCS5_PBKDF2_HMAC() generates both key and IV at the same time. Although it's concatenated to one string. It's up you to split the string into the needed parts.
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair);
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);
Detailed description
Before going into specifics I feel that I should mention that I'm using C and not C++. I do however hope that the information provided is helpful even for C++.
Before anything else the string needs to be decoded from base64 in the application. After that we can move along to the key and IV generation.
The openssl tool indicates that a salt is being used by starting the encrypted string with the string 'Salted__' followed by 8 bytes of salt (at least for aes-256-cbc). In addition to the salt we also need to know the length of both the key and the IV. Luckily there are API calls for this.
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
We also need to know the number of iterations (the default in openssl 1.1.1 when using -pbkdf2 is 10000), as well as the message digest function which in this case will be EVP_sha512() (as specified by option -md sha512).
When we have all of the above it's time to call PKCS5_PBKDF2_HMAC().
PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair);
Short info on the arguments
pass is of type (const char *)
password length (int), if set to -1 the length will be determined by strlen(pass)
salt is of type (const unsigned char *)
salt length (int)
iteration count (int)
message digest (const EVP_MD *), in this case returned by EVP_sha512()
total length of key + iv (int)
keyivpair (unsigned char *), this is where the key and IV is stored
Now we need to split the key and IV apart and store them i separate variables.
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);
And now we have a key and IV which can be used to decrypt data encrypted by the openssl tool.
PoC
To further clarify I wrote the following proof of concept (written on and for Linux).
/*
* PoC written by zoke
* Compiled with gcc decrypt-poc.c -o decrypt-poc -lcrypto -ggdb3 -Wall -Wextra
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
void bail() {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
if(argc < 3)
bail();
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned char salt[8]; // openssl tool uses 8 bytes for salt
unsigned char decodeddata[256];
unsigned char ciphertext[256];
unsigned char plaintext[256];
const char *pass = argv[1]; // use first argument as password (PoC only)
unsigned char *encodeddata = (unsigned char *)argv[2]; // use second argument
int decodeddata_len, ciphertext_len, plaintext_len, len;
// Decode base64 string provided as second option
EVP_ENCODE_CTX *ctx;
if(!(ctx = EVP_ENCODE_CTX_new()))
bail();
EVP_DecodeInit(ctx);
EVP_DecodeUpdate(ctx, decodeddata, &len, encodeddata, strlen((const char*)encodeddata));
decodeddata_len = len;
if(!EVP_DecodeFinal(ctx, decodeddata, &len))
bail();
EVP_ENCODE_CTX_free(ctx);
// openssl tool format seems to be 'Salted__' + salt + encrypted data
// take it apart
memcpy(salt, decodeddata + 8, 8); // 8 bytes starting at 8th byte
memcpy(ciphertext, decodeddata + 16, decodeddata_len - 16); // all but the 16 first bytes
ciphertext_len = decodeddata_len - 16;
// Get some needed information
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
int iter = 10000; // default in openssl 1.1.1
unsigned char keyivpair[iklen + ivlen];
// Generate the actual key IV pair
if(!PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair))
bail();
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);
// Decrypt data
EVP_CIPHER_CTX *cipherctx;
if(!(cipherctx = EVP_CIPHER_CTX_new()))
bail();
if(!EVP_DecryptInit_ex(cipherctx, cipher, NULL, key, iv))
bail();
if(!EVP_DecryptUpdate(cipherctx, plaintext, &len, ciphertext, ciphertext_len))
bail();
plaintext_len = len;
if(!EVP_DecryptFinal_ex(cipherctx, plaintext + len, &len))
bail();
plaintext_len += len;
EVP_CIPHER_CTX_free(cipherctx);
plaintext[plaintext_len] = '\0'; // add null termination
printf("%s", plaintext);
exit(EXIT_SUCCESS);
}
Application tested by running
$ openssl aes-256-cbc -e -a -md sha512 -pbkdf2 -pass pass:test321 <<< "Some secret data"
U2FsdGVkX19ZNjDQXX/aACg7d4OopxqvpjclkaSuybeAxOhVRIONXoCmCQaG/Vg9
$ ./decrypt-poc test321 U2FsdGVkX19ZNjDQXX/aACg7d4OopxqvpjclkaSuybeAxOhVRIONXoCmCQaG/Vg9
Some secret data
The Key/IV generation used by the command line tool is in apps/enc.c and was very helpful when figuring this out.

Stack level too deep error in ruby native extension, how to reduce stack level?

I have a Ruby on Rails api which handles a simple API call and returns some encrypted data. The encryption is done in C++, using the ruby native C api. (reference here).
The native part works fine when compiled and linked as a standalone program, and also when used with ruby in IRB.
However, when I use it from within the Rails API, I sometimes get a "Stack level too deep" error.
The error seems to occur or not depending on the size of the data processed.
According to this answer, the stack 'level' is actually stack space, so it would make sense that if I have more data to process, then I have more data in the stack, so it fills up quicker etc...
I had initially left all my variables in the stack, for simplicity and to avoid forgetting to free allocated memory. Seeing this error, I switched to a dynamical allocation approach. However contrarily to what I was expecting, the Stack level too deep error occurs for even smaller data size.
data_controller.rb
def load_data data_path, width
authorize!
encrypted = NativeDataProtector.encrypt(data_path, get_key(), get_iv())
return [ encrypted, "application/octet-stream" ]
end
native_encryptor.cpp
VALUE encrypt_n(VALUE _self, VALUE data_path, VALUE key, VALUE salt){
DataProtector protector;
string *b64 = protector.encrypt(StringValueCStr(data_path), \
StringValueCStr(key), \
StringValueCStr(salt));
VALUE ret = rb_str_new(b64->c_str(), b64->length());
delete(b64);
return ret;
}
extern "C" void Init_data_protector() {
VALUE mod = rb_define_module("NativeDataProtector");
rb_define_module_function(mod, "encrypt", (VALUE(*)(ANYARGS))encrypt_n, 3);
}
encrypt.h
#include <ruby.h>
#include "extconf.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <list>
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
class DataProtector {
private :
int pad_cleartext(vector<unsigned char> *cleartext);
vector<unsigned char> *read_data(string path);
int aes_encrypt(vector<unsigned char> *plaintext, string key,
string iv, unsigned char *ciphertext);
string to_b64(unsigned char* in);
void handleErrors(void);
public :
string *encrypt(string data_path, string key, string salt);
};
encrypt.cpp
string *DataProtector::encrypt(string data_path, string key, string salt) {
vector<unsigned char> *cleartext = readData(data_path);
int length = pad_cleartext(cleartext);
unsigned char* output = new unsigned char[length + 16];
int ciphertext_len;
// encrypt
string *encrypted = new string("");
ciphertext_len = aes_encrypt(&((*cleartext), key, iv, output);
(*encrypted) += to_b64(output);
delete(cleartext);
delete(output);
return encrypted;
}
int DataProtector::aes_encrypt(vector<unsigned char> *plaintext, string key,
string iv, unsigned char *ciphertext)
{
EVP_CIPHER_CTX *ctx;
int len;
int ciphertext_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 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_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, (const unsigned char *)key.c_str(), (const unsigned char *)iv.c_str()))
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, ciphertext, &len, reinterpret_cast<unsigned char*>(plaintext->data()), plaintext->size()))
handleErrors();
ciphertext_len = len;
/* Finalise the encryption. Further ciphertext bytes may be written at
* this stage.
*/
if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
ciphertext_len += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
int DataProtector::pad_cleartext(vector<unsigned char> *in) {
// padds to length multiple of 16
int nb_blocks = in->size() / 16 + ((in->size()%16 == 0)? 1:1);
int size = nb_blocks*16;
for (unsigned int i=in->size(); i<size; i++) {
unsigned char c = '0';
in->push_back(c);
}
return size;
}
vector<unsigned char> *DataProtector::read_data(string path) {
streampos size;
ifstream file(path, ios::binary);
file.seekg(0, ios::end);
size = file.tellg();
file.seekg(0, ios::beg);
vector<unsigned char> *data = new vector<unsigned char>(fileSize);
file.read((char*) &data[0], size);
return data;
}
void DataProtector::handleErrors(void) {
ERR_print_errors_fp(stderr);
abort();
}
(the actual encryption is from here)
The error stack trace I get :
SystemStackError (stack level too deep):
app/controllers/data_controller.rb:41:in `encrypt'
app/controllers/data_controller.rb:41:in `load_data'
app/controllers/data_controller.rb:15:in `show'
Is Believe that the reason for this error is too much data allocated on the stack, and not a recursion issue. However, I don't understand why switching to heap allocation did not improve anything.
I can imagine 2 solutions :
cutting up the data in ruby and calling the native method several times with less data.
increasing the ruby stack size.
However both these solutions are unideal for my project, for performance/resource issues.
Is there any other way I can reduce the usage of the stack by my program ?

How to get human readable std::string from RSA_private_encrypt

I have RSA encrypted my string but it's now a unsigned char *. How do I create a human readable std::string that I can output for the user? I want to use it in an amazon signed url. Here are the meat and potatoes of the code from GitHub
unsigned char* RSA_SHA1_Sign(std::string policy, RSA *privateKey) throw(std::runtime_error)
{
//sha1 digest the data
unsigned char hash[SHA_DIGEST_LENGTH] = {'0'};
SHA1((const unsigned char *)policy.c_str(), policy.length(), hash);
// Sign the data
int rsaSize = RSA_size(privateKey);
// std::unique_ptr<unsigned char[]> signedData(new unsigned char[size]);//if c++11 available
unsigned char *signedData = (unsigned char *)malloc(sizeof(unsigned char) * rsaSize);
unsigned int signedSize = 0;
//use RSA_sign instead of RSA_private_encrypt
if(!RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, signedData, &signedSize, privateKey)){
throw std::runtime_error("Failed to sign");
}
return signedData;
}
std::string base64Encode(unsigned char *signedData)
{
//prepare
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bmem = BIO_new(BIO_s_mem());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
b64 = BIO_push(b64, bmem);
//write
BIO_write(b64, signedData, 256);
BIO_flush(b64);
//create string
BUF_MEM *bptr;
BIO_get_mem_ptr(b64, &bptr);
std::string base64String(bptr->data);
BIO_free_all(b64);
return base64String;
}
int main(int argc, const char * argv[]) {
RSA *privateKey = createRSAFromPrivateKeyFile("/path/to/privatekey");
std::string sourceString = "testing";
std::string signature = RSA_SHA1_Sign(sourceString, privateKey);
std::string encodedSignature = base64Encode(signature);
std::cout << "RESULT: " << encodedSignature << std::endl;
return 0;
}
UPDATE: I was using the wrong sign function. Once updated, using base64 encode gave me the correct string.
RSA_PKCS1_PADDING
PKCS #1 v1.5 padding. This function does not handle the algorithmIdentifier specified in PKCS #1.
When generating or verifying PKCS #1 signatures, RSA_sign(3) and RSA_verify(3) should be used.
To save all the data, use this std::string constructor: std::string( char *data, int size ). The size will be useful as the output MIGHT contain a null character.
To send it to amazon over an url, consider using the base64 encoding, again, as the encrypted data might contain NULLs and other shenanigans.
Firstly, to get it into an std::string object, which will probably be helpful in general:
std::string s{private_key, size};
However, to then make that compatible with Amazon's scheme you'll need to pick out (or write your own) Base64 library and URL encoder to escape special URL chars. A cursory search of Google or StackOverflow will provide you with what you need in this respect and it's beyond the scope of this question to write out how to do Base64 encoding and URL escaping in C++.
Also, since you're using C++, consider std::unique_ptr<unsigned char[]> rather than straight-up malloc();
std::unique_ptr<unsigned char[]> signedData{new unsigned char[size]};

How to use salsa20 (or ChaCha)?

Assume that we have a large file which can be read in chunks of 1024 bytes or so, how can I encrypt and decrypt each chunk using Salsa or Chacha 20?
Also, where would I specify the number of rounds (i.e. 8, 12, or 20)?
So far, I haven't been able to figure it out by looking at the eSTREAM test package :(
I've downloaded the following files via eSTREAM and the Salsa20 homepage:
chacha.c
ecrypt-config.h
ecrypt-machine.h
ecrypt-portable.h
ecrypt-sync.h
And I see the comments in encrypt-sync.h talk about calling functions in this order:
ECRYPT_keysetup();
ECRYPT_ivsetup();
ECRYPT_encrypt_bytes();
But I have absolutely no idea exactly what I'm supposed to be supplying as parameters to make this work...
Here's my best attempt so far, starting with one small string of plaintext (my C is rusty... it's possible I've made some basic mistake, though I can't see it):
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ecrypt-sync.h"
#define CHUNKSIZE 1024
void getRandomBytes(u8 **str, u32 len);
void showData(u8 *data, u8 *header);
int main(int argc, char** argv)
{
char plaintext[CHUNKSIZE] = "THIS IS A TEST";
ECRYPT_ctx ctx;
u8 *key, *IV, *ciphertext, *result;
/*
Don't use random values till we get it working with zeroes at least
getRandomBytes(&key, ECRYPT_MAXKEYSIZE/8);
getRandomBytes(&IV, ECRYPT_MAXIVSIZE/8);
*/
key = (u8 *)calloc((size_t)ECRYPT_MAXKEYSIZE/8, sizeof(u8));
IV = (u8 *)calloc((size_t)ECRYPT_MAXIVSIZE/8, sizeof(u8));
printf("Encrypting [%s] using random %d bit key and %d bit IV:\n", plaintext, ECRYPT_MAXKEYSIZE, ECRYPT_MAXIVSIZE);
ECRYPT_init();
ECRYPT_keysetup(&ctx, key, ECRYPT_MAXKEYSIZE, ECRYPT_MAXIVSIZE);
ECRYPT_ivsetup(&ctx, IV);
ciphertext = (u8 *)calloc((size_t)CHUNKSIZE, sizeof(u8));
ECRYPT_encrypt_bytes(&ctx, plaintext, ciphertext, CHUNKSIZE);
//showData(ciphertext, "CIPHERTEXT");
result = (u8 *)calloc((size_t)CHUNKSIZE, sizeof(u8));
ECRYPT_decrypt_bytes(&ctx, ciphertext, result, CHUNKSIZE);
printf("And now decrypting back: [%s]\n", result);
return 0;
}
void showData(u8 *data, u8 *header) {
printf("\n-----BEGIN %s-----\n%s\n-----END %s-----\n", header, data, header);
}
void getRandomBytes(u8 **str, u32 len) {
int fd = open("/dev/random", O_RDONLY);
char *ptr = malloc((size_t) + 1);
read(fd, ptr, len);
close(fd);
ptr[len] = '\0';
*str = ptr;
}
Results are like:
Encrypting [THIS IS A TEST] using random 256 bit key and 64 bit IV:
And now decrypting back: [(bunch of random characters)]
Where it should be:
And now decrypting back: [THIS IS A TEST]
Feel free to provide your solution in either C or C++
Thank you!
If you are going to use Salsa20 in real code and you are asking questions like this, you probably want to use the NaCl library with nice friendly C++ wrappers.
See The NaCl website.
To answer your actual question: you need to set the IV up again for the decryption operation. The IV consists of your nonce and a block offset. The encrypt/decrypt functions increment the offset, giving your code a different IV for the encryption and decryption functions.

My AES encryption/decryption functions don't work with random ivecs

I was bored and wrote a wrapper around openSSL to do AES encryption with less work. If I do it like this:
http://pastebin.com/V1eqz4jp (ivec = 0)
Everything works fine, but the default ivec is all 0's, which has some security problems. Since I'm passing the data back as a string anyway, I figured, why not generate a random ivec and stick it to the front, the take it back off when I decrypt the string? For some reason it doesn't work though.
Well actually, it almost works. It seems to decrypt the middle of the string, but not the beginning or end:
String is: 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
Encrypting..
���l%%1u���B!
�����`pN)�ɶ���[l�ӏ��{�Q�?�2�/�HԵ�y"�=Z�Cu����l%%1u���B!
Decrypting..
String is: �%���G*�5J�0��0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
I honestly have no idea what's going wrong. Maybe some stupid mistake, or maybe I'm missing something about AES?
Here's the code: (Edited to incorporate Steve Jessop's solution to my first problem)
/*!
* Simple AES
* Brendan Long
* March 29, 2010
*
* Simplified encryption and decryption using OpenSSL's AES library.
* Remember to compile with -lcrypto and link against the library
* g++ (your stuff) -lcrypto simpleAes.cpp (or simpleAes.o)
*
* Implementation note: Using the default ivec (0) is not secure. For
* the full security that AES offers, use a different
* ivec each time (it does not need to be secret,
* just different.
*
* This code is released into the public domain. Yada yada..
* Read this for details: http://creativecommons.org/licenses/publicdomain/
*
* If for some reason public domain isn't good enough, you may use, alter,
* distribute or do anything else you want with this code with no restrictions.
*/
#include <openssl/aes.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
bool seed = true;
/*!
* Encrypts a string using AES with a 256 bit key
* Note: If the key is less than 32 bytes, it will be null padded.
* If the key is greater than 32 bytes, it will be truncated
* \param in The string to encrypt
* \param key The key to encrypt with
* \return The encrypted data
*/
std::string aes_encrypt(std::string in, std::string key){
// Seed the random number generator once
if(seed){
srand( (unsigned int) time(NULL));
seed = false;
}
// Generate a random ivec
unsigned char ivec[16];
for(int i=0; i<16; i++){
ivec[i] = (unsigned char) rand();
}
// Round up to AES_BLOCK_SIZE
size_t textLength = ((in.length() / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
// Always pad the key to 32 bits.. because we can
if(key.length() < 32){
key.append(32 - key.length(), '\0');
}
// Get some space ready for the output
unsigned char *output = new unsigned char[textLength];
// Generate a key
AES_KEY *aesKey = new AES_KEY;
AES_set_encrypt_key((unsigned char*)key.c_str(), 256, aesKey);
// Encrypt the data
AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length() + 1, aesKey, ivec, AES_ENCRYPT);
// Make the data into a string
std::string ret((char*) output, textLength);
// Add the ivec to the front
ret = std::string((char*)ivec, 16) + ret;
// Clean up
delete output;
delete aesKey;
return ret;
}
/*!
* Decrypts a string using AES with a 256 bit key
* Note: If the key is less than 32 bytes, it will be null padded.
* If the key is greater than 32 bytes, it will be truncated
* \param in The string to decrypt
* \param key The key to decrypt with
* \return The decrypted data
*/
std::string aes_decrypt(std::string in, std::string key){
// Get the ivec from the front
unsigned char ivec[16];
for(int i=0;i<16; i++){
ivec[i] = in[i];
}
in = in.substr(16);
// Always pad the key to 32 bits.. because we can
if(key.length() < 32){
key.append(32 - key.length(), '\0');
}
// Create some space for output
unsigned char *output = new unsigned char[in.length()];
// Generate a key
AES_KEY *aesKey = new AES_KEY;
AES_set_decrypt_key((unsigned char*)key.c_str(), 256, aesKey); // key length is in bits, so 32 * 8 = 256
// Decrypt the data
AES_cbc_encrypt((unsigned char*)in.c_str(), output, in.length(), aesKey, ivec, AES_DECRYPT);
// Make the output into a string
std::string ret((char*) output);
// Clean up
delete output;
delete aesKey;
return ret;
}
You should save the ivec[16] into 'output' BEFORE encrypting.
That's it...
I'd also like to add that it'll be much simpler to work with char* instead of string.
This line is wrong:
std::string ret((char*) output);
The decrypted data doesn't have a nul terminator, since you encrypted in.length() bytes. That accounts for the garbage at the end, but not the garbage at the beginning. There may be other problems as well.
A friend of mine figured out the problem. I'm doing this:
Generate random number and store it in ivec
Encrypt data with ivec
Append ivec to beginning of output data
The problem is that step 2 changes the contents of ivec. I was basically storing random numbers at the beginning of my string. The solution was to add this:
unsigned char ivec[16];
// set ivec to random numbers
std::string ivecString((char*) ivec, 16);
// encrypt data
return ivecString + encryptedData;
In general, you cannot treat the output of the encryption stage as a string, unless you perform an additional step, such as Base 64 encoding the output. Any output byte could be a nul.