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

2022-01-10 00:00:00 encryption cryptography c# java

我需要将加密数据从 Java 客户端发送到 C# 服务器.现在我正在学习如何使用 AES 加密数据(要求).按照这个公认的答案 android 加密/使用 AES 解密,我正在执行以下操作:

I need to send encrypted data from a Java client to a C# server. Right now I'm learning how to encrypt data using AES (requirement). Following this accepted answer android encryption/decryption with AES I'm doing the following:

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.

Since the algorithm uses a SecureRandom using the keyStart I am not sure if this can be decoded in C# or even in another Java program, without the SecureRandom.

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

Will this encryption/decryption work with just knowing the value of keyStart or since I'm using SecureRandom I still need to pass something else in order to decrypt?

另外,有没有更好的方法,或者这个就可以了?

Also, is there a better way to do it or is this one just fine?

推荐答案

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

No, the whole idea that you should use a SecureRandom for key derivation from static data is rather bad:

  1. SecureRandom的主要功能是生成随机值,不应该用作密钥流的生成器;
  2. SecureRandom,当用 "SHA1PRNG" 实例化时,并没有实现定义明确的算法,而且实际上已知算法会发生变化,即使从一个 Sun JDK 到另一个;
  3. Oracle 提供的"SHA1PRNG" 实现使用初始种子作为only 种子,其他人可能只是添加 种子到随机池中.
  1. SecureRandom's main function is to generate random values, it should not be used as a generator for a key stream;
  2. SecureRandom, when instantiated with "SHA1PRNG" does not implement a well defined algorithm, and the algorithm has actually be known to change, even from one Sun JDK to another;
  3. The Oracle provided implementation of "SHA1PRNG" uses the initial seed as only seed, others may just add the seed to the random pool.

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

Using "SHA1PRNG" as key derivation function has been known to produce issues on several versions of Android, and may fail on any other Java RE.

那么你应该怎么做呢?

  1. 使用 new SecureRandom() 甚至更好,KeyGenerator 生成真正的随机密钥,如果您需要全新的随机密钥,则无需播种随机数生成器;
  2. 直接向SecretKeySpec提供一个已知密钥的byte[],或者使用十六进制解码器从十六进制解码(注意String 实例很难从内存中删除,所以只有在没有其他方法的情况下才这样做);
  3. 如果您想从密码创建密钥,请使用 PBKDF2(使用比在链接);
  4. 如果您想从一个密钥种子创建多个密钥,请使用真正的基于密钥的密钥派生机制,例如使用 HKDF(见下文).
  1. Use new SecureRandom() or even better, KeyGenerator to generate a truly random key, without seeding the random number generator if you need a brand new random key;
  2. Directly provide a byte[] of a known key to SecretKeySpec, or use a hexadecimal decoder to decode it from hexadecimals (note that String instances are hard to delete from memory, so only do this if there is no other way);
  3. Use PBKDF2 if you want to create a key from a password (use a higher iteration count than the one provided in the link though);
  4. Use a true Key Based Key Derivation Mechanism if you want to create multiple keys from one key seed, e.g. use HKDF (see below).

如果种子是由例如生成的,选项 4 将是首选.密钥协商算法,例如 Diffie-Hellman 或 ECDH.

Option 4 would be preferred if the seed was generated by e.g. a key agreement algorithm such as Diffie-Hellman or ECDH.

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

Note that for option 3, PBKDF2, you would be wise to keep to ASCII passwords only. This is due to the fact that the PBKDF2 implementation by Oracle does not use UTF-8 encoding.

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

As for option 4, I've helped with adding all good KBKDF's to the Bouncy Castle libraries so there isn't a need to implement a KBKDF yourself if you can add Bouncy Castle to your classpath and/or list of installed security providers. Probably the best KBKDF at the moment is HKDF. If you cannot add Bouncy Castle to your classpath then you might want to use the leftmost bytes of SHA-256 output over the derivation data as a "poor man's" KDF.

相关文章