The CTR mode in bouncy castle does not allow one to do partial block encryption. So if you want to encrypt 4 bytes you need to pad it to the full 16 bytes. It will always want to do a full block.
This also means that you can't do a partial offset into the CTR. You can increment the counter correctly, but you can't start encrypting from within a partial block.
Lets say you need to encrypt 17 bytes and write them to a file, which later can be opened in append mode to append more data to it using AES-128/CTR-BE mode:
1234567890ABCDEF\0
C-style terminated string.
That is 2 blocks in CTR mode (1 full block used, 1 partial block (1 byte out of a 16 byte block)).
The next time you open the file you want to append data to it, what you would do is this:
1. Load your previous key, and counter into AES-128/CTR
2. Increment the counter by the full blocks already used
3. Update the location into the current block by encrypting a random byte
4. Provide the rest of your plaintext which will now be correctly encrypted for appending to an already encrypted segment
Now you can read your file back using the following:
1. the AES key, counter for the start of the file
2. Use CTR to "decrypt" the content from start to finish
So that would look something like this in Java code with JCE:
Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aeskey, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
c.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
I've left out some stuff like creating the iv (really the counter), so now if we wanted to advance into the first block what we could do (and this works with JCE) is this:
c.update(new byte[count]);
Where count contains the amount we want to offset into the next block.
On JCE this does exactly what I described above, it moves forward "count" characters into the next block and then you can do:
CipherOutputStream cipher_out = new CipherOutputStream(output, c);
where "output" is DataOutputStream(new FileOutputStream("filename", true)) which is a file opened in append mode. Now you can write stuff to the file using the normal functions used by an OutputStream. This will then correctly append your new data to the end of the file so that if you start reading at the beginning of the file you can read through the end and get valid data. (We are using this to encrypt log files that are opened in Append only mode).
Where BouncyCastle breaks down is that you can not use c.update() to move forward into the block, thus appending is not possible because if you don't end on a block boundary your next write is going to have overlap. The only thing you can do in c.update() is provide it the amount of bytes that is the same as the block size. So you have to pad all input into c.update() to 16 bytes. Basically instead of being a stream cipher that allows seeking it has become yet another block cipher.
This also means that you can't do a partial offset into the CTR. You can increment the counter correctly, but you can't start encrypting from within a partial block.
Lets say you need to encrypt 17 bytes and write them to a file, which later can be opened in append mode to append more data to it using AES-128/CTR-BE mode:
C-style terminated string.That is 2 blocks in CTR mode (1 full block used, 1 partial block (1 byte out of a 16 byte block)).
The next time you open the file you want to append data to it, what you would do is this:
Now you can read your file back using the following: So that would look something like this in Java code with JCE: I've left out some stuff like creating the iv (really the counter), so now if we wanted to advance into the first block what we could do (and this works with JCE) is this: Where count contains the amount we want to offset into the next block.On JCE this does exactly what I described above, it moves forward "count" characters into the next block and then you can do:
where "output" is DataOutputStream(new FileOutputStream("filename", true)) which is a file opened in append mode. Now you can write stuff to the file using the normal functions used by an OutputStream. This will then correctly append your new data to the end of the file so that if you start reading at the beginning of the file you can read through the end and get valid data. (We are using this to encrypt log files that are opened in Append only mode).Where BouncyCastle breaks down is that you can not use c.update() to move forward into the block, thus appending is not possible because if you don't end on a block boundary your next write is going to have overlap. The only thing you can do in c.update() is provide it the amount of bytes that is the same as the block size. So you have to pad all input into c.update() to 16 bytes. Basically instead of being a stream cipher that allows seeking it has become yet another block cipher.