3

I have been following the tutorial "How do I encrypt and decrypt files using DES?" to add a simple file encryption to an existing Android 4+ app.

Everything works fine, except that the encryption and decryption is very, very slow.

The following two methods are pretty much the complete tutorial:

public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {
    DESKeySpec dks = new DESKeySpec(key.getBytes());
    SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
    SecretKey desKey = skf.generateSecret(dks);
    Cipher cipher = Cipher.getInstance("DES"); // DES/ECB/PKCS5Padding for SunJCE

    if (mode == Cipher.ENCRYPT_MODE) {
        cipher.init(Cipher.ENCRYPT_MODE, desKey);
        CipherInputStream cis = new CipherInputStream(is, cipher);
        doCopy(cis, os);
    } else if (mode == Cipher.DECRYPT_MODE) {
        cipher.init(Cipher.DECRYPT_MODE, desKey);
        CipherOutputStream cos = new CipherOutputStream(os, cipher);
        doCopy(is, cos);
    }
}

public static void doCopy(InputStream is, OutputStream os) throws IOException {
    byte[] bytes = new byte[64];
    int numBytes;

    // Why is this taking so long?
    while ((numBytes = is.read(bytes)) != -1) {
        os.write(bytes, 0, numBytes);
    }

    os.flush();
    os.close();
    is.close();
}

Quite simple and basic, but it takes about 20-30 seconds to de/encrypt a 1 MB file. In detail it is the while-loop that copies the bytes between the two streams which is so slow.

Changing the size of the byte[] to a bigger value like 65536 to read more bytes at once does not change anything. I thought reading more bytes at once would speed up the process but this is not the case.

Copying data between "normal" streams without encryption does not take so long. Is it really the encryption that is this expensive? I have been using similar encryptions on other plattforms and such delays have never been a problem.

The tutorial uses DES but changing the algorithm to something different, e.g. AES does not change anything either.

Any ideas how I can speed this up?

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
Andrei Herford
  • 17,570
  • 19
  • 91
  • 225
  • What happens if you cut out the cryptography and just use `doCopy(is, os)`? – CodesInChaos Sep 01 '14 at 14:23
  • If I use `doCopy(is, os)` were `is` is a simple (no cipher) `FileInputStream` and `os` a simple (no cipher) `FileOutputStream` the complete copy of my current test file takes about 150ms. If a CipherStream is included the operation with the same file takes up to 15000ms. Thus it is obvious that the delay is caused by the CipherStream. But why? Of encryption/decryption needs time, but this many? – Andrei Herford Sep 01 '14 at 14:57
  • A proper crypto implementation should be at least 100x times faster than that, even on a mobile CPU. – CodesInChaos Sep 01 '14 at 15:00
  • This is what I do not understand... Am I doing something wrong? The code uses only the build in encryption features. Is there a better/faster way to encrypt files? – Andrei Herford Sep 01 '14 at 15:02
  • What runtime are you on? Are you using the simulator? What is returned if you print out `Cipher.getProvider()`? – Maarten Bodewes Sep 01 '14 at 17:54
  • 1
    God I hate these awful cryptography tutorials. The tutorial uses defaults for the mode and padding. That is a bug. Always completely specify the cipher transform, e.g. `Cipher.getInstance("DES/CBC/PKCS5PADDING");`. Of course that probably isn't the problem here but I just can't overlook it. – President James K. Polk Sep 01 '14 at 18:04
  • @GregS well, if you combine this DES one with the one that uses SecureRandom to generate the key from a password, you may actually get your data back :P – Maarten Bodewes Sep 01 '14 at 18:50
  • [android](http://stackoverflow.com/questions/24650619/slow-image-encryption-decryption-on-android) [crypto](http://stackoverflow.com/questions/24569286/android-file-decrytion-and-encrytion-taking-time) [is](http://stackoverflow.com/questions/7282930/android-slow-aes-decryption) [slow](http://stackoverflow.com/questions/6257945/aes-decryption-on-android-too-slow-to-be-usable-will-ndk-be-faster-other-ideas), there is really nothing to be done about it on the user side, also check similar questions linked to these. – Oleg Estekhin Sep 02 '14 at 04:59
  • I ran the code on a real device but while the debugger was connected. It seems that slows down the process. If the app is started directly (without eclipse) the de/encryption is as fast as expected. I do not really understand why the debuggers slows down the process this much but it is good to have the problem solved :-) Using BufferedStreams gives some extra speed! – Andrei Herford Sep 02 '14 at 06:22

2 Answers2

3

I ran some experiments on my Google LG Nexus 5 running Android 4.4.4 using basically your code and encrypting a file of 1000000 (one million) bytes, reading and writing to/from the /sdcard filesystem.

no crypt 1421 ms
AES ECB mode 2577 ms
DES ECB 3214

Next, I modified your code slightly to use BufferedInputStream and BufferedOutputStream

no crypt 88 ms
AES ECB mode 855 ms
DES ECB mode 1419 MS

This shows that the timings are sensitive to buffering, and that AES is faster than DES.

The provider name is 'AndroidOpenSSL'

President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
  • Thank you very much for your answer. I ran the code on a real device but while the debugger was connected. It seems that slows down the process. If the app is started directly (without eclipse) the de/encryption is as fast as expected. I do not really understand why the debuggers slows down the process this much but it is good to have the problem solved :-) Using BufferedStreams gives some extra speed! – Andrei Herford Sep 02 '14 at 06:21
1

You might have a problem some process blocking the input file. To test if the issue is this : Calculate the last part of the file that should be read and the count of full buffers that you will read. Replace the while with a for loop and add an additional read and write if there is remainder from filesize/buffer size.

Nejat76
  • 36
  • 2