Aes encryption compatible with openssl cmd
-
Hi
I need AES 128 ecb encryption (Android and iOS), no salt, I know it is unsafe and so one...
I'm loosing my hair because of this!
I tested two methods one with openssl lib and one with Qt-AES
Both of them encrypt and decrypt text inside their own routines, problem is that encypted and base64 encoded text cannot be decrypted by openssl command from Linux, example which works with openssl cmd:enc_test$ echo "letmein"|openssl enc -aes-128-ecb -nosalt -a -k myPassword -p -pbkdf2 -md sha1 key=580D5BAA02014A40B0E30C6E82B60EA5 iZ5IdMK9pkAg0HRF38mHNQ== enc_test$ echo "iZ5IdMK9pkAg0HRF38mHNQ=="|openssl enc -d -aes-128-ecb -nosalt -a -k myPassword -p -pbkdf2 -md sha1 key=580D5BAA02014A40B0E30C6E82B60EA5 letmein enc_test$
If I ommit -pbkdf2 then there will be warning, but it still works, I can also use -iter 1 or undocumented flag -md md5 or -md sha1
Below there is sample code with two examples of openssl (openssl2 is using EVP_BytesToKey to get key from password), openssl is using key directly and for testAes You need to download Qt-AES from link above.
Result of these testing these three in one go:BEGIN openssl2 EncryptMgr::openssl2 ciphertext: "f602bdec9c84c0d3476ebf6115588131" base: "9gK97JyEwNNHbr9hFViBMQ==" EncryptMgr::openssl2 decryptText: "letmein" END openssl2 BEGIN openssl EncryptMgr::openssl str_cipher: "X3CMhIjuQhCJdzDzif6Bfw==" second method: "X3CMhIjuQhCJdzDzif6Bfw==" len: 16 EncryptMgr::openssl Decrypted text is: "letmein" END openssl BEGIN testAes BackEnd::testAes input: "letmein" encoded: "JLl+vimqiwbi5eRKJ3OlPg==" decoded: "letmein" END testAes
Below there is encrypt-mgr.h and encrypt-mgr.cpp file with all these methods, can someone shed some light? I need to encode text and send it to other system which will decode it
Best
Marekencrypt-mgr.h
#include <QtCore> #include <QCryptographicHash> #include "Qt-AES/qaesencryption.h" #include <openssl/conf.h> #include <openssl/evp.h> #include <openssl/err.h> #include <openssl/aes.h> #include <string.h> class EncryptMgr: public QObject { Q_OBJECT public: EncryptMgr(QObject *parent); public slots: void openssl(); void openssl2(); void testAes(); signals: private slots: int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *ciphertext); int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *plaintext); void handleErrors(void); int aes_init(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx,EVP_CIPHER_CTX *d_ctx); unsigned char *aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len); unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len); private: };
encrypt-mgr.cpp
#include "encrypt.h" EncryptMgr::EncryptMgr(QObject *parent):QObject(parent) { } void EncryptMgr::openssl2() { qDebug()<<"BEGIN openssl2"; EVP_CIPHER_CTX* en = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX* de = EVP_CIPHER_CTX_new(); unsigned char key_data[] = "myPassword"; int key_data_len = static_cast<int>(strlen(reinterpret_cast<char *>(key_data))); // gen key and iv. init the cipher ctx object if (aes_init(key_data, key_data_len,NULL, en, de)) { printf("Couldn't initialize AES cipher\n"); exit(1); } QString arg("letmein"); int len = arg.toLocal8Bit().length()+1; auto plainText = reinterpret_cast<unsigned char*>(const_cast<char*>(static_cast<const char*>(arg.toLatin1()))); unsigned char* cipherText = aes_encrypt(en,plainText,&len); QByteArray ba(reinterpret_cast<char*>(cipherText),len); printf("ciphertext = 0x%s\n",static_cast<const char*>(ba.toHex())); qDebug()<<"EncryptMgr::openssl2 ciphertext:"<<ba.toHex()<<" base:"<<ba.toBase64(); int len2 = ba.length(); const char* cipherText2 = ba; unsigned char* decryptText=aes_decrypt(de,reinterpret_cast<unsigned char*>(const_cast<char*>(cipherText2)),&len2); QString decryptStr=QString::fromUtf8((char*)decryptText); qDebug()<<"EncryptMgr::openssl2 decryptText:"<<decryptStr; qDebug()<<"END openssl2"; } int EncryptMgr::aes_init(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx,EVP_CIPHER_CTX *d_ctx) { int i, nrounds = 5; unsigned char key[32]; i = EVP_BytesToKey(EVP_aes_128_ecb(), EVP_sha1(), NULL, key_data, key_data_len, nrounds, key, NULL); if (i != 16) { printf("Key size is %d bits - should be 256 bits\n", i); exit(-1); } EVP_CIPHER_CTX_init(e_ctx); EVP_EncryptInit_ex(e_ctx, EVP_aes_128_ecb(), nullptr, key, NULL); EVP_CIPHER_CTX_init(d_ctx); EVP_DecryptInit_ex(d_ctx, EVP_aes_128_ecb(), nullptr, key, NULL); return 0; } unsigned char* EncryptMgr::aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len) { int c_len = *len + AES_BLOCK_SIZE, f_len = 0; unsigned char *ciphertext = reinterpret_cast<unsigned char*>(malloc(static_cast<size_t>(c_len))); EVP_EncryptInit_ex(e, nullptr, nullptr, nullptr, nullptr); EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, *len); EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len); *len = c_len + f_len; return ciphertext; } unsigned char* EncryptMgr::aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len) { int p_len = *len, f_len = 0; unsigned char *plaintext = reinterpret_cast<unsigned char*>(malloc(static_cast<size_t>(p_len + AES_BLOCK_SIZE))); EVP_DecryptInit_ex(e, nullptr, nullptr, nullptr, nullptr); EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len); EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len); *len = p_len + f_len; return plaintext; } void EncryptMgr::openssl() { qDebug()<<"BEGIN openssl"; /* A 128 bit key */ unsigned char *key = (unsigned char *)"580D5BAA02014A40B0E30C6E82B60EA5"; /* Message to be encrypted */ unsigned char *plaintext = (unsigned char *)"letmein"; /* Buffer for ciphertext. Ensure the buffer is long enough for the * ciphertext which may be longer than the plaintext, dependant on the * algorithm and mode */ unsigned char ciphertext[128]; /* Buffer for the decrypted text */ unsigned char decryptedtext[128]; int decryptedtext_len, ciphertext_len; /* Initialise the library */ ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); OPENSSL_config(NULL); /* Encrypt the plaintext */ ciphertext_len = this->encrypt (plaintext, strlen ((char *)plaintext), key, ciphertext); /* Do something useful with the ciphertext here */ printf("Ciphertext is:\n"); // QByteArray str_cipher = QByteArray((char*)ciphertext); QByteArray arr; for(int i=0;i<ciphertext_len;i++) { arr[i]=ciphertext[i]; } QByteArray str_cipher=QByteArray::fromRawData(reinterpret_cast<char*>(ciphertext), ciphertext_len); qDebug()<<"EncryptMgr::openssl str_cipher:"<<str_cipher.toBase64()<<" second method:"<<arr.toBase64()<<" len:"<<ciphertext_len; BIO_dump_fp (stdout, (const char *)ciphertext, ciphertext_len); /* Decrypt the ciphertext */ decryptedtext_len = decrypt(ciphertext, ciphertext_len, key, decryptedtext); /* Add a NULL terminator. Expecting printable text */ decryptedtext[decryptedtext_len] = '\0'; /* Show the decrypted text */ printf("Decrypted text is:\n"); printf("%s\n", decryptedtext); QString str_decrypt = QString::fromUtf8((char*)decryptedtext); qDebug()<<"EncryptMgr::openssl Decrypted text is:"<<str_decrypt; /* Clean up */ EVP_cleanup(); ERR_free_strings(); qDebug()<<"END openssl"; } void EncryptMgr::handleErrors(void) { ERR_print_errors_fp(stderr); abort(); } int EncryptMgr::encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *ciphertext) { EVP_CIPHER_CTX *ctx; int len; int ciphertext_len; /* Create and initialise the context */ if(!(ctx = EVP_CIPHER_CTX_new())) this->handleErrors(); /* Initialise the encryption operation. IMPORTANT - ensure you use a key * In this example we are using 128 bit AES (i.e. a 128 bit key). */ if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key,NULL)) this->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, plaintext, plaintext_len)) this->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 EncryptMgr::decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *plaintext) { EVP_CIPHER_CTX *ctx; int len; int plaintext_len; /* Create and initialise the context */ if(!(ctx = EVP_CIPHER_CTX_new())) this->handleErrors(); /* Initialise the decryption operation. IMPORTANT - ensure you use a key * In this example we are using 128 bit AES (i.e. a 128 bit key). The */ if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key,NULL)) this->handleErrors(); /* 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)) this->handleErrors(); plaintext_len = len; /* Finalise the decryption. Further plaintext bytes may be written at * this stage. */ if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors(); plaintext_len += len; /* Clean up */ EVP_CIPHER_CTX_free(ctx); return plaintext_len; } void EncryptMgr::testAes() { qDebug()<<"BEGIN testAes"; QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::ECB); QString inputStr("letmein"); QString key("580D5BAA02014A40B0E30C6E82B60EA5"); QByteArray encodeText = encryption.encode(inputStr.toLocal8Bit(), key.toLocal8Bit()); QByteArray decodeText = encryption.decode(encodeText, key.toLocal8Bit()); QString decodedString = QString(encryption.removePadding(decodeText)); qDebug()<<"BackEnd::testAes input:"<<inputStr<<" encoded:"<<encodeText.toBase64()<<" decoded:"<<decodedString; //<<" hashIV:"<<hashIV; qDebug()<<"END testAes"; }
-
Hi,
@Marek said in Aes encryption compatible with openssl cmd:
QString arg("letmein");
int len = arg.toLocal8Bit().length()+1;
auto plainText = reinterpret_cast<unsigned char*>(const_cast<char*>(static_cast<const char*>(arg.toLatin1())));Why are you using two different encoding ?
plainText does not point what you think. The toLatin1 method returns a QByteArray, not a char pointer.
-
@SGaist Hi
I took this example from forum https://forum.qt.io/topic/96587/qt-with-openssl-aes-256-cbc-encryption/4when I change this line to:
unsigned char* plainText=reinterpret_cast<unsigned char *>(arg.toLocal8Bit().data()); OR unsigned char plainText[]="letmein";
Output is the same as previous
BEGIN openssl2 EncryptMgr::openssl2 ciphertext: "f602bdec9c84c0d3476ebf6115588131" base: "9gK97JyEwNNHbr9hFViBMQ==" EncryptMgr::openssl2 decryptText: "letmein" END openssl2
Here I'm testing all known (to me) combination of openssl enc cmd
enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 IAa9TSIdLCIU7XRDiHemxg== enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 Uxj0FgyEY3Bc1XDaaoIsTw== enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md md5 fdDcCauSOiGYGNmUPnFaLg== enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md md5 wa5TR/Vbe3spT7RhVKxX3Q== enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md sha1 HFL8j/EPy/Q5NGoy5Ujkcw== enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1 iwY4XIcGLATKRyyP+n377w== enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 9Mjk39/gvgO42P3nuVzKHg== enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md md5 J5bUApllT5MCB2hAukm9Zw== enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 1mt3AKZ9fpLCgLIlPIOkHA== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 Bd6B3CUO0nn6thX+Cs3nWw== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 jl4vRGtTxItse8xHf4vygQ== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md md5 tCFPTf5AkS34DOkPcPK6aQ== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md md5 I0r5J3ZnlctvH9bjphaDQg== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 1 -md sha1 tWpHRO2gyhkIj04fx8f51A== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1 eWHrPFxtOcbN3eVy2eUzOA== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 zkcvwZ+6q8NEmS4my2TFJQ== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md md5 tZzIRZ7crQ38LDLYxyz3tA== enc_test$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 iZ5IdMK9pkAg0HRF38mHNQ== enc_test$
complete madness ;)
Best,
Marek -
@Marek said in Aes encryption compatible with openssl cmd:
arg.toLocal8Bit().data()
You've working on a temporary. Basic c++ error.
-
@Marek said in Aes encryption compatible with openssl cmd:
unsigned char* plainText=reinterpret_cast<unsigned char *>(arg.toLocal8Bit().data());
You are taking the data pointer of a temporary QByteArray which lifetime ends after the semi colon therefore pointing to somewhere that may or may not contain the data.
You have to ensure that the lifetime of your objects.
As already written numerous time:
QString someString("Foo bar"); QByteArray latin1Array = someString.toLatin1(); // Now you can use your pointer thingy based on latin1Array.data()
-
@SGaist @Christian-Ehrlicher thanks for your help
but no difference
QString arg("letmein"); QByteArray latin1Array = arg.toLatin1(); unsigned char* plainText=reinterpret_cast<unsigned char *>(latin1Array.data());
Result is the same:
BEGIN openssl2 EncryptMgr::openssl2 ciphertext: "f602bdec9c84c0d3476ebf6115588131" base: "9gK97JyEwNNHbr9hFViBMQ==" EncryptMgr::openssl2 decryptText: "letmein" END openssl2
And I even tested just hardcoding string letmein as unsinged char (same result)
unsigned char plainText[]="letmein"
But maybe there is some error with reading cipher text from unsigned char to QByteArray ?
Best
Marek -
There is so much code which is not needed so I don't understand what's really going on. For such a simple encryption not more than 10 lines should be needed. Please provide a minimal compilable example.
And are you sure the\0
is added on command line? I would guess no. -
@Christian-Ehrlicher
There are three types of encrypt/decrypt atempts in this code thats why it looks big,
I can't shrink it unless I remove one of methods-
openssl2 function is using:
aes_init - to init openssl and EVP_BytesToKey to create key from password
aes_encrypt - to encypt
aes_decrypt - to decrypt
openssl2 is initializing aes, performs encrypt and decrypt and prints the result -
openssl function is using:
encrypt - to encrypt
decrypt - to decrypt
handleErrors - doesn't matter
This one is different in terms of key handling, does not derive key from password, key is already provided, taken from openssl command
openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 -p
- This is just using https://github.com/bricke/Qt-AES
all in single procedure
Best,
Marek -
-
As I said - your code isn't very readable. Please provide one example, no classes, nothing around so we can actually compile it here.
-
@Christian-Ehrlicher ok just a minute
best,
Marek -
As I understand this should give the same result as openssl command since it is using key taken from openssl command
$ echo "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 -p key=580D5BAA02014A40B0E30C6E82B60EA5 iZ5IdMK9pkAg0HRF38mHNQ== $ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -pbkdf2 -md sha1 -p key=580D5BAA02014A40B0E30C6E82B60EA5 1mt3AKZ9fpLCgLIlPIOkHA==
void EncryptMgr::opensslShort() { qDebug()<<"BEGIN openssl"; /* A 128 bit key */ unsigned char *key = (unsigned char *)"580D5BAA02014A40B0E30C6E82B60EA5"; /* Message to be encrypted */ unsigned char *plaintext = (unsigned char *)"letmein"; /* Buffer for ciphertext. Ensure the buffer is long enough for the * ciphertext which may be longer than the plaintext, dependant on the * algorithm and mode */ unsigned char ciphertext[128]; int ciphertext_len; int len; int plaintext_len=strlen ((char *)plaintext); EVP_CIPHER_CTX *ctx; /* Initialise the library */ ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); OPENSSL_config(NULL); /* Encrypt the plaintext */ if(!(ctx = EVP_CIPHER_CTX_new())) qDebug()<<"some errors"; /* Initialise the encryption operation. IMPORTANT - ensure you use a key * In this example we are using 128 bit AES (i.e. a 128 bit key). */ if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key,NULL)) qDebug()<<"some errors"; /* 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, plaintext, plaintext_len)) qDebug()<<"some errors"; 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; /* Do something useful with the ciphertext here */ QByteArray arr; for(int i=0;i<ciphertext_len;i++) { arr[i]=ciphertext[i]; } QByteArray str_cipher=QByteArray::fromRawData(reinterpret_cast<char*>(ciphertext), ciphertext_len); qDebug()<<"EncryptMgr::openssl str_cipher:"<<str_cipher.toBase64()<<" second method:"<<arr.toBase64()<<" len:"<<ciphertext_len; EVP_CIPHER_CTX_free(ctx); /* Clean up */ EVP_cleanup(); ERR_free_strings(); }
Best,
Marek -
This is a minimal example which also prints out the same as the command line:
auto ciphertext = reinterpret_cast<const unsigned char*>("letmein"); const int c_len = strlen(reinterpret_cast<const char*>(ciphertext)); int f_len1 = 0; int f_len2 = 0; auto keyIn = reinterpret_cast<const unsigned char*>("myPassword"); int k_len = strlen(reinterpret_cast<const char*>(keyIn)); unsigned char keyOut1[16]; PKCS5_PBKDF2_HMAC_SHA1("myPassword", k_len, nullptr, 0, 5, sizeof(keyOut1), keyOut1); EVP_CIPHER_CTX* e = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(e); EVP_EncryptInit_ex(e, EVP_aes_128_ecb(), nullptr, keyOut1, nullptr); std::vector<unsigned char> dataOut(c_len + EVP_CIPHER_CTX_block_size(e)); EVP_EncryptUpdate(e, dataOut.data(), &f_len1, ciphertext, c_len); EVP_EncryptFinal_ex(e, dataOut.data() + f_len1, &f_len2); EVP_CIPHER_CTX_free(e); qDebug() << QByteArray::fromRawData(reinterpret_cast<char*>(dataOut.data()), f_len1 + f_len2).toBase64();
--> "iwY4XIcGLATKRyyP+n377w=="
enc_test$ echo -en "letmein" |openssl enc -aes-128-ecb -nosalt -k 'myPassword' -a -iter 5 -md sha1 iwY4XIcGLATKRyyP+n377w==
-
@Christian-Ehrlicher Thanks man, this works for me ;)
When I find some time I try to find bug in my code.Best Regards,
Marek