Decrypt AES with OpenSSL & Qt 5.5.1 Win32 VS2013



  • Hello!

    Did hit a dead end trying to decode a AES encoded string using Openssl 1.0.1s and Qt 5.5.1 Win32 Visual Studio 2013. Since it works on the commandline with openssl.exe it has to be my code or implementation. I'm using Qt Creator 3.5.1.

    After compiling Openssl (shared libs + debug) i just added

    win32: LIBS += -L$$PWD/openssl/lib/ -llibeay32
    INCLUDEPATH += $$PWD/openssl/include
    DEPENDPATH += $$PWD/openssl/include
    

    to my .pro file, run qmake and did build all and run the app. But so far only crashes without any error.

    Here is my code:

    #include <openssl/conf.h>
    #include <openssl/evp.h>
    #include <openssl/err.h>
    
    // password: test
    // cypertext: Dr. Test
    // works: PpUr+LMHvaKmf0q6J7Oyzo4jbFO5kfWyXl0d8nD3hyM= | openssl enc -d -a -A -aes-128-cbc -iv 507055722b4c4d4876614b6d66307136-K 098f6bcd4621d373cade4e832627b4f6
    
    QString cypher = "PpUr+LMHvaKmf0q6J7Oyzo4jbFO5kfWyXl0d8nD3hyM=";
    QString pw_md5 = "098f6bcd4621d373cade4e832627b4f6";
    QString iv16 = "507055722b4c4d4876614b6d66307136";
    
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    OPENSSL_config(NULL);
    
    unsigned char *ciphertext = (unsigned char*) cypher.data();
    int ciphertext_len = static_cast< int >(cypher.size());
    unsigned char *key = (unsigned char*) pw_md5.data();
    unsigned char *iv = (unsigned char*) iv16.data();
    unsigned char *plaintext = (unsigned char*) "";
    
    qDebug() << "ciphertext: " << ciphertext;
    qDebug() << "ciphertext_len: " << ciphertext_len;
    
    EVP_CIPHER_CTX *ctx;
    
    int len;
    int plaintext_len;
    
    if(!(ctx = EVP_CIPHER_CTX_new()))
    {
    	ERR_print_errors_fp(stderr);
    }
    
    if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
    {
    	ERR_print_errors_fp(stderr);
    }
    
    if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
    {
    	ERR_print_errors_fp(stderr);
    }
    plaintext_len = len;
    
    if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
    {
    	ERR_print_errors_fp(stderr);
    }
    plaintext_len += len;
    
    EVP_CIPHER_CTX_free(ctx);
    EVP_cleanup();
    ERR_free_strings();
    
    qDebug() << "plaintext_len: " << plaintext_len;
    qDebug() << "plaintext: " << plaintext;
    

    if i set

    int ciphertext_len = 0;
    

    i get at least a final block length error (after i quit the app) and the app won't crash. If i set it to 44 manually, the app still crashes.

    Any ideas?

    Thanks!



  • Hi, don't know about the OpenSSL block lengths problem, but the reason you're crashing I think is because you're allocating memory badly, for example
    unsigned char *plaintext = (unsigned char*) ""; is almost always guaranteed to crash.
    Why not skip the QStrings, do something simpler like this:

    ...
    unsigned char cypher[] = "PpUr+LMHvaKmf0q6J7Oyzo4jbFO5kfWyXl0d8nD3hyM=";
    unsigned char pw_md5[] = "098f6bcd4621d373cade4e832627b4f6";
    unsigned char iv16[] = "d8e8fca2dc0f896fd7cb4cb0031ba249";
    
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    OPENSSL_config(NULL);
    
    unsigned char *ciphertext = cypher;
    int ciphertext_len = sizeof(cypher) - 1;
    unsigned char *key = pw_md5;
    unsigned char *iv = iv16;
    unsigned char plaintext[1024];
    ...
    

    Now the app shouldn't crash, so you can try debugging the OpenSSL decrypting stuff...



  • Thanks to you, no more crashing. :D

    Tank you!

    Still getting wrong last black error. But at least i can try to fix that somehow. I was guessing some Qt users may use Openssl and got experience using it with Qt.


  • Lifetime Qt Champion

    Hi,

    Depending on your needs the QCA module might be something to look into.



  • @SGaist Thanks, but it is no option for me. Did load qca-2.1.0 and also did try to compile it on Windows VS2013 and cmake. No chance i will get this running without a step my step tutorial for Win. I did try to compile it on Ubuntu, no problem, works instantly, Windows, nah. I will look into this module again if i ever reach a level called ultra skilled hacker.

    Also tryed Crypto++ and was able to get some code running, but the sample codes do not work for me and the docs are not really helpful.


  • Lifetime Qt Champion

    To avoid the need to level-up that high: open the QCA project in Qt Creator and build it with the Kit you are using for your project, then install it and you should be done. IIRC, you can even choose the install target from the list in the Project panel.



  • Thank you!

    But no luck, it won't compile. 5 errors.

    C2061: syntax error : identifier 'QIODevice'
    C2065: 'QIODevice' : undeclared identifier
    C2065: 'file' : undeclared identifier
    C2448: 'QCA::Hash::update' : function-style initializer appears to be a function definition
    C2061: syntax error : identifier 'QIODevice'
    

  • Lifetime Qt Champion

    Where did you get QCA from ?



  • @SGaist Thank you for asking. I got it from http://delta.affinix.com/qca/ (qca-2.1.0.tar.gz).

    Edit: Did find a newer version @ https://www.freshports.org/devel/qca-qt5/ (qca-2.1.1.tar.xz) and i was able to compile it. At least the debug files did compile. But the big problem is

    if(!QCA::isSupported("aes128"))
    {
         qDebug() << "No AES 128 supported ...";
    }
    if(!QCA::isSupported("aes128-cbc"))
    {
         qDebug() << "No AES 128  CBC supported ...";
    }
    if(!QCA::isSupported("aes128-cbc-pkcs7"))
    {
         qDebug() << "No AES 128 CBS pkcs7 supported ...";
    }
    

    According to the docs http://delta.affinix.com/docs/qca/ my version does not support AES CBC at all so it is worthless to me atm. Did i something wrong here?

    win32: LIBS += -L$$PWD/qca/lib/ -lqca-qt5d
    INCLUDEPATH += $$PWD/qca/include
    DEPENDPATH += $$PWD/qca/include
    

    and

    #include <QtCrypto/QtCrypto>
    

    Thanks!


  • Lifetime Qt Champion

    CBS ? Don't you mean CBC ? If CBC, then yes it does, just take a look at QCA's code.

    Before calling these function did you initialize QCA ?

    Also, do you have OpenSSL in your PATH environment variable when running the application ?



  • Yes, CBC, sorry.

    I did put a QCA::Initializer init; after ui->setupUi(this); And yes, Openssl is in my paths env (C:\OpenSSL-Win32\bin), i can call openssl.exe from command line.

    cmake tells me:

    qca-botan off
    qca-cyrus-sasl off
    qca-gcrypt off
    qca-gnupg on
    qca-logger on
    qca-nss off
    qca-ossl on
    qca-pkcs11 off
    qca-softstore on
    

    Do you know if i need one or more plugins to use AES 128 CBC?



  • I've mastered the installation now. After building it with Qt Creator i went to the build directory and executed a nmake install. After editing the include paths i also had to add CONFIG += crypto in my pro file. So far no more AES128 CBC not supported errors. But still no luck in decrypting my string.

    QString ciphertext = "PpUr+LMHvaKmf0q6J7Oyzo4jbFO5kfWyXl0d8nD3hyM=";
    QString key1 = "098f6bcd4621d373cade4e832627b4f6";
    QString iv1 = "507055722b4c4d4876614b6d66307136";
    
    QByteArray key;
    key = key1.toLatin1();
    
    QByteArray iv;
    iv = iv1.toLatin1();
    
    QCA::SecureArray arg = ciphertext.toLatin1();
    QCA::Cipher cipher(QString("aes128"),QCA::Cipher::CBC, QCA::Cipher::NoPadding, QCA::Decode, key, iv);
    
    QCA::SecureArray plainText = cipher.update(arg);
    if(!cipher.ok())
    {
    	qDebug() << "update fail";
    }
    plainText = cipher.final();
    if(!cipher.ok())
    {
    	qDebug() << "final fail";
    }
    
    qDebug() << "plainText.data(): " << plainText.data();
    

    The decrypted text should be Dr. Test and password is test. The iv are the fist 16 bytes from the ciphertext and the password is a md5 hash from test. Works in openssl command line. I get a qDebug final fail result.

    Any ideas about that?

    Thanks!



  • I'm doing something wrong and i can't figure out what. Changed to utf8, plain key and iv, vise versa, but somehow i won't get this example decrypted. It does work this way in the openssl command line tool.

    So far i did not find any example code that used a custom key and iv, only random generated ones. Also gave QCA::SymmetricKey::SymmetricKey(key) and QCA::InitializationVector::InitializationVector(iv) a chance, did not really help.

    Any ideas?


  • Lifetime Qt Champion

    Your code doesn't match all the openssl line options. -a -A means that you must first decode your Base64 encoded string.



  • @SGaist Thanks! I don't know why but i did forget about the base64 decode. After changing from toLatin1 to toUtf8 and decode the base64 string i don't get the final error anymore. But i won't get the expected plain text only

    o????:??????a?s?]??Iy?[W6???l??(f?$?I{??^ ????Cï5?
    

    same result if i use

    qDebug() << "process: " << QCA::SecureArray(cipher.process(decodedBase64.toUtf8())).data();
    

    Maybe the utf8 conversion is the problem but it won't take a QString. I get the same result if i append the string to a QByteArray. Btw. noting changes if i use QByteArray to decode base64 or QCA::Base64 decoder(QCA::Decode);

    At least the final error is gone, that's some progress! ;)

    Edit: And i added "md5" as crypto service provider because it was encoded this way. md5 is in the list of providers, so i hope i implemented this correctly.

    QCA::Cipher cipher(QString("aes128"), QCA::Cipher::CBC, QCA::Cipher::NoPadding, QCA::Decode, key, iv, "md5");
    

    But cipher.provider()->name(); returns "qca-ossl".


  • Lifetime Qt Champion

    Do you mean that you passed your original string through md5 before encrypting it ?



  • The password / key was md5 hashed.

    key = md5(utf8(password));
    

    and the iv are the first 16 bytes from the base64 string.

    Edit: Not the key of course, the iv are the first 16 bytes from the base64 string.


  • Lifetime Qt Champion

    Can you show the complete procedure ?



  • Thanks for asking. This is the VB code to encrypt the string:

    Dim AES As New RijndaelManaged
    
    Dim md5 As New MD5CryptoServiceProvider
    Dim key() As Byte = md5.ComputeHash(Encoding.UTF8.GetBytes(password))
    
    md5.Clear()
    AES.Key = key
    AES.GenerateIV()
    Dim iv() As Byte = AES.IV
    Dim ms As New MemoryStream
    
    ms.Write(iv, 0, iv.Length)
    
    Dim cs As New CryptoStream(ms, AES.CreateEncryptor, CryptoStreamMode.Write)
    Dim data() As Byte = System.Text.Encoding.UTF8.GetBytes(string)
    
    cs.Write(data, 0, data.Length)
    cs.FlushFinalBlock()
    
    Dim encoded() As Byte = ms.ToArray()
    Return (Convert.ToBase64String(encoded))
    cs.Close()
    AES.Clear()
    

    As far as i can tell is the password and the string are utf8 bytes and the password is the md5 hash of it. I did try to pass it through

    QCA::SecureArray key = QCA::SecureArray::SecureArray(password);
    

    before and after utf8 and md5. So far no luck.

    Thank you!



  • And this is my attempt:

    QString MainWindow::decryptString(QString password, QString encodedString)
    {
        // final decodec string
        QString decodedString;
    
        // get the iv
        QByteArray array(encodedString.left(16).toStdString().c_str(), encodedString.left(16).size());
        QCA::SecureArray iv = array.toHex();
    
        // decode base64
        QCA::Base64 decoder(QCA::Decode);
        const char* decoded = decoder.decodeString(encodedString).toStdString().c_str();
    
        QCA::SecureArray key = QByteArray(QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5).toHex());
        QCA::SecureArray arg = decoded;
    
        QCA::Cipher cipher(QString("aes128"), QCA::Cipher::CBC, QCA::Cipher::NoPadding, QCA::Decode, key, iv);
        QCA::SecureArray plainText = cipher.update(arg);
    
        if(!cipher.ok())
        {
            qDebug() << "update Fail";
        }
    
        cipher.final();
        if(!cipher.ok())
        {
            qDebug() << "final fail";
        }
    
        qDebug() << "process: " << QCA::SecureArray(cipher.process(decoded)).data();
    
        decodedString = plainText.data();
        qDebug() << "Decoded: " << decodedString;
    
        return decodedString;
    }
    

    I call it like:

    qDebug() << "decoded: " << decryptString("test", "PpUr+LMHvaKmf0q6J7Oyzo4jbFO5kfWyXl0d8nD3hyM=");
    

    The only step i masted was getting rid of the final fail, but i don't know if that was really the case. Not sure how, i changed this so often, i don't even know how much time i spend on this. I didn't think it can be that difficult to use it.

    So far i still did not fine one single example code using qca with aes128cbc and custom iv and key. Very, very strange.

    Any ideas?

    Thanks!


  • Lifetime Qt Champion

    Do you mean this example ?



  • Yes. It is the only one i found so far. But the iv and key are random generated, it does not show how to use an already existing key and iv correctly. I really don't know what i am missing here.


  • Lifetime Qt Champion

    Just replace the random key and iv by yours.



  • I did. That was my first idea, did not work. Id did check the key and iv output, same as i use with openssl, does work in openssl, does not work in Qt / QCA - at least not the way i do it. I don't know, maybe i miss something in general or just just a tiny mistake, but after days and hours, i can confirm, it won't work for me.


  • Lifetime Qt Champion

    I took the example as is, replaced key and iv by

    QByteArray key("098f6bcd4621d373cade4e832627b4f6");
    QByteArray iv("d8e8fca2dc0f896fd7cb4cb0031ba249");
    

    and it's working fine.



  • If you can tell me why this code

    QString decodedString;
    QCA::Initializer init;
    QByteArray array(encodedString.left(16).toStdString().c_str(), encodedString.left(16).size());
    QCA::SecureArray iv = array.toHex();
    QCA::Base64 decoder(QCA::Decode);
    QCA::SecureArray decoded = decoder.decodeString(encodedString).toStdString().c_str();
    QCA::SecureArray key = QByteArray(QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5).toHex());
    QCA::Cipher cipher(QString("aes128"), QCA::Cipher::CBC, QCA::Cipher::NoPadding, QCA::Decode, key, iv);
    QCA::SecureArray plainText = cipher.update(decoded);
    if(!cipher.ok())
    {
    	qDebug() << "update Fail";
    }
    plainText  = cipher.final();
    if(!cipher.ok())
    {
    	qDebug() << "final fail";
    }
    qDebug() << "process: " << QCA::SecureArray(cipher.process(decoded)).data();
    QString decodedString = plainText.data();
    qDebug() << "Decoded: " << decodedString;
    

    is not working, it may help me. If you tell me some code is working for you, it does not. This is btw. taken from the example, i did not change anything beside adding key and iv.


  • Lifetime Qt Champion

    The first thing that looks strange is your iv creation. You take 16 bytes of your encoded string and turn it to hexadecimal. Why ?

    Also, why all the conversions ? Just use QByteArray.



  • 098f6bcd4621d373cade4e832627b4f6 is "test" in md5. all the strings i have to decode are encoded with a different iv, the iv is always the first 16 bytes in hex from the encoded string, d8e8fca2dc0f896fd7cb4cb0031ba249 in this case.

    So i have to hash the key "test" and get the iv from the encoded string.

    And of course, i did test the key and iv just as QByteArray, before and after you posted the example, but the decoding so far did always fail.


  • Lifetime Qt Champion

    That's something that is really not clear. You are telling me that you are using as iv for the decoding a part of the alrey encoded string ? So what did you use as iv for the encoding part ?



  • The iv is taken from the still encrypted and base64 encoded string, first 16 bytes hex. iv 507055722b4c4d4876614b6d66307136 is QByteArray array(encodedString.left(16).toStdString().c_str(), encodedString.left(16).size()); qDebug() << "iv: " << array.toHex();

    Proof:

    echo PpUr+LMHvaKmf0q6J7Oyzo4jbFO5kfWyXl0d8nD3hyM= | openssl enc -d -a -A -aes-128-cbc -iv 507055722b4c4d4876614b6d66307136 -K 098f6bcd4621d373cade4e832627b4f6
    8°&¦=YaÌ?{Äa+Dr. Test
    

  • Lifetime Qt Champion

    And what iv did you use to encrypt the string ?



  • If you scroll up a few posts, there is the source code.


  • Lifetime Qt Champion

    Do you mean the VB code ? Then you generate an IV in there, and use a different one when decrypting your string or am I missing something there ?



  • It is the same iv. There is no problem with the iv i believe, it does work to decrypt in openssl and VB. It is a common way to use the first 16 bytes of a string for iv. Like salting passwords, kind of, not really but i'm no expert and can't explain it better. Defending on the software and api you may have to remove the iv from the string before decrypting but i don't thing it is the case in qca - but i did try it anyways - no luck.

    Maybe i don't use the API correctly. The example code:

    cipher.setup( QCA::Decode, key, iv );
    
    QCA::SecureArray cipherText = u.append(f);
    QCA::SecureArray plainText = cipher.update(cipherText);
    if (!cipher.ok()) {
    	printf("Update failed\n");
    }
    
    printf("Decryption using AES128 of [0x%s] is %s\n",
    	   qPrintable(QCA::arrayToHex(cipherText.toByteArray())), plainText.data());
    
    plainText = cipher.final();
    if (!cipher.ok()) {
    	printf("Final failed\n");
    }
    
    printf("Final decryption block using AES128 is %s\n", plainText.data());
    printf("One step decryption using AES128: %s\n",
    	   QCA::SecureArray(cipher.process(cipherText)).data() );
    

    So cipherText here is u.append(f); the update and the final results together.

    Or maybe i found a bug. Who knows? But chances are i'm doing it wrong, somehow.


  • Lifetime Qt Champion

    That's what I'm trying to clear up with you.

    What you wrote is that you were using a part of your already encoded string as IV when setting up decoding and that's that part that is puzzling me. AFAIK, you should use the same IV that you generated, whatever the means, when you encoded that string. So basically (pseudo code):

    Get secret key as sk
    Get password
    Generate IV as iv: 16 first chars of md5 of password
    Encode "Dr. Test" with sk + iv as encoded_str
    

    and later:

    Get secret key as sk
    Get password
    Generate IV as iv: 16 first char of md5 of password
    Decode encoded_str with sk + iv
    Get Dr. Test
    

    What I understand of what you wrote for the decoding part is:

    Get secret key as sk
    Get password
    Generate IV as iv: 16 first char of encoded_str
    Decode encoded_str with sk + iv
    Get garbage + Dr. Test
    


  • I did not wrote the VB code nor the encoder or decoder for this part. I just try to decrypt strings in XML files encoded using this VB code. For now i use a bash script on Linux but i fail to implement it in my Qt app so far.

    Bash:

    passmd5="$(echo -n "$1" | md5sum | cut -d '-' -f1 | tr -d '[[:space:]]')"
    theiv="$(echo $2 | xxd -l 16 -ps)"
    echo $2 | openssl enc -d -a -A -aes-128-cbc -iv $theiv -K $passmd5 | tail -c +17
    

    Works perfectly. No luck in Qt.


  • Lifetime Qt Champion

    Can you give a sample of input you pass to that script and what you should have as output ?



  • Of course:

    #!/bin/bash
    password="test" # the password
    cstring="PpUr+LMHvaKmf0q6J7Oyzo4jbFO5kfWyXl0d8nD3hyM=" # the aes-128-cbc and base64 encoded string
    passmd5="$(echo -n "$password" | md5sum | cut -d '-' -f1 | tr -d '[[:space:]]')" # md5 hash the password
    theiv="$(echo $cstring | xxd -l 16 -ps)" # get the iv
    decoded="$(echo $cstring | openssl enc -d -a -A -aes-128-cbc -iv $theiv -K $passmd5 | tail -c +17)" # decode in openssl
    
    # expected: Dr. Test
    echo decoded: $decoded
    

  • Lifetime Qt Champion

    Based on your script

    QByteArray data = "PpUr+LMHvaKmf0q6J7Oyzo4jbFO5kfWyXl0d8nD3hyM=";
    QByteArray iv = data.left(16).toHex();
    QCA::Hash hash( "md5" );
    hash.update("test");
    QCA::SecureArray key = hash.final();
    QCA::Cipher cipher(QString("aes128"),QCA::Cipher::CBC,
                                     // use Default padding, which is equivalent to PKCS7 for CBC
                                    QCA::Cipher::DefaultPadding,
                                    // this object will encrypt
                                    QCA::Decode,
                                    key, iv);
    
            QCA::SecureArray decryptedData = cipher.process(QByteArray::fromBase64(data));
            if (!cipher.ok()) {
                qDebug() << "Decryption failed !  ";
            }
            qDebug() << decryptedData.toByteArray().mid(16);
    

    Note however that using tail like that looks suspicious. I'm still not convinced of your initial vector handling. It should be the same used when encoding the string. Here it clearly is not.



  • @SGaist Perfekt, thank you!

    I was total stuck. Password hash, update and final before. Thanks again!



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.