Why PKS5Padding alters 128 bytes string in Java - java

I have implemented an AES (CBC) algorithm in Java crypto with PKS5Padding. I noticed that for a input string size of 128 bytes; size of the cyphered text is 144 bytes. Is this normal? For my understanding AES algo should not pad this string since it is a multiplication of 16.

PKCS#7/PKCS#5 padding is always applied. Check the Wikipedia page. If your 16 byte aligned data would end with a byte 01 and you would remove the padding, then you would have one less byte of plaintext. So for AES 1 to 16 bytes are added, and having a multiple of the block size is therefore the worst situation instead of the best.

Related

why would cipher.getOutputSize() return value higher than required?

I'm trying to decrypt a file encrypted with AES256 CBC, and here's my code to do that:
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
cipher.init(false, new ParametersWithIV(new KeyParameter(key), iv));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
It's quite simple, however cipher.getOutputSize(data.length) would always return a value which is larger than needed. I always end up with some weird characters at the end of the buffer.
I have tried to decrypt two html files, and here's the end of them:
</HTML>����������������
// Processed: 9304 Size: 9312
and
</body></html>��������
// Processed: 636 Buffer size: 640
So the additional bytes size is not consistent, I cannot just decrement 'by some'.
What am I doing wrong when calculating the size of the output array?
It's relevant to padding.
AES encrypts block by block, and every block must be 128 bits long. Passing in some data that is not divisible into blocks of 128 bits means that the final block of plaintext must be padded until it is 128 bits in size.
getOutputSize is returning you the the size of the ciphertext should you encrypt. Remember that the plaintext is padded and then encrypted, so the input size to output size is the same. You can see this because 636 mod 16 = 12 and 636 - 12 + 16 = 640. E.g. it is rounding up to the nearest multiple of 16 because this is how many extra padding bytes were included before encryption.
Since you are using this while decrypting, the array is larger than required because you are allocating the bytes that would be required. getOutputSize can be used for both encryption and decryption provided you are aware that it is a "worst-case" scenario for buffer allocation.
TL;DR: getOutputSize() lets you know what sized buffer to allocate (a worst case), return values from processBytes/doFinal tell you how much of the buffer was actually used (exact).
You are using PaddedBufferedBlockCipher for decryption (above code defaults to PKCS7 padding). getOutputSize() cannot determine the exact amount of output plaintext until it actually sees the decrypted data for the final block, so the returned value will be an upper bound, which is why the return value is there for processBytes/doFinal to let you know how much was actually output.
For simplicity, PaddedBufferedBlockCipher also ignores the details of the particular padding scheme used - it assumes only that the final block could contain some amount of padding that will be removed.
The unused bytes at the end of 'outBuf' are not written to by the cipher. In particular, they will not contain the padding. Presumably the "weird characters" are zero bytes from the array initialisation.

Wrong cipher text length when using 3DESede with PKS5 padding of javax.crypto

I am using the following code in my android application to encrypt a string in Triple DES using the Encrypted Code Book (ECB) mode with three independent keys (aka 3DESede), which are provided as a 24 byte sized key array. Therefore I use the Java Crypto API. This works pretty well, but if I encrypt an eight character string I get a 16 byte cipher text, which should not happen as 3DES operates on chunks of 64 bit (resp. 8 byte). Same holds for the PKCS5 padding as this also operates on chunks of 64 bit. So my question is what causes this problem?
private static byte[] encryptText(String plaintext, byte[] keyBytes) throws Exception {
// Get plaintext as ASCII byte array
final byte[] plainBytes;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
plainBytes = plaintext.getBytes(StandardCharsets.US_ASCII);
} else {
plainBytes = plaintext.getBytes("US-ASCII");
}
// Generate triple DES key from byte array
final DESedeKeySpec keySpec = new DESedeKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey key = keyFactory.generateSecret(keySpec);
// Setup the cipher
final Cipher c3des = Cipher.getInstance("DESede/ECB/PKCS5Padding");
c3des.init(Cipher.ENCRYPT_MODE, key);
// Return ciphertext
return c3des.doFinal(plainBytes);
}
PKCS5Padding adds 1-8 bytes of padding when used with DES. If you encrypt 8 bytes you will get 8 additional bytes of padding to get to an even number of blocks.
If you used Cipher.getInstance("DES/ECB/NoPadding") and encrypted 8 bytes, you will get 8 bytes of cipher text.
When PKCS#5 padding is used it must always add padding otherwise on decryption there would be no way to determine if padding was added. So, even if the input data is an exact multiple of the block size padding must be added and that will be 8 bytes.
See PKCS padding:
If the original data is an integer multiple of N bytes, then an extra block of bytes with value N is added. This is necessary so the deciphering algorithm can determine with certainty whether the last byte of the last block is a pad byte indicating the number of padding bytes added or part of the plaintext message. Consider a plaintext message that is an integer multiple of N bytes with the last byte of plaintext being 01. With no additional information, the deciphering algorithm will not be able to determine whether the last byte is a plaintext byte or a pad byte. However, by adding N bytes each of value N after the 01 plaintext byte, the deciphering algorithm can always treat the last byte as a pad byte and strip the appropriate number of pad bytes off the end of the ciphertext; said number of bytes to be stripped based on the value of the last byte.
PKCS#5 padding is identical to PKCS#7 padding, except that it has only been defined for block ciphers that use a 64-bit (8 byte) block size. In practice the two can be used interchangeably.

Bouncy Castle PKCS7 padding

I need help to find what is actually a padding value for this expression in Bouncy Castle java framework. Kinda not sure about the values.
encrypt(bytes, iv, secret, "AES/CBC/PKCS7Padding")
If the original data is an integer multiple of N bytes, then an extra block of bytes with value N is added. This is necessary so the deciphering algorithm can determine with certainty whether the last byte of the last block is a pad byte indicating the number of padding bytes added or part of the plaintext message. Consider a plaintext message that is an integer multiple of N bytes with the last byte of plaintext being 01. With no additional information, the deciphering algorithm will not be able to determine whether the last byte is a plaintext byte or a pad byte. However, by adding N bytes each of value N after the 01 plaintext byte, the deciphering algorithm can always treat the last byte as a pad byte and strip the appropriate number of pad bytes off the end of the ciphertext; said number of bytes to be stripped based on the value of the last byte.
The padding byte used is the same as the number of padding bytes. Thus, if 11 bytes of padding are needed then that padding consists of 11 copies of the byte 0x0b. Padding is always applied, so that if the number of bytes is already a multiple of 16 then 16 bytes of padding are used, the pad byte being 0x10 in that case.
This padding scheme has the nice feature that, upon decryption, you can examine the last byte of the padded plaintext to determine how many padding bytes there are in total.
Note that PKCS7 padding is not a java-specific standard but rather a very common padding scheme independent of language.

What size of initialization vector needed for AES-256 encryption in java?

I am using AES-256 encryption with CFB mode. I have to use 32 byte key.
But I am little bit confused about the initialization vector.
How many bytes of initialization vector should be used with AES-256 ?
Is it 16 bytes or 32 bytes ?
Any code example will be appreciated.
TL;DR: AES in CFB mode requires a unique (but not necessarily randomized) IV of 16 bytes.
The size of the IV depends on the mode, but typically it is the same size as the block size, which for AES is always 16 bytes. There are modes that differ from this, notably GCM mode which has a default size of 12 bytes but can take any sized IV - although keeping to the default is highly recommended.
The old school modes such as CBC and CFB however simply require an IV of the same size as the block size. Even CTR commonly requires 16 bytes, although in principle the IV can be any size less than 16, in which case it is (again, commonly) right padded with zero valued bytes. Note that CTR is often initialized with an initial counter value which means you must make sure that the counter is not repeated yourself.
The block size of AES is 16 bytes, whatever the key size. Saying that you have AES-256 and a key of 32 bytes is superfluous information. For AES-256 the key size must be 256 bits or 32 bytes.
The IV for CFB mode - as stated earlier - must always be 16 bytes as AES is a 128 bit block cipher. AES is restricted with regards to the block size compared with the Rijndael cipher. Rijndael may be configured with different block sizes.

AES Encryption from integers to integers

I am using AES encryption algorithm in java to encrypt my database values..My encryption function returns encrypted value as String but the columns of type "Int" fails to store such string values which is quite logical..Is there a way to encrypt the integers as integers (numerical values)? Thankyou.
Plain AES returns an array of bytes. You can store this as an array of bytes, a Base64 text string or as a BigInteger:
BigInteger myBigInt = new BigInteger(AESByteArray);
It is very unlikely that the 128 bit, or larger, AES result will fit into a 32 bit Java int.
If you want 32 bit input and 32 bit output, so everything fits into a Java int, then either write your own 32 bit Feistel cipher, or use Hasty Pudding Cipher, which can be set for any bit size you require.
Encrypting integer into integer is FPE (format preserving encryption). FPE does not change data type or data length.
Here is a reason why databases implementing FPE only for character data, never for int.
AES 128 will encrypt 128-bit block. Which is 16 bytes.
If you want to encrypt 64 or 32 bit integer(4 or 8 byte values), you still have to encrypt 16 byte block. This problem can be solved by adding 8 (or 12) bytes to int32 or int64 values. This creates issue - if added bytes are always 0, you create huge weakness in encryption, as your data set is severely limited. It can be used for brute force attack on AES etc. In turn, this can be solved by filing with cryptographically strong random number added 8 or 12 bytes (that also creates a weakness, as most likely your random genertor is not strong enough). When decrypting, you can purge extra added bytes and extract only 4 or 8 bytes our of 16 bytes.
Still, life is not perfect. AES encryption does not change size of block, it always produces 16 bytes. You can encrypt your int into 16 bytes, but database can store only 8 bytes for int.
Unless you will store data in binary(16) column. But that is not an integer, and you are asking for integer.
In theory, numeric(38) is taking 16 bytes. In some databases it is possible to set 16 bytes to arbitrary value and then extract. I have not seen it is implemented.
You can always encode your string in an integer, however it could be a large integer.
If you can't afford large integer, you can encode it in multiple small integers.
If you can afford neither large integer nor multiple integers, maybe you can't do it well anyway, using a block cipher in ECB mode is almost always a bad idea.
Try converting the output of the encryption from string to binary, and then from binary to a decimal integer.

Categories

Resources