用具有固定种子的SecureRandom对象构建Cipher对象是否安全?

时间:2019-01-14 12:47:41

标签: java security cryptography

我的一位同事要求我检查他的代码是否足够安全。我看到了一些这样的代码片段:


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

我认为以这种方式加密某些内容并不安全,但是我无法确定此代码段的问题所在。

我的问题:

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

1 个答案:

答案 0 :(得分:5)

  

encrypt功能安全吗?

不,对于安全性的任何良好定义都是不安全的。

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

更重要的是,new SecureRandom()只是从提供商列表中获取第一个随机数生成器。通常这是"SHA1PRNG",但是当前-对于Oracle Java 11 SE运行时-它返回的速度更快,定义更好。DRBG。这些密文不兼容,因此一个密文无法在另一运行时解密。该代码根本不可移植。

不同的运行时可能返回完全不同的随机数生成器-可能针对运行时配置进行了优化。这些随机数发生器可能完全取决于给定的种子 ,如果种子是从种子中提取随机数而设置的,则将其设置为种子。它还可以将种子混合到状态。这将产生一个完全随机的密钥,除非您将其保存在某个地方,否则您将永远不会对其进行重新生成。

基本上,由于ECB,此方法可能既不安全,又过于安全-本身并不是一件容易的事。您可能永远无法再次解密密文,但仍然可以区分相同的明文块。


另一个小问题是getBytes使用平台默认编码。例如,这有所不同。在Windows(Windows-1252)和Linux(UTF-8)以及Android平台(当然也是UTF-8)之间切换。因此,如果可以的话,请在其他系统上解码该纯文本,之后您仍然可能会感到惊讶。


此过程非常糟糕,应将其存储在圆形垃圾接收器中并实施新的操作。为此,最好至少在GCM模式下使用由随机字节和现代密码(例如AES)组成的密钥和IV。如果您有密码,则应使用PBKDF2之类的密码哈希(或基于密钥的密钥派生功能)或更现代的密码来从中派生密钥。


工藤寻找从密码派生密钥的更差方法。 getRawKey很糟糕,但是这个情况更糟。换句话说,你问的很好。