Qt with OpenSSL AES 256 CBC Encryption



  • Hi all ,
    I have written code using OpenSSL library to encrypt data using AES 256 CBC encryption but when I encrypt data then decrypt it I don't end up with the original data , I followed the wiki pages of the library so I don't know what I am doing wrong now can you please take a look at the code and point to me my errors .
    Thank you in advance.

    Encryption File :

    int Encryption::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], iv[32];
    
      /*
       * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
       * nrounds is the number of times the we hash the material. More rounds are more secure but
       * slower.
       */
      i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
      if (i != 32) {
        qDebug()<<"The key isn't 256 bit";
        return -1;
      }
    
      EVP_CIPHER_CTX_init(e_ctx);
      EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
      EVP_CIPHER_CTX_init(d_ctx);
      EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
    
      return 0;
    }
    
    unsigned char* Encryption:: Encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len)
    {
      /* max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes */
      int c_len = *len + AES_BLOCK_SIZE, f_len = 0;
      unsigned char *ciphertext = (unsigned char*)malloc(c_len);
    
      /* allows reusing of 'e' for multiple encryption cycles */
      EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);
    
      /* update ciphertext, c_len is filled with the length of ciphertext generated,
        *len is the size of plaintext in bytes */
      EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, *len);
      /* update ciphertext with the final remaining bytes */
      EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len);
    
      *len = c_len + f_len;
      return ciphertext;
    }
    
    unsigned char* Encryption::Decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
    {
      /* plaintext will always be equal to or lesser than length of ciphertext*/
      int p_len = *len, f_len = 0;
      unsigned char *plaintext = (unsigned char*)malloc(p_len);
      EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
      EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
      EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
    
    
      *len = p_len + f_len;
      return plaintext;
    }
    

    Main :

     /* Message to be encrypted */
            unsigned char* plaintext =
                        (unsigned char*)"The quick brown fox jumps over the lazy dog\0";
    
    
          EVP_CIPHER_CTX *en, *de;
           en = EVP_CIPHER_CTX_new();
           de = EVP_CIPHER_CTX_new();
    
            /* 8 bytes to salt the key_data during key generation. This is an example of
               compiled in salt. We just read the bit pattern created by these two 4 byte
               integers on the stack as 64 bits of contigous salt material -
               ofcourse this only works if sizeof(int) >= 4 */
            unsigned int salt[] = {12345, 54321};
            unsigned char *key_data;
            int key_data_len, i;
    
            key_data = (unsigned char*)"01234567890123456789012345678901";
            key_data_len = strlen((const char*)key_data);
    
            Encryption AES;
    
            /* gen key and iv. init the cipher ctx object */
            if (AES.Init(key_data, key_data_len, (unsigned char *)&salt, en, de)) {
              printf("Couldn't initialize AES cipher\n");
            }
    
            /* encrypt and decrypt each input string and compare with the original */
              unsigned char *ciphertext;
              int olen, len;
    
              /* The enc/dec functions deal with binary data and not C strings. strlen() will
                 return length of the string without counting the '\0' string marker. We always
                 pass in the marker byte to the encrypt/decrypt functions so that after decryption
                 we end up with a legal C string */
              olen = len = strlen((const char*)plaintext)+1;
    
    
    
              ciphertext = AES.Encrypt(en, plaintext, &len);
              plaintext = (unsigned char*)"";
              plaintext = AES.Decrypt(de, ciphertext, &len);
    
              QString s;
              QString result = "";
              int rev = strlen((const char*)ciphertext);
    
                  // Print String in Reverse order....
              for ( int i = 0; i<rev; i++)
                   {
                  //qDebug()<<"here";
                      s = QString("%1").arg(ciphertext[i],0,10);
    
                      if(s == "0"){
                        s="00";
                       }
                    result.append(s);
    
                    }
    
              ui->label_7->setText(result);
    
              rev = len;
              result = "";
              for ( int i = 0; i<rev; i++)
                             {
                                s = QString("%1").arg(plaintext[i],0,10);
    
                                if(s == "0"){
                                  s="00";
                                 }
                              result.append(s);
    
                              }
    
              ui->label_8->setText(result);
    
              free(ciphertext);
              free(plaintext);
    
            EVP_CIPHER_CTX_free(en);
            EVP_CIPHER_CTX_free(de);
    

  • Lifetime Qt Champion

    Hi,

    What example is that ?
    What result do you get ?
    What result is expected ?

    Did you run it successfully without Qt involved ? From the looks of it, Qt has nothing to do with your current trouble as there's no interaction between Qt classes and your OpenSSL code.



  • Hi, the code is from the old example file openssl_aes.c.txt (link here )
    I know that code works, why not use printf() as it does, instead of showing the results reversed in label_7 and label_8?



  • Hi, wrote a Qt-flavored version which might help you:
    Using Qt 5.11.2 on my Ubuntu 18.04, I created a Qt console project.
    I added LIBS += -lcrypto to my .pro file and clicked Build/Run qmake.
    Then I changed main.cpp into this:

    #include <QCoreApplication>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <openssl/aes.h>
    #include <openssl/evp.h>
    
    int 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], iv[32];
    
        i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
        if (i != 32)
        {
            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_256_cbc(), nullptr, key, iv);
        EVP_CIPHER_CTX_init(d_ctx);
        EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), nullptr, key, iv);
    
        return 0;
    }
    
    unsigned char *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 *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;
    }
    
    // modified main to use QString and QByteArray
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        EVP_CIPHER_CTX* en = EVP_CIPHER_CTX_new();
        EVP_CIPHER_CTX* de = EVP_CIPHER_CTX_new();
    
        unsigned int salt[] = {12345, 54321};
        unsigned char key_data[] = "01234567890123456789012345678901";
        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, reinterpret_cast<unsigned char *>(&salt), en, de))
        {
            printf("Couldn't initialize AES cipher\n");
            exit(1);
        }
    
    // encrypt or decrypt?
        if (2 != argc)
        {
        // neither, print help text
            printf("Usage:\n./untitled Plaintext\nPrints the ciphertext\n\n./untitled 0xXXXXX\nPrints the plaintext.\n");
            exit(1);
        }
    
        QString arg = argv[1];
    
    // is it decrypt?
        if (arg.startsWith("0x") || arg.startsWith("0X"))
        {
        // yes, decrypt
            auto ba = QByteArray::fromHex(arg.mid(2).toLatin1());
            int len = ba.length();
            const char* cipherText = ba;
            printf("plainText = %s\n",aes_decrypt(de,reinterpret_cast<unsigned char*>(const_cast<char*>(cipherText)),&len));
        }
        else
        {
        // encrypt
            int len = arg.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()));
        }
    }
    

    If you specify a normal string it prints the encrypted string in hex, and if you specify the hex string (beginning with 0x) then it prints the original plaintext. Hope this helps!



  • Thanks for your help it encrypt now and decrypt to the original text, but now I want to be able to encrypt files instead of text I wrote this code and it result in a runtime error from debugging sometimes it crash at file.close() and sometimes it crash after writeFile function because of segmentation error I don't know what is the reason can someone point to me the error in my code please :

    MainWindow :

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QFileDialog>
    #include <encryption.h>
    #include <QDebug>
    #include <QMessageBox>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        QString Extension;
        qint64 size;
        fileName = QFileDialog::getOpenFileName(this);
        if(!fileName.isEmpty())
        {
            size = QFileInfo(fileName).size();
            Extension = QFileInfo(fileName).suffix();
            ui->label->setText(fileName);
            ui->label_2->setText(QString::number(size)+" Bytes");
            ui->label_3->setText(Extension);
        }
    
    
    }
    
    void MainWindow::on_pushButton_2_clicked()
    {
        /* Set up the key and iv. Do I need to say to not hard code these in a
           * real application? :-)
           */
        Encryption AES;
        unsigned char* plaintext;
        //fileName = QFileDialog::getOpenFileName(this);
        QString SavedfileName = QFileDialog::getSaveFileName(this,
                tr("Save Encryption file"), "",
                tr("All Files (*)"));
    
        if(!fileName.isEmpty())
        {
            plaintext = AES.ReadFile(fileName);
            EVP_CIPHER_CTX *en, *de;
            en = EVP_CIPHER_CTX_new();
            de = EVP_CIPHER_CTX_new();
    
            unsigned int salt[] = {12345, 54321};
            unsigned char *key_data;
            int key_data_len, i;
    
            key_data = (unsigned char*)ui->lineEdit->text().constData();
            key_data_len = strlen((const char*)key_data);
    
    
    
            /* gen key and iv. init the cipher ctx object */
            if (AES.Init(key_data, key_data_len, (unsigned char *)&salt, en, de)) {
              printf("Couldn't initialize AES cipher\n");
            }
    
              unsigned char *ciphertext;
              int olen, len;
    
              /* The enc/dec functions deal with binary data and not C strings. strlen() will
                 return length of the string without counting the '\0' string marker. We always
                 pass in the marker byte to the encrypt/decrypt functions so that after decryption
                 we end up with a legal C string */
              olen = len = strlen((const char*)plaintext)+1;
    
    
    
              ciphertext = AES.Encrypt(en, plaintext, &len);
    
              if (SavedfileName.isEmpty()){
                  QMessageBox::information(this, tr("Unable to open encryption file!"),tr("Please enter a name to the encryption file."));
                      return;}
              else {
                  QFile file(SavedfileName);
                  if (!file.open(QIODevice::WriteOnly)) {
                      QMessageBox::information(this, tr("Unable to open file"),
                          file.errorString());
                      return;
                  }
                  else{    
                      AES.WriteFile(SavedfileName,ciphertext);
                  }
              }
    
    
              free(ciphertext);
              free(plaintext);
    
            EVP_CIPHER_CTX_free(en);
            EVP_CIPHER_CTX_free(de);
            
    
        }
    
    }
    
    void MainWindow::on_pushButton_3_clicked()
    {
        //plaintext = AES.Decrypt(de, ciphertext, &len);
    }
    
    

    Encryption class:

    #include "encryption.h"
    #include <QDebug>
    #include <QFile>
    #include <QDataStream>
    
    Encryption::Encryption(QObject *parent) : QObject(parent)
    {
    
    }
    
    int Encryption::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], iv[32];
    
      /*
       * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
       * nrounds is the number of times the we hash the material. More rounds are more secure but
       * slower.
       */
      i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
      if (i != 32) {
        qDebug()<<"The key isn't 256 bit";
        return -1;
      }
    
      EVP_CIPHER_CTX_init(e_ctx);
      EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
      EVP_CIPHER_CTX_init(d_ctx);
      EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
    
      return 0;
    }
    
    unsigned char* Encryption:: Encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len)
    {
      /* max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes */
      int c_len = *len + AES_BLOCK_SIZE, f_len = 0;
      unsigned char *ciphertext = (unsigned char*)malloc(c_len);
    
      /* allows reusing of 'e' for multiple encryption cycles */
      EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);
    
      /* update ciphertext, c_len is filled with the length of ciphertext generated,
        *len is the size of plaintext in bytes */
      EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, *len);
      /* update ciphertext with the final remaining bytes */
      EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len);
    
      *len = c_len + f_len;
      return ciphertext;
    }
    
    unsigned char* Encryption::Decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
    {
      /* plaintext will always be equal to or lesser than length of ciphertext*/
      int p_len = *len, f_len = 0;
      unsigned char *plaintext = (unsigned char*)malloc(p_len);
      EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
      EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
      EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
    
    
      *len = p_len + f_len;
      return plaintext;
    }
    
    unsigned char *Encryption::ReadFile(QString fileName)
    {
        QByteArray data;
        QFile file(fileName);
        if(!file.open(QFile::ReadOnly))
        {
            qCritical() << file.errorString();
            return (unsigned char*) (data.constData());
        }
    
        data = file.readAll();
        file.close();
        return (unsigned char*) (data.constData());
    }
    
    void Encryption::WriteFile(QString fileName, unsigned char* data)
    {
        QFile file(fileName);
        if(!file.open(QFile::WriteOnly))
        {
            qCritical() << file.errorString();
            return;
        }
    
        file.write((const char*)data,strlen((const char*)data));
        file.close();
    }
    
    

    Debug results from two separate runs:
    1_1542376539106_runtime error2.png 0_1542376539104_runtime error.png



  • @SGaist this Example

    the problem with the original code was in displaying the result encryption and decryption text after changing it the decrypted text ended being the same as the original text.

    Now the program crash sometimes after I try to write to a file the encrypted code with segmentation error and sometimes when I try to close the file I think depending on the key_data size because that's the only variable here.

    Can you please take a look on my other reply and give me a hint as why this is happening ?

    Thanks in advance.


  • Lifetime Qt Champion

    You should run your application through a debugger, that will show you exactly where it crashes.



  • @SGaist I did and posted above the results but I found the problem I forgot to add :

     unsigned char* plaintext = (unsigned char*)malloc(size);
     unsigned char* plaintext = (unsigned char*)malloc(size+AES_BLOCK_SIZE);
    

    respectively, after adding those two lines in their rightful places there were no more crashes but now I face another problem I changed the codeslightly to be able to encrypt large files now when I encrypt files the encrypted file is much smaller than the original file which should not be the case and when I try to decrypt it the program exit on this line :

    EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
    

    with this message :

    crypto\evp\evp_enc.c:157: OpenSSL internal error: assertion failed: ctx->cipher->block_size == 1 || ctx->cipher->block_size == 8 || ctx->cipher->block_size == 16
    

    what I understand from this error is that the encrypted file is not a multiple of blocks of encryption block size which should be here 16 bytes but that should not be the case since padding is enabled by default so the encrypted file should be a multiple of blocks, so I couldn't figure out what is the problem.
    Can you help please ?

    This is the code after changes :

    MainWindow:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QFileDialog>
    #include <QDebug>
    #include <QMessageBox>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        QString Extension;
        fileName = QFileDialog::getOpenFileName(this);
        if(!fileName.isEmpty())
        {
            size = QFileInfo(fileName).size();
            Extension = QFileInfo(fileName).suffix();
            ui->label->setText(fileName);
            ui->label_2->setText(QString::number(size)+" Bytes");
            ui->label_3->setText(Extension);
        }
    
    
    }
    
    void MainWindow::on_pushButton_2_clicked()
    {
        /* Set up the key and iv. Do I need to say to not hard code these in a
           * real application? :-)
           */
    
        unsigned char* plaintext = (unsigned char*)malloc(size);
        //fileName = QFileDialog::getOpenFileName(this);
        QString SavedfileName = QFileDialog::getSaveFileName(this,
                tr("Save Encryption file"), "",
                tr("All Files (*)"));
    
        if(!fileName.isEmpty())
        {
            plaintext = AES.ReadFile(fileName);
            en = EVP_CIPHER_CTX_new();
            de = EVP_CIPHER_CTX_new();
    
            unsigned int salt[] = {12345, 54321};
            unsigned char *key_data;
            int key_data_len, i;
    
            key_data = (unsigned char*)ui->lineEdit->text().constData();
            key_data_len = strlen((const char*)key_data);
    
    
    
            /* gen key and iv. init the cipher ctx object */
            if (AES.Init(key_data, key_data_len, (unsigned char *)&salt, en, de)) {
              printf("Couldn't initialize AES cipher\n");
            }
    
    
              int olen, len;
    
              /* The enc/dec functions deal with binary data and not C strings. strlen() will
                 return length of the string without counting the '\0' string marker. We always
                 pass in the marker byte to the encrypt/decrypt functions so that after decryption
                 we end up with a legal C string */
              olen = len = strlen((const char*)plaintext)+1;
              unsigned char *ciphertext =(unsigned char*)malloc(len+ AES_BLOCK_SIZE);
    
    
    
              ciphertext = AES.Encrypt(en, plaintext, &len);
    
              if (SavedfileName.isEmpty()){
                  QMessageBox::information(this, tr("Unable to open encryption file!"),tr("Please enter a name to the encryption file."));
                      return;}
              else {
                  QFile file(SavedfileName);
                  if (!file.open(QIODevice::WriteOnly)) {
                      QMessageBox::information(this, tr("Unable to open file"),
                          file.errorString());
                      return;
                  }
                  else{
                      qDebug() << "Hi-1";
                      AES.WriteFile(SavedfileName,ciphertext);
                      qDebug() << "Hi4";
                  }
              }
    
    
              qDebug() << "Hi5";
              free(ciphertext);
              free(plaintext);
              qDebug() << "Hi6";
    
            EVP_CIPHER_CTX_free(en);
            qDebug() << "Hi7";
    
    
    
        }
    
    }
    
    void MainWindow::on_pushButton_3_clicked()
    {
        //plaintext = AES.Decrypt(de, ciphertext, &len);
    
        unsigned char* plaintext = (unsigned char*)malloc(size+AES_BLOCK_SIZE);
        //fileName = QFileDialog::getOpenFileName(this);
    
        QString fileName = QFileDialog::getOpenFileName(this,
                tr("Open encryption file"), "",
                tr("All Files (*)"));
    
        if(!fileName.isEmpty())
        {
            QString SavedfileName = QFileDialog::getSaveFileName(this,
                    tr("Save Decryption file"), "",
                    tr("All Files (*)"));
    
            plaintext = AES.ReadFile(fileName);
    
            unsigned char *key_data;
            int key_data_len, i;
    
            key_data = (unsigned char*)ui->lineEdit->text().constData();
            key_data_len = strlen((const char*)key_data);
    
    
    
    
              int olen, len;
    
              /* The enc/dec functions deal with binary data and not C strings. strlen() will
                 return length of the string without counting the '\0' string marker. We always
                 pass in the marker byte to the encrypt/decrypt functions so that after decryption
                 we end up with a legal C string */
              olen = len = strlen((const char*)plaintext)+1;
              unsigned char *ciphertext =(unsigned char*)malloc(len);
    
    
              ciphertext = AES.Decrypt(en, plaintext, &len);
    
              if (SavedfileName.isEmpty()){
                  QMessageBox::information(this, tr("Unable to open decryption file!"),tr("Please enter a name to the decryption file."));
                      return;}
              else {
                  QFile file(SavedfileName);
                  if (!file.open(QIODevice::WriteOnly)) {
                      QMessageBox::information(this, tr("Unable to open file"),
                          file.errorString());
                      return;
                  }
                  else{
                      qDebug() << "Hi-1";
                      AES.WriteFile(SavedfileName,ciphertext);
                      qDebug() << "Hi4";
                  }
              }
    
    
              qDebug() << "Hi5";
              free(ciphertext);
              free(plaintext);
              qDebug() << "Hi6";
    
            EVP_CIPHER_CTX_free(de);
            qDebug() << "Hi7";
    
    
    
        }
    
    }
    
    

    Encryption Class:

    #include "encryption.h"
    #include <QDebug>
    #include <QFile>
    #include <QDataStream>
    
    Encryption::Encryption(QObject *parent) : QObject(parent)
    {
    
    }
    
    int Encryption::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], iv[32];
    
      /*
       * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
       * nrounds is the number of times the we hash the material. More rounds are more secure but
       * slower.
       */
      i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
      if (i != 32) {
        qDebug()<<"The key isn't 256 bit";
        return -1;
      }
    
      EVP_CIPHER_CTX_init(e_ctx);
      EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
      EVP_CIPHER_CTX_init(d_ctx);
      EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
    
      return 0;
    }
    
    unsigned char* Encryption:: Encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len)
    {
      /* max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes */
      int c_len = *len + AES_BLOCK_SIZE, f_len = 0;
      unsigned char *ciphertext = (unsigned char*)malloc(c_len);
    
      /* allows reusing of 'e' for multiple encryption cycles */
      EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);
    
      /* update ciphertext, c_len is filled with the length of ciphertext generated,
        *len is the size of plaintext in bytes */
      int encrypted_bytes = 0;
    
      for(int i=0;i<*len/AES_BLOCK_SIZE+1;++i)
      {
          EVP_EncryptUpdate(e, ciphertext+encrypted_bytes, &c_len, plaintext+encrypted_bytes, AES_BLOCK_SIZE);
          encrypted_bytes += AES_BLOCK_SIZE;
      }
      /* update ciphertext with the final remaining bytes */
      EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len);
    
      *len = c_len + f_len;
      return ciphertext;
    }
    
    unsigned char* Encryption::Decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
    {
      /* plaintext will always be equal to or lesser than length of ciphertext*/
      int p_len = *len, f_len = 0;
      unsigned char *plaintext = (unsigned char*)malloc(p_len);
      qDebug()<< "hi8";
      EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
      qDebug()<< "hi9";
      int decrypted_bytes = 0;
    
      for(int i=0;i<*len/AES_BLOCK_SIZE;++i){
          EVP_DecryptUpdate(e, plaintext+decrypted_bytes, &p_len, ciphertext+decrypted_bytes, AES_BLOCK_SIZE);
          decrypted_bytes += AES_BLOCK_SIZE;
      }
    
      EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
    
    
      *len = p_len + f_len;
      return plaintext;
    }
    
    unsigned char *Encryption::ReadFile(QString fileName)
    {
        QByteArray data;
        QFile file(fileName);
        if(!file.open(QFile::ReadOnly))
        {
            qCritical() << file.errorString();
            return (unsigned char*) (data.constData());
        }
    
        data = file.readAll();
        file.close();
        return (unsigned char*) (data.constData());
    }
    
    void Encryption::WriteFile(QString fileName, unsigned char* data)
    {
        QFile file(fileName);
        if(!file.open(QFile::WriteOnly))
        {
            qCritical() << file.errorString();
            return;
        }
        qDebug() << "Hi0";
    
        file.write((const char*)data,strlen((const char*)data));
        qDebug() << "Hi2";
        file.close();
        qDebug() << "Hi3";
    }
    
    

    Note that AES_BLOCK_SIZE is defined in Encryption.h as follow :

    #define AES_BLOCK_SIZE 16
    

    Thanks in advance.



  • I found the reason for the error I was passing the encryption context instead of the decryption one now the only problem is that it encrypts a small part of the file and when it decrypts it doesn't decrypt it all right so I want to know what I am doing wrong for it to not encrypt all the file and for the decryption to go wrong .

    Thanks in advance.



  • Now Igot it to encrypt all the file but the last problem remaining is that some part of the file after decryption gets messed up.
    Can you please help me figure that out ?
    The text on the left is the decrypted one, you can see the distortion in the file header.
    0_1542458611925_compare.png
    Thanks.



  • Hi, I see at least one problem: when you call AES,Encrypt() the returned ciphertext contains more or less random characters, so there's one chance in 256 that one unsigned char is '\0' (0x00 in hex). That means if you are using strlen(ciphertext) to determine how much to write to your file, there's a chance that strlen(ciphertext) will give you the wrong answer, i.e. returning a lower number. One way to get around this problem is to use the length of the ciphertext returned by the AES.Encrypt() call. it's the 3rd argument, instead of relying on that strlen() call.



  • @hskoglund Thanks , yes i noticed that earlier and replaced all strlen() calls with a fixed length and that what fixed the crashes.Now the problem is the random bytes that appears inside the decrypted file and don't know what cause this can you please take a look and tell me what I am doing wrong ?

    MainWindow:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QFileDialog>
    #include <QDebug>
    #include <QMessageBox>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        ui->progressBar->setMinimum(0);
        ui->progressBar->setMaximum(100);
        ui->progressBar_2->setMinimum(0);
        ui->progressBar_2->setMaximum(100);
    
        connect(ui->lineEdit,SIGNAL(textEdited(QString)),this,SLOT(Enable_Encryption_Button(QString)));
        connect(&AES,SIGNAL(Encryption_Finished()),this,SLOT(Enable_Decryption_Button()));
        connect(&AES,SIGNAL(Encryption_Percentage_Changed(int)),this,SLOT(Change_Encryption_Precentage(int)));
        connect(&AES,SIGNAL(Decryption_Percentage_Changed(int)),this,SLOT(Change_Decryption_Precentage(int)));
        connect(&AES,SIGNAL(Encryption_Finished()),this,SLOT(Finish_Encryption()));
        connect(&AES,SIGNAL(Decryption_Finished()),this,SLOT(Finish_Decryption()));
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        ui->progressBar->reset();
        ui->progressBar_2->reset();
    
        QString Extension;
        fileName = QFileDialog::getOpenFileName(this);
        if(!fileName.isEmpty())
        {
            size = QFileInfo(fileName).size();
            Extension = QFileInfo(fileName).suffix();
            ui->label->setText(fileName);
            ui->label_2->setText(QString::number(size)+" Bytes");
            ui->label_3->setText(Extension);
    
            ui->lineEdit->setEnabled(true);
        }
    
    
    }
    
    void MainWindow::on_pushButton_2_clicked()
    {
        unsigned char* plaintext = (unsigned char*)malloc(size);
    
        QString SavedfileName = QFileDialog::getSaveFileName(this,
                tr("Save Encryption file"), "",
                tr("All Files (*)"));
    
        if(!fileName.isEmpty())
        {
            plaintext = AES.ReadFile(fileName);
            en = EVP_CIPHER_CTX_new();
            de = EVP_CIPHER_CTX_new();
    
            unsigned int salt[] = {12345, 54321};
            unsigned char *key_data;
            int key_data_len;
    
            key_data = (unsigned char*)ui->lineEdit->text().constData();
            key_data_len = strlen((const char*)key_data);
    
    
    
            /* Generate key and iv. init the cipher ctx object */
            if (AES.Init(key_data, key_data_len, (unsigned char *)&salt, en, de)) {
              QMessageBox::warning(this, tr("Error while Initializing algorithm!"), tr("Couldn't initialize AES cipher!"));
            }
    
    
              int len;
    
              len = size+1;
              unsigned char *ciphertext =(unsigned char*)malloc(len+ AES_BLOCK_SIZE);
    
    
    
              ciphertext = AES.Encrypt(en, plaintext, &len);
    
              if (SavedfileName.isEmpty()){
                  QMessageBox::information(this, tr("Unable to open encryption file!"),tr("Please enter a name to the encryption file."));
                      return;}
              else {
                  QFile file(SavedfileName);
                  if (!file.open(QIODevice::WriteOnly)) {
                      QMessageBox::information(this, tr("Unable to open file"),
                          file.errorString());
                      return;
                  }
                  else{
                      AES.WriteFile(SavedfileName,ciphertext,size+1+ AES_BLOCK_SIZE);
                  }
              }
    
    
              free(ciphertext);
              free(plaintext);
    
            EVP_CIPHER_CTX_free(en);
    
        }
    
    }
    
    void MainWindow::on_pushButton_3_clicked()
    {
       if(!ui->lineEdit->text().isEmpty())
       {
           unsigned char* plaintext = (unsigned char*)malloc(size+AES_BLOCK_SIZE);
    
           QString fileName = QFileDialog::getOpenFileName(this,
                   tr("Open encryption file"), "",
                   tr("All Files (*)"));
    
           if(!fileName.isEmpty())
           {
               QString SavedfileName = QFileDialog::getSaveFileName(this,
                       tr("Save Decryption file"), "",
                       tr("All Files (*)"));
    
               plaintext = AES.ReadFile(fileName);
    
               int len;
    
               len = size+AES_BLOCK_SIZE+1;
               unsigned char *ciphertext =(unsigned char*)malloc(len);
    
    
               ciphertext = AES.Decrypt(de, plaintext, &len);
    
               if (SavedfileName.isEmpty()){
                   QMessageBox::information(this, tr("Unable to open decryption file!"),tr("Please enter a name to the decryption file."));
                       return;}
               else {
                   QFile file(SavedfileName);
                   if (!file.open(QIODevice::WriteOnly)) {
                       QMessageBox::information(this, tr("Unable to open file"),
                           file.errorString());
                       return;
                   }
                   else{
                       AES.WriteFile(SavedfileName,ciphertext,size+AES_BLOCK_SIZE+1);
                   }
               }
    
    
               free(ciphertext);
               free(plaintext);
    
               EVP_CIPHER_CTX_free(de);
           }
       }
    
    }
    
    void MainWindow::Change_Encryption_Precentage(int percentage)
    {
        ui->progressBar->setValue(percentage);
    }
    
    void MainWindow::Change_Decryption_Precentage(int percentage)
    {
        ui->progressBar_2->setValue(percentage);
    }
    
    void MainWindow::Finish_Encryption()
    {
        ui->progressBar->setValue(100);
    }
    
    void MainWindow::Finish_Decryption()
    {
        ui->progressBar_2->setValue(100);
    }
    
    void MainWindow::Enable_Encryption_Button(QString text)
    {
        if(!text.isEmpty())
            ui->pushButton_2->setEnabled(true);
        else
            ui->pushButton_2->setDisabled(true);
    }
    
    void MainWindow::Enable_Decryption_Button()
    {
        if(!ui->lineEdit->text().isEmpty())
            ui->pushButton_3->setEnabled(true);
    }
    
    
    

    Encryption Class :

    #include "encryption.h"
    #include <QDebug>
    #include <QFile>
    #include <QMessageBox>
    
    Encryption::Encryption(QWidget *parent) : QWidget(parent)
    {
    
    }
    
    int Encryption::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], iv[32];
    
      /*
       * Generate key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
       * nrounds is the number of times we hash the material. More rounds are more secure but
       * slower.
       */
      i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
      if (i != 32) {
        QMessageBox::warning(this, tr("Error while generating the key"), tr("The key isn't 256 bit!"));
        return -1;
      }
    
      EVP_CIPHER_CTX_init(e_ctx);
      EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
      EVP_CIPHER_CTX_init(d_ctx);
      EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
    
      return 0;
    }
    
    unsigned char* Encryption:: Encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len)
    {
      // max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE bytes
      int c_len = *len + AES_BLOCK_SIZE, f_len = 0;
      unsigned char *ciphertext = (unsigned char*)malloc(c_len);
    
      // allows reusing of 'e' for multiple encryption cycles
      EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);
    
      /* update ciphertext, c_len is filled with the length of ciphertext generated,
        *len is the size of plaintext in bytes */
      int encrypted_bytes = 0;
    
      for(int i=0;i<*len/AES_BLOCK_SIZE+1;++i)
      {
          EVP_EncryptUpdate(e, ciphertext+encrypted_bytes, &c_len, plaintext+encrypted_bytes, AES_BLOCK_SIZE);
          encrypted_bytes += AES_BLOCK_SIZE;
          float counter = i , size = *len/AES_BLOCK_SIZE+1;
          emit Encryption_Percentage_Changed(counter/size * 100);
      }
      // update ciphertext with the final remaining bytes
      EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len);
    
      *len = c_len + f_len;
      emit Encryption_Finished();
      return ciphertext;
    }
    
    unsigned char* Encryption::Decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
    {
      // plaintext will always be equal to or lesser than length of ciphertext
      int p_len = *len, f_len = 0;
    
      unsigned char *plaintext = (unsigned char*)malloc(p_len);
    
      EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
    
      int decrypted_bytes = 0;
    
      for(int i=0;i<*len/AES_BLOCK_SIZE;++i){
          EVP_DecryptUpdate(e, plaintext+decrypted_bytes, &p_len, ciphertext+decrypted_bytes, AES_BLOCK_SIZE);
          decrypted_bytes += AES_BLOCK_SIZE;
          float counter = i , size = *len/AES_BLOCK_SIZE;
          emit Decryption_Percentage_Changed(counter/size * 100);
      }
    
      EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
    
    
      *len = p_len + f_len;
    
      emit Decryption_Finished();
    
      return plaintext;
    }
    
    unsigned char *Encryption::ReadFile(QString fileName)
    {
        QByteArray data;
        QFile file(fileName);
        if(!file.open(QFile::ReadOnly))
        {
            QMessageBox::warning(this, tr("Error while Opening file to read!"), file.errorString());
            return (unsigned char*) (data.constData());
        }
    
        data = file.readAll();
        file.close();
        return (unsigned char*) (data.constData());
    }
    
    void Encryption::WriteFile(QString fileName, unsigned char* data,qint64 size)
    {
        QFile file(fileName);
        if(!file.open(QFile::WriteOnly))
        {
            QMessageBox::warning(this, tr("Error while Opening file to write!"), file.errorString());
            return;
        }
    
        file.write((const char*)data,size);
        file.close();
    }
    
    

    Thanks in advance.



  • Hmm I see you are no longer using a single call to EVP_EncryptUpdate() and EVP_DecryptUpdate(), that means you have to very careful with the # of the returned encrypted unsigned chars, so this: EVP_EncryptUpdate(e, ciphertext+encrypted_bytes, &c_len, plaintext+encrypted_bytes, AES_BLOCK_SIZE); is probably wrong because you're using the variable encrypted_bytes to step through both the plaintext and the cipher unsigned chars. Problem is that the number of AES256 encrypted unsigned chars are usually more than the plaintext ones, so using encrypted_bytes will create a bug. The function EVP_EncryptUpdate() returns the number of encrypted unsigned chars in the 3rd argument (c_len), you could try using that for the stepping.



  • @hskoglund Thanks for the help I tried the following with no success :

    unsigned char* Encryption:: Encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len)
    {
      // max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE bytes
      int c_len = *len + AES_BLOCK_SIZE, f_len = 0;
      unsigned char *ciphertext = (unsigned char*)malloc(c_len);
    
      // allows reusing of 'e' for multiple encryption cycles
      EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);
    
      /* update ciphertext, c_len is filled with the length of ciphertext generated,
        *len is the size of plaintext in bytes */
      int encrypted_bytes = 0;
      int Plain_Pos = 0;
    
      for(int i=0;i<*len/AES_BLOCK_SIZE+1;++i)
      {
          EVP_EncryptUpdate(e, ciphertext+encrypted_bytes, &c_len, plaintext+Plain_Pos, AES_BLOCK_SIZE);
          encrypted_bytes += c_len;
          Plain_Pos += AES_BLOCK_SIZE;
    
          float counter = i , size = *len/AES_BLOCK_SIZE+1;
          emit Encryption_Percentage_Changed(counter/size * 100);
      }
      // update ciphertext with the final remaining bytes
      EVP_EncryptFinal_ex(e, ciphertext+encrypted_bytes, &f_len);
    
      *len = encrypted_bytes + f_len;
      qDebug() << *len;
      emit Encryption_Finished();
    
      return ciphertext;
    }
    
    unsigned char* Encryption::Decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
    {
      // plaintext will always be equal to or lesser than length of ciphertext
      int p_len = *len, f_len = 0;
    
      unsigned char *plaintext = (unsigned char*)malloc(p_len);
    
      EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
    
      int decrypted_bytes = 0;
      int Plain_Pos = 0;
    
      for(int i=0;i<*len/AES_BLOCK_SIZE;++i){
          EVP_DecryptUpdate(e, plaintext+decrypted_bytes, &p_len, ciphertext+Plain_Pos, AES_BLOCK_SIZE);
          decrypted_bytes += p_len;
          Plain_Pos += AES_BLOCK_SIZE;
          float counter = i , size = *len/AES_BLOCK_SIZE;
          emit Decryption_Percentage_Changed(counter/size * 100);
      }
    
      EVP_DecryptFinal_ex(e, plaintext+decrypted_bytes, &f_len);
    
    
      *len = decrypted_bytes + f_len;
      qDebug() << *len;
      emit Decryption_Finished();
    
      return plaintext;
    }
    


  • Hi, because you're doing multiple EVP_EncryptUpdate() and EVP_DecryptUpdate() calls, as I said, you have to very careful with the # of bytes.
    encrypted_bytes looks to be counting up ok now, but what about the input (the plaintext)?

    If you look at the for loop in Encryption:: Encrypt:

    ...
    for(int i=0;i<*len/AES_BLOCK_SIZE+1;++i)
    ...
    

    and then at the for loop in Encryption::Decrypt:

    ...
    for(int i=0;i<*len/AES_BLOCK_SIZE;++i){
    ...
    

    only one of them can be correct (either it's +1 or not). So Encryption::Encrypt will overshoot (encrypt unsigned chars beyond the plaintext) which means that the malloc() call unsigned char *ciphertext = (unsigned char*)malloc(c_len); will not allocate enough unsigned chars :-(

    One way around this problem is to pad the plaintext from the file with extra null bytes so that the length of the plaintext is always a multiple of AES_BLOCK_SIZE and change the first for loop to:

    ...
    for(int i=0;i<*len/AES_BLOCK_SIZE;++i)
    ...
    


  • @hskoglund I have a question :

    Why does being ciphertext bigger than plaintext matter ?
    aren't they different only because of the padding and when I am calling EVP_EncryptUpdate with some plaintext bytes they will result in a ciphertext bytes of the same size which are of AES_BLOCK_SIZE size? or am I wrong ?



  • @hskoglund I think the problem is indeed in those numbers but I don't know how to tweak them because of the default padding maybe I can fix it by using manual padding like you suggested but I really don't wanna do that this task is for a company I interviewed for and they work in cyber security and I was given this task as a test so I think they would like me to know how to use the default padding.


  • Lifetime Qt Champion

    @Mohammedbie said in Qt with OpenSSL AES 256 CBC Encryption:

    EVP_EncryptUpdate

    You should not use fixed size like you are doing. You should read the file you want to encrypt one block after the other. The block might be at most AES_BLOCK_SIZE but could be less because the last one will likely be smaller.


Log in to reply
 

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