Java Android加密API未为AES生成安全IV

Java Android加密API未为AES生成安全IV,java,android,encryption,Java,Android,Encryption,我正在使用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(p

我正在使用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中):

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

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

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

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

当你看密文的前面时,你会发现它是以一堆“a”字符开头的。0x00字节的基数为64

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

SecureRandom r = new SecureRandom();
byte[] ivBytes = new byte[16];
r.nextBytes(ivBytes);

cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivBytes));

静脉注射不安全。它在密码文本前面,因此任何攻击者只需读取密码文本并查看前16个字节即可查看它是什么

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


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

即使IV是公开可见的,也需要使用加密强RNG。例如,参见。你需要好的、强的、随机的静脉注射。不要使用弱IV代。你是对的,谢谢你的纠正。我知道我可以自己生成一个IV,这就是我从现在开始要做的,因为我不能再相信图书馆会为我做这件事了。我只是好奇,这种情况只发生在一些设备上,大多数情况下,我都会得到带有适当随机IVs的密文,就像这样:
7TUKMTxQTicdblLvBw0+33mcnoeonwcx5bnnkmen6hrect4ssopzupvgzdfvf3d5kuznlxuvu/PLt3ngv5hK9pUx98Oi3WblWnY2yN+tX0=
。如您所见,前16个字节是随机的。但有些设备会产生不好的非随机IVs。这是该制造商的ROM的错误,还是旧Android版本的问题?我不熟悉Android上的JCE提供商,但我认为制造商在ROM中包含哪些提供商以及哪个是默认提供商方面有发言权。这不是制造商第一次证明其安全性不好。请注意,加密时您将得到
呼叫方提供的IV不允许。
除非您在该链接上禁用密码-有趣的文档。我一直在研究这个问题,它看起来像旧的(4.3)
AndroidOpenSSL
实现做到了这一点,但在较新的版本中似乎已经修复了这一点(7.0很好)。我注意到您没有指定提供者。我有兴趣在你的帖子中更多地了解“有时”的本质。我已经在这里谈了一些这个问题
SecureRandom r = new SecureRandom();
byte[] ivBytes = new byte[16];
r.nextBytes(ivBytes);

cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivBytes));