Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. 3rd Party Software
  4. Qt with OpenSSL AES 256 CBC Encryption
QtWS25 Last Chance

Qt with OpenSSL AES 256 CBC Encryption

Scheduled Pinned Locked Moved Unsolved 3rd Party Software
18 Posts 3 Posters 12.0k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Mohammedbie
    wrote on last edited by
    #9

    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.

    1 Reply Last reply
    0
    • M Offline
      M Offline
      Mohammedbie
      wrote on last edited by
      #10

      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.

      1 Reply Last reply
      0
      • hskoglundH Online
        hskoglundH Online
        hskoglund
        wrote on last edited by
        #11

        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.

        M 1 Reply Last reply
        3
        • hskoglundH hskoglund

          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.

          M Offline
          M Offline
          Mohammedbie
          wrote on last edited by
          #12

          @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.

          1 Reply Last reply
          0
          • hskoglundH Online
            hskoglundH Online
            hskoglund
            wrote on last edited by
            #13

            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.

            1 Reply Last reply
            3
            • M Offline
              M Offline
              Mohammedbie
              wrote on last edited by Mohammedbie
              #14

              @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;
              }
              
              1 Reply Last reply
              0
              • hskoglundH Online
                hskoglundH Online
                hskoglund
                wrote on last edited by hskoglund
                #15

                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)
                ...
                
                1 Reply Last reply
                1
                • M Offline
                  M Offline
                  Mohammedbie
                  wrote on last edited by
                  #16

                  @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 ?

                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    Mohammedbie
                    wrote on last edited by
                    #17

                    @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.

                    1 Reply Last reply
                    0
                    • SGaistS Offline
                      SGaistS Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on last edited by
                      #18

                      @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.

                      Interested in AI ? www.idiap.ch
                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                      1 Reply Last reply
                      2

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved