0
votes

I am working on a class to encrypt/decrypt large files so I am trying to use the streams instead of byte arrays to avoid OutOfMemory exceptions. In the encryption method I add random salt and iv to the beginning of the encrypted file and it works fine, and here is the code:

public File encryptFile(File inputFile, File outPutFile, String password, ProgressBar progressBar, Label progressPercentage){
        //Create IV
        byte[] ivBytes = new byte[16];
        SecureRandom random1 = new SecureRandom();
        random1.nextBytes(ivBytes);
        IvParameterSpec iv = new IvParameterSpec(ivBytes);


        //Create the key with the salt
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[SALT_SIZE];
        random.nextBytes(salt);

        SecretKeySpec keySpec = generateAesKey(password, salt);

        //Create and Init the cipher
        Cipher c = Cipher.getInstance("AES/CBC/"+padding);
        c.init(Cipher.ENCRYPT_MODE, keySpec, iv);

        byte[] buf = new byte[8192];
        FileInputStream in = new FileInputStream(inputFile);
        FileOutputStream out = new FileOutputStream(outPutFile);
        int nread;
        int progress = 0;

        byte[] ivAndSalt = new byte[ivBytes.length + salt.length];
        System.arraycopy(ivBytes,0, ivAndSalt,0, ivBytes.length );
        System.arraycopy(salt, 0, ivAndSalt, ivBytes.length, salt.length);
        out.write(ivAndSalt);

        while((nread = in.read(buf)) != -1) {
            byte[] enc = c.update(buf, 0, nread);
            out.write(enc);
            progress++;
        }

Then I try to get the iv and salt in decryption method and then decrypt the rest of the file into an output one with FileInputStream.getChannel.position():

 public File decryptFile(File inputFile, File outPutFile, String password, ProgressBar progressBar, Label progressPercentage) {
        //Create and Init Cipher
        Cipher c = Cipher.getInstance("AES/CBC/" + padding);

        FileInputStream in = new FileInputStream(inputFile);
        FileOutputStream out = new FileOutputStream(outPutFile);

        //Getting the iv and salt
        byte[] ivBytes = new byte[16];
        byte[] salt = new byte[SALT_SIZE];
        byte[] ivAndSalt = new byte[ivBytes.length+SALT_SIZE];
        in.read(ivAndSalt, 0, ivBytes.length+SALT_SIZE);
        System.arraycopy(ivAndSalt, 0, ivBytes, 0, ivBytes.length);
        System.arraycopy(ivAndSalt, ivBytes.length, salt, 0, SALT_SIZE);
        IvParameterSpec iv =new IvParameterSpec(ivBytes);
        SecretKeySpec keySpec = generateAesKey(password, salt);
        c.init(Cipher.DECRYPT_MODE, keySpec, iv);


        in.getChannel().position(ivAndSalt.length);

        int nread;
        int progress = 0;
        byte[] buf = new byte[8192];

        while((nread = in.read(buf)) != -1) {
                byte[] enc = c.update(buf, 0, nread);
                out.write(enc);
                progress++;

            /*if (enc.length / 8192 != 0)
                    System.out.println((nread*progress) + "%");*/
        }
        System.out.println("Size of out before doFinal(): " + out.getChannel().size());
        byte[] enc = c.doFinal();
        out.write(enc);
        System.out.println("Size of out after doFinal(): " + out.getChannel().size());
        return  outPutFile;
    } 

I didn't get an error on calling decryptFile() but the produced file is corrupted and this means that there is an issue somewhere in the decrypting.

1
There is nothing obviously wrong with the algorithm, but the code is not structured well for debugging -- effectively it is doing too much for each of those single functions (IO, key derivation, looping through chunks, encoding or decoding). Break methods down to a few simple ones and write tests to narrow down the problem. E.g. do you know that writing and then reading the ivBytes + salt produces the values that were initially there? Write a test. Do you know if encrypting and then decrypting a single chunk of 8192 bytes produces result identical to input? Extract a function and write test.Oleg Sklyar
Also stream.read(buf) != -1 does not guarantee you read 8192 bytes.Oleg Sklyar
I'd just skip in.getChannel().position(ivAndSalt.length); though otherwise nothing obvious.. If the decryption woudn't be ok, you'd get an exception..gusto2
I tried to comprise the iv and salt and it was fine, I am sure it is something with skipping the salt and iv bytes in the encrypted file. Is there any advice to avoid that?user10222681
@OlegSklyar so how to guarantee that?user10222681

1 Answers

0
votes

The problem was totally something silly, I forgot to close the input and output streams after doFinal() and writing the enc bytes array to output:

in.close();
out.close();