Java AES 密文大小

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15880901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 21:10:34  来源:igfitidea点击:

Java AES cipher text size

javacryptographyaesblockencryption

提问by Peter Jaloveczki

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

我正在使用一种非常标准的 Java AES 加密/解密方式。

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.

如您所见,我使用的是 128 位密钥和 128 位消息。我总是得到我预期的结果,但是,加密的结果总是 256 位长。第二个 128 位总是相同的。除了截断结果之外,如何确保密码仅返回前 128 位,而不更改前 128 位?我觉得我有点混淆了这里块大小的定义。

回答by laz

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 ECBand PKCS5Padding. If those are what you want to use then specify them this way:

在解决您的问题之前,有一些强制性的事情需要解决。我在这段代码中看到的有潜在危险的一件事是密码没有用显式模式和填充指定。这意味着它取决于提供者的默认设置。如果这是当前随 Oracle JVM 分发的提供程序,则这些默认值是ECBPKCS5Padding。如果这些是您想要使用的,那么以这种方式指定它们:

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

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

第二个是这ECB不是一个好的使用模式选择,因为它不是很安全。CBC是一个更好的选择,因为它使用了初始化向量。

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 Cipherlike this produces 16 bytes of encrypted data:

进入问题。加密文本大小的原因是由于填充方案,在这种情况下PKCS5。填充是必要的,以确保纯文本具有算法可以处理的长度。对于 AES,它必须是 16 字节的倍数。在未加密数据的长度已经是 16 字节的倍数的情况下,填充必须添加额外的 16 字节(请参阅 jbtule 的评论here)。Cipher像这样初始化会产生 16 字节的加密数据:

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.

这要求未加密数据的长度已经是 16 字节的倍数,因为它根本不会填充它。如果不是,则抛出异常。

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

准确了解您正在尝试做什么以提供好的建议可能会有所帮助。

回答by Zim-Zam O'Pootertoot

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:

您的密码实例正在使用 PKCS5Padding 填充,它将最多 16 个字节的填充添加到密文中。有几种方法可以纠正这个问题:

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.

选项 1:不使用使用填充的 Cipher.getInstance("AES"),而是使用 Cipher.getInstance("AES/CBC/NoPadding")。但是,不建议这样做,因为它要求明文是 16 字节的倍数。

Option 2: Use BouncyCastleas your crypto provider, and then use

选项 2:使用BouncyCastle作为您的加密货币提供商,然后使用

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,

初始化密码。这使用计数器模式 (CTR) 而不是密码块链接模式 (CBC),并且计数器模式不需要任何填充。在计数器模式下,使用唯一的初始化向量很重要,它可以与密文一起以明文传输;例如,

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.

然后在解密密文时,用相同的初始化向量初始化密码。如何传输 IV 取决于您,但同样,它不需要保密。

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

密码块链接模式的初始化向量也应该是唯一的,但这不像计数器模式那么重要。