AES中IV长度不正确的解决方案

9

我正在尝试在Java中实现AES加密,以下是我使用的代码:

 byte[] sessionKey = {00000000000000000000000000000000};
 byte[] iv = {00000000000000000000000000000000};
 byte[] plaintext = "6a84867cd77e12ad07ea1be895c53fa3".getBytes();
 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sessionKey, "AES"), new IvParameterSpec(iv));
 byte[] ciphertext = cipher.doFinal(plaintext);

 cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sessionKey, "AES"), new IvParameterSpec(iv));
 byte[] deciphertext = cipher.doFinal(ciphertext);

我需要为测试目的修复此密钥和IV,但我遇到了以下异常:
Exception in thread "main"
java.security.InvalidAlgorithmParameterException: 
  Wrong IV length: must be 16 bytes long    at
com.sun.crypto.provider.SunJCE_h.a(DashoA12275)     at
com.sun.crypto.provider.AESCipher.engineInit(DashoA12275)   at
javax.crypto.Cipher.a(DashoA12275)  at
javax.crypto.Cipher.a(DashoA12275)  at
javax.crypto.Cipher.init(DashoA12275)   at
javax.crypto.Cipher.init(DashoA12275)

我该如何在这个AES实现中使用这个固定IV呢?有什么方法吗?


实际上,我并不困惑于这个错误,只是不确定我现在如何使用当前的IV。 - Shahed
6个回答

45

首先,

byte[] iv = {00000000000000000000000000000000};

创建一个大小为1的字节数组,而不是大小为32的字节数组(如果这是你的意图)。

其次,AES的IV大小应为16字节或128位(这是AES-128的块大小)。如果使用AES-256,则IV大小应为128位,因为AES标准仅允许128位块大小。原始的Rijndael算法允许其他块大小,包括256位长块大小。

第三,如果你打算使用AES-256,需要下载并安装JCE无限制权限策略文件(滚动页面到底部);我还建议阅读附带的许可证。

这将导致您的代码如下更改:

byte[] iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

最后,初始化向量应该是独特且不可预测的。用一个由16个0组成的字节序列作为 IV 的值,是不合适的选择。如果这是生产代码,请考虑寻求帮助。


IV的大小是基于块大小而不是密钥大小。 - Damien_The_Unbeliever
@Shahed,如果你一开始使用Rijndael算法,我怀疑你将无法符合FIPS标准,因此使用BouncyCastle没有意义;也许你需要使用AES本身,该算法在FIP-197中有规定(我在这里自言自语,对FIPS合规性需求一无所知,但我认为我是正确的)。 - Vineet Reynolds
2
使用十六进制解码器,例如Apache Commons Codec中的一个,来解码十六进制编码的值并获取字节数组。如果您有任何进一步的问题,建议您提出一个新问题,因为评论区不适合提问新问题。 - Vineet Reynolds
你能否请看一下我的问题?我似乎无法让它工作。http://stackoverflow.com/questions/34061675/convert-ios-encryption-to-android - MetaSnarf
如果IV大小基于块大小,那么为什么mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);返回32?它返回生成IV字节的字节大小吗? - user4271704
显示剩余10条评论

15

高级加密标准中得知:

该标准包含三种分组密码,分别为AES-128、AES-192和AES-256,这些密码最初来自于更大的一组密码——Rijndael。每个分组密码都有一个128位的分组大小,并分别具有128、192和256位的密钥大小。

(强调添加)

初始化向量中得知:

对于块密码的操作模式,IV通常与密码的块大小相同。

将这两个因素结合起来,你会发现IV对于AES始终为128位,而与密钥大小无关。


你能否请看一下我的问题?我似乎无法让它工作。http://stackoverflow.com/questions/34061675/convert-ios-encryption-to-android - MetaSnarf
如果IV大小基于块大小,那么为什么mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256,MCRYPT_MODE_CBC)返回32?它返回生成IV字节的字节大小吗? - user4271704
@user4271704 - RIJNDAEL是AES的基础,并确实使用不同的块大小。您需要决定您是否正在进行AES(始终为16字节)或RIJNDAEL。 - Damien_The_Unbeliever
我知道这一点,我想知道如何计算IV大小?我应该将块大小从位转换为字节以获取IV大小吗? - user4271704

2
我遇到这个问题的原因是因为我将IV作为字符串读取,并且以错误的方式将其转换为字节数组。
正确的方式:
Hex.decodeHex(initializationVector.toCharArray()

使用 org.apache.commons.codec.binary.Hex

错误的方式:

initializationVector.getBytes()

当你调用getBytes()时,它只取代表字符串的所有比特,并将其切割成字节,这就是为什么这是错误的原因。所以0最终被写成Unicode表中0的索引所组成的比特,而不是0,实际上是30,并且会写在2个字节上。
相反,你想要的是0被表示为字节00000000

唯一对我有效的方法..还必须这样做才能创建SecretKeySpec。 - undefined

2
为什么不使用类似这样的东西,而不是使用“魔数”:
SecureRandom random = new SecureRandom();
byte[] iv = random.generateSeed(16);

因此,您会得到16个随机字节作为您的iv。


2
没有必要手动生成IV。可以在加密后从Cipher中获取:cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); - Piohen
4
为了解密,您需要相同的初始向量(IV),因此使用随机IV无效,除非您保存了生成的IV。 - ademar111190

2
这里的AES可能是AES-128而不是AES-256。如果你想启用AES-256,你必须包含额外的jar文件,因为有出口管制政策。所以首先要检查一下。在大多数情况下,AES-128已经足够了。
当AES是128位时,你的IV不能超过128位,即16个字节。所以改变初始化向量的长度。
这样应该可以工作。另外,阅读这篇文章 http://en.wikipedia.org/wiki/Initialization_vector 警告:使用固定IV并不是一个好习惯。它必须是随机或伪随机的,才能提供更好的安全性。

请问您能否给我一些关于启用AES-256所需的JAR包的想法? - Shahed
可以从http://www.bouncycastle.org/java.html使用Bouncy castle的jar包,或者阅读此链接http://java.sun.com/developer/technicalArticles/Security/AES/AES_v1.html。Bouncy castle可能会对您有所帮助,但我对另一个链接表示怀疑。在阅读后尝试通过Google搜索该jar包。希望能帮到您。如果我的答案解决了您的问题,请接受它! :) - Andrew Anderson
你能否请看一下我的问题?我似乎无法让它工作。http://stackoverflow.com/questions/34061675/convert-ios-encryption-to-android - MetaSnarf

0

虽然与此无关,但我遇到了同样的问题。

IvParameterSpec ivParameterSpec = new IvParameterSpec(key1.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(key2.getBytes("UTF-8"),"AES"); 我的错误是key1和key2是长度大于16的字符串。将它们的长度更改为16即可解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接