Java AES cipher text size

2019-03-17 00:09发布

I'm using a very standard way of Java AES encryption / decryption.

byte[] key = hexStringToByteArray("C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF");

byte[] message = hexStringToByteArray("01A0A1A2A3A4A5A6A703020100060001");

SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

byte[] encrypted = cipher.doFinal(message);

cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] original = cipher.doFinal(encrypted);

As you can see I'm using 128 bit key, and 128 bit message. I always pretty much get the result I expected, however, the encrypted result is always 256 bit long. The second 128 bits being always the same. Other than truncating the result, how can I ensure that the cipher returns only the first 128 bits, without changing the first 128 bits? I feel like I've got kind of mixed up with the definition of block size here.

2条回答
混吃等死
2楼-- · 2019-03-17 00:41

Your cipher instance is using PKCS5Padding padding, which adds up to 16 bytes of padding to the ciphertext. There are a couple of ways to correct this:

Option 1: Instead of using Cipher.getInstance("AES"), which uses padding, use Cipher.getInstance("AES/CBC/NoPadding"). This is NOT recommended, however, as it requires that the plaintext be a multiple of 16 bytes.

Option 2: Use BouncyCastle as your crypto provider, and then use

import org.bouncycastle.jce.provider.BouncyCastleProvider;
Cipher.getInstance("AES/CTR/NoPadding", new BouncyCastleProvider());

to initialize the cipher. This uses Counter mode (CTR) instead of Cipher Block Chaining mode (CBC), and Counter mode does not require any padding. With Counter mode it is important that you use a unique initialization vector, which can be transmitted in plaintext along with the ciphertext; for example,

byte[] IV = new byte[16];
new SecureRandom().getBytes(IV);
cipher.init(Cipher.ENCRYPT_MODE, key, IV);

Then when decrypting the ciphertext, initialize the cipher with the same initialization vector. It is up to you how you transmit the IV, but again, it does not need to be kept secret.

The initialization vector for Cipher Block Chaining mode should also be unique, but this is not as critical as it is for Counter mode.

查看更多
Luminary・发光体
3楼-- · 2019-03-17 01:01

There are a couple of mandatory things to get out of the way before approaching your question. One thing that I see in this code that is potentially dangerous is that the cipher isn't specified with explicit mode and padding. This means it is up to the provider defaults. If this is the provider currently distributed with Oracle's JVM, those defaults are ECB and PKCS5Padding. If those are what you want to use then specify them this way:

 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

The second is that ECB isn't a good choice of modes to use because it isn't very secure. CBC is a much better option since it makes use of an initialization vector.

On to the question. The reason for the size of the encrypted text is due to the padding scheme, in this case PKCS5. The padding is necessary to ensure that the plain text is of a length that the algorithm can handle. For AES, it must be a multiple of 16 bytes. In the case where the unencrypted data's length is already a multiple of 16 bytes, the padding must add an additional 16 bytes (see jbtule's comment here). Initializing the Cipher like this produces 16 bytes of encrypted data:

Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");

This requires that the unencrypted data is already a multiple of 16 bytes in length since it won't pad it at all. If it isn't, an exception is thrown.

It might be beneficial to understand exactly what you are trying to do to give a good recommendation.

查看更多
登录 后发表回答