>我在系统身份验证系统上遇到问题。 我们的服务器使用1.6版本,而客户端使用1.8版本,在身份验证过程中,我们通过SecureRandom
"SHA1PRNG"
生成密钥,同时使用以下代码: 即:
KeyGenerator keygen = KeyGenerator.getInstance("Blowfish");
SecureRandom foo = SecureRandom.getInstance("SHA1PRNG");
foo.setSeed("baa".getBytes());
keygen.init(foo);
问题是,我们发现客户端中生成的密钥与服务器中生成的密钥不同。我们已经厌倦了打印出所有步骤,发现问题是由SecureRandom
引起的,即foo.setSeed("baa".getBytes());
后如果我们调用foo.nextBytes()
,它会给出不同的值。
因此,我们想知道是否有任何方法可以保持双方生成相同的值?(鉴于客户端和服务器中的 Java 版本都无法更改。还是任何独立于平台的 JavaSecureRandom
方法?
背景信息:SERVER 和 CLIENT 在 Unix 中运行。 我有一个运行Java 1.8的桌面,我已经测试了以下内容:
桌面Java 1.8可以加密和解密在CLIENT(Java 1.8)中生成的密钥
CLIENT(Java1.8)不能加密或解密SERVER(Java 1.6)中生成的密钥,反之亦然。
CLIENT 已经安装了 Java 1.6(仅用于测试)无法加密或解密在 SERVER (Java 1.6) 中生成的密钥。我们猜测这是因为
/dev/random
或/dev/urandom
已被覆盖到 Java 1.8 版本。因此,即使Java的版本是相同的,它们也有不同的行为。
来自SecureRandom
的文档:
此外,
SecureRandom
必须产生非确定性输出。 因此,传递给SecureRandom
对象的任何种子材料都必须 不可预测,所有SecureRandom
输出序列都必须 加密强度,如 RFC 1750:随机性中所述 安全建议。
因此,您不仅通过传递可预测的种子违反了SecureRandom
的要求,而且明确要求SecureRandom
的输出是不可预测的。
为了生成可预测的随机性序列,请使用Random
:
如果使用相同的种子创建了两个
Random
实例,并且 对每个方法调用进行相同的方法调用序列,它们将生成和 返回相同的数字序列。
但请注意:如果每次都使用相同的种子,则数字将始终相同,因此您必须使用相同的初始种子,该种子以某种方式在客户端和服务器之间共享。每次重新启动服务器应用程序时,都需要重置此初始种子。
Random
的实例必须在对例程的调用之间共享,否则每次都会生成相同的单个号码:
public static void main(final String[] args) {
IntStream.range(1, 10)
.map(i -> new Random(42).nextInt())
.forEach(System.out::println);
}
输出:
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
一般来说,您要做的是一个坏主意TM。最好使用非对称加密方案,而不是尝试自己重新发明轮子。
不要发明自己的键偏差函数。使用为此发明的功能。
您不会编写用作密钥偏差输入的内容,但如果它是密码,则可以使用 PBE(基于密码的加密)密钥规范 - 如下所示:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "Blowfish");