Android加密API不会为AES生成安全IV

我正在使用javax.crypto在我的应用程序中执行一些加密操作。 我使用AES进行加密/解密,如下所示:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] cipherText = cipher.doFinal(plaintext); byte[] iv = cipher.getIV(); //The problematic IV 

在加密之后,生成的IV被预先附加到密文。

Java规范明确指出,如果未向cipher.init()提供IV,则必须自动生成IV:

如果此密码需要任何无法从给定密钥派生的算法参数,则底层密码实现应该自己生成所需的参数(使用特定于提供程序的默认值或随机值)

但有时我最终会得到一些似乎不是很随机的密文,比如这个(在base64中):

  AAAAAAAAAAAAAAAAAAAAAOmI9Qh1fMiG6HV3tKZK3q5sCruaPdYqYnoUOM00rs6YZY3EvecYfR6vTHzZqk7ugknR9ZMipedYWJB1YOLmSYg= 

前面的一堆A字符是IV。 IV实际上是16个零字节。

大多数情况下,库会生成适当的随机IV,但有时候,它会弹出零。 为什么会这样?

一些(大多数?)提供者只使用零字节填充IV作为其默认值。 我强调你的报价:

如果此密码需要任何无法从给定密钥派生的算法参数,则底层密码实现应该自己生成所需的参数(使用特定于提供程序的默认值或随机值)

当您查看密文的前面时,您会看到它以一堆“A”字符开头。 对于0x00字节,它是Base 64。

如果你想确保你有一个随机的IV,你必须自己生成它:

 SecureRandom r = new SecureRandom(); byte[] ivBytes = new byte[16]; r.nextBytes(ivBytes); cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivBytes)); 

IV不安全。 它被添加到密文之前,因此任何攻击者只需要读取密文并查看前16个字节以查看它是什么。

始终使用相同的IV并不是一个好主意 – 它有效地将实际密文的第一个块转换为ECB模式,这是不安全的。

我建议你自己生成随机IV。 您不需要使用加密安全的RNG,因为攻击者无论如何都可以看到它。 您只需要避免对不同的消息使用相同的IV。 即使像8字节(64位)计数器那样简单的8字节随机数也足以避免ECB效应。 在计数器翻转之前更改密钥,并在更改密钥时将计数器重置为0。