0

I am trying to use 5-arg update method in java.crypto.Cipher, to encrypt/decrypt a bunch of input data, that comes in slices. Since data to be encrypted is at particular positions in array, and I want to overwrite encrypted data with decrypted form, hence the reason for using 5-arg update (defined as update(src, srcOffset, length, dst, dstOffset)).

However, I encounter the problem that after decrypting, the output is garbled, for 32 bytes, when using AES/CBC/PKCS5PADDING algorithm. If I switch from using 5-arg update to creating a dedicated array for holding the output, and then doing System.arraycopy, the output is correct.

Here is a simple test program:

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class CryptoTest
{
  private class Slice
  {
    byte[] array;

    int offset;

    int size;
  }

  private Slice[] createSlices(int howMany, int whatSize)
  {
    Slice[] slices = new Slice[howMany];
    for (int i = 0; i < howMany; i++)
    {
        slices[i] = new Slice();
        slices[i].array = new byte[whatSize];
        slices[i].offset = 0;
        slices[i].size = 0;
    }

    return slices;
  }

  public void testWithOffsets() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, ShortBufferException,
                IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException
  {
    int num = 10;
    int size = 32784;
    Slice[] slices = createSlices(num, size);

    // Create the keys and parameters
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    SecretKey key = keyGenerator.generateKey();

    SecretKey encryptionKey = new SecretKeySpec(key.getEncoded(), "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");

    cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);

    // save IV
    byte iv[] = cipher.getIV();

    // setup input data
    for (int i = 0; i < num; i++)
    {
        slices[i].offset = 16;
        slices[i].size = 32768;
    }
    slices[num - 1].size = 5424;

    // calculate a sum of input data, for comparisson later
    Long sum = 0L;
    for (int i = 0; i < num; i++)
        for (int j = slices[i].offset; j < slices[i].offset + slices[i].size; j++)
            sum = sum + slices[i].array[j];

    // encrypt
    int finalSize = 0;
    for (int i = 0; i < num; i++)
    {
        if (i == num - 1)
            finalSize = cipher.doFinal(slices[i].array, slices[i].offset, slices[i].size, slices[i].array, slices[i].offset);
        else
            finalSize = cipher.update(slices[i].array, slices[i].offset, slices[i].size, slices[i].array, slices[i].offset);
        System.err.println("Original size is " + slices[i].size + " final size is " + finalSize);
        slices[i].size = finalSize;
    }

    // decrypt
    SecretKey decryptionKey = new SecretKeySpec(key.getEncoded(), "AES");
    Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");

    IvParameterSpec ips = new IvParameterSpec(iv);
    decipher.init(Cipher.DECRYPT_MODE, decryptionKey, ips);

    for (int i = 0; i < num; i++)
    {
    /* ** */
        if (i == num - 1)
            finalSize = decipher.doFinal(slices[i].array, slices[i].offset, slices[i].size, slices[i].array, slices[i].offset);
        else
            finalSize = decipher.update(slices[i].array, slices[i].offset, slices[i].size, slices[i].array, slices[i].offset);
        System.err.println("Original size is " + slices[i].size + " final size is " + finalSize);
        slices[i].size = finalSize;
    }

    // sum of output data, should be the same as the input data
    Long newsum = 0L;
    for (int i = 0; i < num; i++)
        for (int j = slices[i].offset; j < slices[i].offset + slices[i].size; j++)
            newsum = newsum + slices[i].array[j];

    System.err.println("newsum " + newsum + " oldsum " + sum);
    assert newsum == sum;
  }

  public static void main(String[] args) throws Exception
  {
    CryptoTest ct = new CryptoTest();

    ct.testWithOffsets();
    ct.testWithoutOffsets();
  }
}

Expected output is 'newsum 0 oldsum 0', but I don't get that one. If I replace the body of the loop marked by ** with the following, I get the expected output:

        byte[] output = new byte[slices[i].array.length];

        if (i == num - 1)
            finalSize = decipher.doFinal(slices[i].array, 0, slices[i].size, output);
        else
            finalSize = decipher.update(slices[i].array, 0, slices[i].size, output);
        System.err.println("Original size is " + slices[i].size + " final size is " + finalSize);
        slices[i].size = finalSize;
        System.arraycopy(output, 0, slices[i].array, 0, finalSize);

Hence, my question is what I'm doing wrong here? I expect both forms to work correctly.

Nikola Knezevic
  • 789
  • 5
  • 20
  • jel ti radi bez updateovanja? jel mozes da odradis osnovni byte[] == decrypt(crypt(byte[])) test? – Shark Apr 02 '13 at 13:26
  • 1
    My guess is that, even though that method supports using the same array, some algorithms may read/write the array in blocks, and you won't be able to write back to the exact same spot in the array as it's read some part of a block when it starts overwriting another part of the same block. – Rob I Apr 02 '13 at 13:29
  • @Shark I don't understand (not because you write in Serbian :))? CBC is a block cipher, that's why byte-per-byte won't work. – Nikola Knezevic Apr 02 '13 at 13:35
  • What I mean is, are you sure you decrypt the data you crypted when you're not using update()? See http://stackoverflow.com/questions/12198228/not-decrypting-what-i-crypted not a shameless self-plug really :) – Shark Apr 02 '13 at 13:37
  • @RobI, I think you're right, that might be the cause. What I'm seeing when inspecting the input and the output is that first 32 bytes of second, third, ... slice differ between the original and the decrypted form. – Nikola Knezevic Apr 02 '13 at 13:49
  • @Shark, ya, I'm pretty sure I'm decrypting the crypted data. Have a look at the code, all `byte[]` arrays, all the same offsets. And as I replied to @rob-i, only first 32 bytes differ. – Nikola Knezevic Apr 02 '13 at 13:52

0 Answers0