我的一位同事要求我检查他的代码是否足够安全。我看到了一些这样的代码片段:
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
函数安全吗?答案 0 :(得分:5)
encrypt
功能安全吗?
不,对于安全性的任何良好定义都是不安全的。
首先,它使用ECB,除非明文块不相关,否则这是不安全的。
更重要的是,new SecureRandom()
只是从提供商列表中获取第一个随机数生成器。通常这是"SHA1PRNG"
,但是当前-对于Oracle Java 11 SE运行时-它返回的速度更快,定义更好。DRBG
。这些密文不兼容,因此一个密文无法在另一运行时解密。该代码根本不可移植。
不同的运行时可能返回完全不同的随机数生成器-可能针对运行时配置进行了优化。这些随机数发生器可能完全取决于给定的种子 ,如果种子是从种子中提取随机数而设置的,则将其设置为种子。它还可以将种子混合到状态。这将产生一个完全随机的密钥,除非您将其保存在某个地方,否则您将永远不会对其进行重新生成。
基本上,由于ECB,此方法可能既不安全,又过于安全-本身并不是一件容易的事。您可能永远无法再次解密密文,但仍然可以区分相同的明文块。
另一个小问题是getBytes
使用平台默认编码。例如,这有所不同。在Windows(Windows-1252)和Linux(UTF-8)以及Android平台(当然也是UTF-8)之间切换。因此,如果可以的话,请在其他系统上解码该纯文本,之后您仍然可能会感到惊讶。
此过程非常糟糕,应将其存储在圆形垃圾接收器中并实施新的操作。为此,最好至少在GCM模式下使用由随机字节和现代密码(例如AES)组成的密钥和IV。如果您有密码,则应使用PBKDF2之类的密码哈希(或基于密钥的密钥派生功能)或更现代的密码来从中派生密钥。
工藤寻找从密码派生密钥的更差方法。 getRawKey
很糟糕,但是这个情况更糟。换句话说,你问的很好。