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.