0

I've following code to decrypt the files in Java which are encrypted via PHP mcrypt function.

private String iv = "MYKEYHERE";//Dummy iv (CHANGE IT!)
private String SecretKey = "MYKEYHERE";//Dummy secretKey (CHANGE IT!)

private byte[] decrypt(String code)
{
    byte[] decrypted = null;
    try {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
        SecretKeySpec keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
        if(code == null || code.length() == 0)
            throw new Exception("Empty string");

        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

        decrypted = cipher.doFinal(hexToBytes(code));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return decrypted;
}

private static byte[] hexToBytes(String str) {
    if (str==null) {
        return null;
    } else if (str.length() < 2) {
        return null;
    } else {
        int len = str.length() / 2;
        byte[] buffer = new byte[len];
        for (int i=0; i<len; i++) {
            try {
                buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
            } catch (NumberFormatException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return buffer;
    }
}

and I'm reading and writing the files from SDCARD as

String encyptedData = readFileFromSDCard(params[0]);

byte[] decryptedByteArray = decrypt(encyptedData);

File rootFile = new File(Constants.folioTempLocation+params[1]);
rootFile.mkdirs();

File outFile = new File(rootFile,  new File(params[0]).getName());
FileOutputStream out = new FileOutputStream(outFile);
//IOUtils.write(decryptedByteArray, out);
out.write(decryptedByteArray);
out.flush();
out.close();

There is no problem with decryption and writing files back to SD_CARD. But I'm getting unknown characters at the end of each file which is restricting the whole decrypted file to work properly.

I'm attaching screenshot of unknown characters concatenated to end of string. I'm also attaching an encrypted_html_file for anyone want to test the code using this file.

Screenshot sample image

Nitin Misra
  • 4,472
  • 3
  • 34
  • 52
  • @Andrew T. thanks for edit, do you have idea what I'm doing wrong? – Nitin Misra Jul 15 '14 at 03:34
  • Actually I don't know since I haven't encountered it myself. If you test it on other files, is it always returning 5 NULL characters? – Andrew T. Jul 15 '14 at 03:38
  • @AndrewT. the `NUL` characters are arbitrary, it varies from 2 to 10 at the end of string. Notepad++ shows this as `NUL` characters but Notepad shows it as blank spaces. – Nitin Misra Jul 15 '14 at 03:40
  • It is a [null control character](http://en.wikipedia.org/wiki/Null_character), represent as 0x00 in byte. Since there is no problem with decryption, I suspect the encryption artifact. In any case, probably it can be fixed by removing trailing nulls after decrypting the file, before saving the file. – Andrew T. Jul 15 '14 at 03:53
  • @AndrewT. thanks for the research, can you post the answer how to remove trailing `NUL` coz I've also tried to trim `byte[]` from end but no luck. – Nitin Misra Jul 15 '14 at 03:55

2 Answers2

3

CBC is a block mode and must be used with proper padding scheme.

PHP's mcrypt uses zero padding - it appends \0 characters to the input data to make it length a multiple of block size. Obviously you get these zero bytes back when decryption.

The mycrypt page has an example of how to implement proper PKCS#7 padding scheme in the user comments. Among other mcrypt padding questions How to add/remove PKCS7 padding from an AES encrypted string? provides a similar example.

Community
  • 1
  • 1
Oleg Estekhin
  • 8,063
  • 5
  • 49
  • 52
0

Since I don't know the cause of the problem, I can only provide a work-around to remove the trailing nulls (NUL).

public static byte[] removeTrailingNulls(byte[] source) {
    int i = source.length;
    while (source[i - 1] == 0x00) {
        i--;
    }

    byte[] result = new byte[i];
    System.arraycopy(source, 0, result, 0, i);

    return result;
}

Try it just after decrypting the file

byte[] decryptedByteArray = removeTrailingNulls(decrypt(encyptedData));

Warning: This may affect the performance (especially on mobile device) since it uses nearly twice the memory usage.

This workaround is not perfect since it will always remove trailing nulls, regardless of original data. Use at your own risk

Andrew T.
  • 4,701
  • 8
  • 43
  • 62
  • Works like charm, but do i need to worry if i do the process in background thread in devices having primary memory more than 1GB. – Nitin Misra Jul 15 '14 at 04:24
  • The warning is more like a general warning for future viewers: not all devices have more than 1GB, and not all apps can use all of them ;) – Andrew T. Jul 15 '14 at 04:27
  • What if the real data has trailing zero bytes? Your code cannot distinguish between those zero bytes and padding bytes. – President James K. Polk Jul 15 '14 at 22:11
  • @Greg I know, and that's why I say "workaround", not the "proper way". Nonetheless, I will leave this answer here, and I have added another warning for that. Thanks for reminding me. – Andrew T. Jul 16 '14 at 13:42