比播种SecureRandom更好的创建AES密钥的方法

Chr*_*sco 7 c# java encryption cryptography

我需要将加密数据从Java客户端发送到C#服务器.现在我正在学习如何使用AES(要求)加密数据.继这个接受的答案用AES加密/解密我正在做以下事情:

byte[] keyStart = "qweroiwejrwoejlsifeoisrn".getBytes(); // Random character string

byte[] toEncrypt = myMessageString.getBytes();

keyGen = KeyGenerator.getInstance("AES");
sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
keyGen.init(128, sr);
SecretKey secretKey = keyGen.generateKey();
byte[] secretKeyByte = secretKey.getEncoded();

SecretKeySpec skeySpec = new SecretKeySpec(secretKeyByte, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
cipher.doFinal(toEncrypt);
Run Code Online (Sandbox Code Playgroud)

由于算法使用了一个SecureRandom使用keyStart我不知道这是否可以解码,C#甚至在另一个Java程序中解码,没有SecureRandom.

这个加密/解密是否能够只知道keyStart我正在使用的值或者因为SecureRandom我仍然需要传递其他东西才能解密?

还有,有更好的方法来做这个还是这个好吗?

Maa*_*wes 17

不,你应该使用SecureRandom静态数据进行密钥派生的整个想法是相当糟糕的:

  1. SecureRandom的主要功能是生成随机值,它不应该用作密钥流的生成器;
  2. SecureRandom,当实例化"SHA1PRNG"没有实现一个良好定义的算法,该算法实际上是已知的改变,甚至从一台Sun JDK到另一个;
  3. 甲骨文提供的实施"SHA1PRNG"使用初始种子作为唯一的种子,有些人可能只是增加了种子随机池.

"SHA1PRNG"已知使用密钥派生函数会在多个版本的Android上产生问题,并且可能在任何其他Java RE上失败.


那么你该怎么做呢?

  1. 如果你需要一个全新的随机密钥new SecureRandom(),KeyGenerator可以使用甚至更好地生成一个真正的随机密钥,而无需播种随机数生成器;
  2. 直接提供一个byte[]已知密钥SecretKeySpec,或使用十六进制解码器从十六进制解码它(注意String实例很难从内存中删除,所以只有在没有其他方法时才这样做);
  3. 使用PBKDF2,如果你想创建一个从密码密钥(使用更高的迭代次数比提供的链接虽然一个);
  4. 如果你想从一个密钥种子创建多个按键,如使用香港民主促进会(见下文)使用一个真正的基于密钥密钥导出机制.

如果通过例如关键协议算法(例如Diffie-Hellman或ECDH)生成种子,则选项4将是优选的.


请注意,对于选项3,PBKDF2,您最好只保留ASCII密码.这是因为Oracle的PBKDF2实现不使用UTF-8编码.


至于选项4,我帮助将所有优秀的KBKDF添加到Bouncy Castle库中,因此如果您可以将Bouncy Castle添加到类路径和/或已安装的安全提供程序列表中,则无需自己实现KBKDF.目前最好的KBKDF可能是HKDF.如果您不能将Bouncy Castle添加到类路径中,那么您可能希望使用SHA-256输出的最左边字节而不是派生数据作为"穷人"KDF.