1

I am trying to encrypt a 23 MB file using AES algorithm in Android. While the code works when the file size is around 3-4 MB. But when I tested with a 23 MB file it gives me a java.lang.OutOfMemoryError

Here is the code-

                 try{
                        SecretKeySpec skey = new SecretKeySpec(Hex.decodeHex(key.toCharArray()), "AES");
                        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                        cipher.init(Cipher.ENCRYPT_MODE, skey);
                        output = cipher.doFinal(bFile);
                        String SD_CARD_PATH = Environment.getExternalStorageDirectory().toString();
                        FileOutputStream fileOuputStream = new FileOutputStream(SD_CARD_PATH+ "/" + "abcd.db"); 
                        fileOuputStream.write(output);
                        fileOuputStream.close();
                        //System.out.println(output);
                    }catch(Exception e){
                        System.out.println("Error: "+e);
                    }

I get this error in the line- output = cipher.doFinal(bFile); Is there any other way to do this? What should I do?

user2510555
  • 967
  • 2
  • 8
  • 15
  • By streaming the result into the file instead of generating a huge result first, the writing it into the file: http://stackoverflow.com/questions/15470222/cipherinputstream-and-cipheroutputstream-are-not-generating-files – zapl Jan 22 '14 at 11:14
  • Don't keep all the bytes in memory at once. – Henry Jan 22 '14 at 11:14
  • You very likely do NOT want to be using ECB mode. http://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption – leishman Aug 03 '16 at 03:42

2 Answers2

3

You are having an OOME because bFile is an array, so you probably have the whole input file in memory. output is another byte array, and thus you are also holding the whole output file in RAM. And as you know the Java heap of an Android app has a max size (depends of the device, but a common min value is 16MB).

This is one of the major drawbacks of the "array approach". The other one being padding issues related to the size of array buffers. Unfortunately, most code examples returned by search engines show the array approach, and as a result there are plenty of questions in SO about the same problems.

You should be aware that there's an alternative stream approach. Turns out it is easier and safer. It involves using CipherInputStream (for decrypting) and CipherOutputStream for encrypting. Example:

    InputStream is = new FileInputStream(...); //Input stream

    SecretKeySpec skey = new SecretKeySpec(Hex.decodeHex(key.toCharArray()), "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skey); 
    FileOutputStream fileOuputStream = new FileOutputStream(SD_CARD_PATH+ "/" + "abcd.db"); 
    CipherOutputStream cos = new CipherOutputStream(fileOuputStream, cipher);

    //Now read from input and write to output using your favorite utilities library
    //Guava and Apache Commons IO are good examples.
    FooUtils.copy(is, cos);
    //Remember to close streams if the previous call didn't (preferably in a finally block)
Mister Smith
  • 27,417
  • 21
  • 110
  • 193
  • Hi @MisterSmith, I'm new to encryption/decryption algorithms but now I am facing OOM errors when decrypting a file in a project already written, I know OOM is caused by the ByteArray utilised but I don't understand how I can use `CipherInputStream` when decrypting a file. It would be really helpful if you could post an example – Cliff Burton Nov 18 '19 at 16:44
0

You are encrypting the whole file in memory and then writing it. What you should do is writing it little by little, in a multiple part operation.

I think you should look at: Java 256-bit AES Password-Based Encryption

take a look at the second answer. The method

public void WriteEncryptedFile (File input, File output)

should do exactly what you need to do. Hope this helps.

Community
  • 1
  • 1
Arnaud Potier
  • 1,750
  • 16
  • 28