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

时间:2014-06-09 15:59:45

标签: java c# encryption cryptography

我需要将Java客户端的加密数据发送到C#服务器。现在我正在学习如何使用AES(要求)加密数据。按照接受的答案android encryption/decryption with 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);

由于该算法使用SecureRandom使用keyStart我不确定这是否可以在C#或甚至在另一个Java程序中解码,而不是SecureRandom 1}}。

这种加密/解密是否只需要知道keyStart的值,或者因为我使用SecureRandom,我还需要传递其他内容才能解密吗?

此外,还有更好的方法吗?或者这个很好吗?

1 个答案:

答案 0 :(得分:15)

不,你应该使用SecureRandom从静态数据中获取密钥的整个想法是相当糟糕的:

  1. SecureRandom的主要功能是生成随机值,它不应该用作密钥流的生成器;
  2. SecureRandom,当用"SHA1PRNG"实例化时,没有实现定义良好的算法,并且实际上已知算法会发生变化,即使从一个Sun JDK到另一个也是如此;
  3. Oracle提供的"SHA1PRNG"实现使用初始种子作为种子,其他人可能只是种子添加到随机池中。
  4. 已知使用"SHA1PRNG"作为密钥派生函数会在多个版本的Android上产生问题,并且可能在任何其他Java RE上失败。


    那你该怎么做呢?

    1. 使用new SecureRandom()甚至更好,KeyGenerator生成一个真正的随机密钥,如果您需要一个全新的随机密钥,则无需播种随机数生成器;
    2. 直接向byte[]提供SecretKeySpec已知密钥,或使用十六进制解码器从十六进制解码(注意String个实例很难从内存中删除,所以只如果没有其他办法,请这样做;)
    3. 如果要从密码创建密钥,请使用PBKDF2(使用的迭代次数高于链接中提供的次数);
    4. 如果要从一个密钥种子创建多个密钥,请使用真正的基于密钥的密钥派生机制,例如:使用HKDF(见下文)。
    5. 如果种子是由例如种子生成的,那么选择4将是优选的。密钥协商算法,如Diffie-Hellman或ECDH。


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


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