【问题标题】:Is it secure to build a Cipher object with a SecureRandom object which has a fixed seed?使用具有固定种子的 SecureRandom 对象构建 Cipher 对象是否安全?
【发布时间】:2019-01-14 12:47:41
【问题描述】:

我的一位同事让我检查他的代码是否足够安全。我看到了一些这样的代码sn-p:


    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 不会两次生成相同的随机数,即使是由相同的种子初始化,但它只是在测试中起作用。

我认为以这种方式加密某些东西是不安全的,但我不知道这段代码 sn-p 有什么问题。

我的问题:

  1. encrypt 函数安全吗?
  2. 如果不安全,这样做有什么问题?

【问题讨论】:

  • 以这种方式使用 SecureRandom 已被弃用且不可移植。正如您所注意到的,它甚至可能不会连续两次生成相同的值。事实上,当前和过去的实现 生成了相同的值,因此导致这种反模式激增。而是使用正确的基于密码的 KDF,例如 PBKDF2。
  • "如果不安全,这样做有什么问题?"呃,对不起,我不明白这部分问题。什么问题?做什么?
  • 不,它不安全。最后它是一个存储在你的程序中的静态密钥,只是有点混淆了。
  • @Robert lcg 方法被@Test 注释的事实让我认为这个应用程序不一定使用静态键。

标签: java security cryptography


【解决方案1】:

encrypt 函数安全吗?

不,任何好的安全定义都不安全。

首先,它使用ECB,除非明文块不相关,否则它是不安全的。

更重要的是,new SecureRandom() 只是从提供者列表中获取第一个随机数生成器。通常这是"SHA1PRNG",但目前——对于Oracle Java 11 SE 运行时——它返回一个更快、更好定义的DRBG。这些是不兼容的,所以一个密文不能用另一个运行时解密;代码根本不可移植。

不同的运行时可能会返回完全不同的随机数生成器 - 可能针对运行时配置进行了优化。这些随机数生成器可能完全依赖于在从中提取随机数之前设置的给定种子if。它也可以混合种子进入状态。这将产生一个完全随机的密钥,除非您将它保存在某个地方,否则您将永远无法重新生成它。

基本上,这种方法可能因为 ECB 而变得不安全,而且过于安全——这本身就不是一件小事。您可能永远无法再次解密密文,但您仍然可以区分相同的明文块。


另一个小问题是getBytes 使用平台默认编码。这不同,例如在 Windows (Windows-1252) 和 Linux (UTF-8) 和 Android 平台(当然也是 UTF-8)之间。所以在另一个系统上解码明文 - 如果可以的话 - 之后你可能仍然会感到惊讶。


程序太糟糕了,应该将它归档在圆形垃圾接收器中并实施一些新的东西。为此,至少在 GCM 模式下使用由随机字节和现代密码(如 AES)组成的密钥和 IV 是一个好主意。如果您有密码,则应使用密码散列(或基于密钥的密钥派生函数),如 PBKDF2 或更现代的一种从中派生密钥。


Kudo 发现了一种更糟糕的从密码中获取密钥的方法。 getRawKey 很糟糕,但这个更糟糕。换句话说,你问得很好。

【讨论】:

  • 谢谢。由于某种原因,我没有将源代码sn-p粘贴到stackoverflow上,而是写了另一个来表达源代码中的逻辑。最后我发现这段代码是在baidu.com(中国最常用的搜索引擎)搜索“java aes encrypt”时从结果列表的第一项复制而来的。好消息是他们还修复了 SecureRandom 的算法和 getBytes 的编码。 ECB 可能不会在原始代码中使用,它们将密码算法放在一个常量中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 2011-01-28
  • 2013-01-23
  • 1970-01-01
  • 2019-10-18
相关资源
最近更新 更多