java - 使用具有固定种子的 SecureRandom 对象构建 Cipher 对象是否安全?
问题描述
我的一位同事让我检查他的代码是否足够安全。我看到了一些这样的代码片段:
private static byte[] encrypt(String plain, String key) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(key.getBytes());
kg.init(128, secureRandom);
SecretKey secretKey = kg.generateKey();
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(plain.getBytes());
}
@Test
public void lcg() throws Throwable {
String plain = "abc";
String key = "helloworld";
byte[] c1 = encrypt(plain, key);
byte[] c2 = encrypt(plain, key);
Assert.assertArrayEquals(c1, c2);
}
该encrypt
功能用于对敏感数据进行加密,加密后的数据会存入数据库。我认为它首先不起作用,因为 SecureRandom 不会两次生成相同的随机数,即使是由相同的种子初始化,但它只是在测试中起作用。
我认为以这种方式加密某些东西是不安全的,但我不知道这个代码片段有什么问题。
我的问题:
- 功能
encrypt
安全吗? - 如果它不安全,那么这样做有什么问题?
解决方案
功能
encrypt
安全吗?
不,对于任何好的安全定义来说,它都是不安全的。
首先,它使用ECB,除非明文块不相关,否则它是不安全的。
更重要的是,new SecureRandom()
只需从提供者列表中获取第一个随机数生成器。通常这是"SHA1PRNG"
但目前 - 对于 Oracle Java 11 SE 运行时 - 它返回一个更快和更好定义的DRBG
. 这些是不兼容的,所以一个密文不能用另一个运行时解密;该代码根本不可移植。
不同的运行时可能会返回完全不同的随机数生成器——可能针对运行时配置进行了优化。这些随机数生成器可能完全取决于给定的种子,如果它是在从中提取随机数之前设置的。它也可能将种子混合到状态中。这将产生一个完全随机的密钥,除非你将它保存在某个地方,否则你将永远无法重新生成它。
基本上,这种方法可能由于欧洲央行而既不安全又过于安全——这本身就不是一件小事。您可能永远无法再次解密密文,但您仍然可以区分相同的明文块。
另一个小问题是getBytes
使用平台默认编码。这在 Windows (Windows-1252) 和 Linux (UTF-8) 以及 Android 平台(当然也是 UTF-8)之间有所不同。所以在另一个系统上解码明文——如果可以的话——之后你可能仍然会感到惊讶。
这个程序太糟糕了,它应该被归档在圆形垃圾接收器中并实施一些新的东西。为此,至少在 GCM 模式下使用由随机字节和现代密码(如 AES)组成的密钥和 IV 是一个好主意。如果您有密码,您应该使用密码散列(或基于密钥的密钥派生函数),如 PBKDF2 或更现代的一种从中派生密钥。
Kudo 找到了一种更糟糕的方法来从密码中获取密钥。getRawKey
很糟糕,但这个更糟。换句话说,你问得很好。
推荐阅读
- flutter - BottomNavigatonBar 的 onTap(index) 方法上的 SetState() 不会重建小部件树
- python - 如何避免 Python 中的语义错误?
- python - 如何通过多处理有效地迭代分割的图像?
- shell - 在一个步骤/命令中查找、解压缩和 grep 多个文件的内容
- ajax - 还有其他方法可以使用路由设置网址吗?
- c++ - std::thread 不退出
- python - AttributeError:“XGBClassifier”对象没有属性“save_raw”
- r - 如何使我的函数(几个 ggplot2 图)的输出成为 html 文件(显示这些图)?
- c - 如何并行化以下for循环
- c++ - 重载类方法与模板