Java AES / GCM / NoPadding - 什么是cipher.getIV()给我?

时间:2015-08-06 09:14:32

标签: java security encryption cryptography aes-gcm

我在Java 8中使用AES/GCM/NoPadding加密,我想知道我的代码是否存在安全漏洞。我的代码似乎工作,因为它加密和解密文本,但有些细节不清楚。

我的主要问题是:

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV(); // ?????

IV是否满足“对于给定的密钥,IV不得重复”的要求。来自RFC 4106

我也很感激我对相关问题的任何答案/见解(见下文),但第一个问题最让我烦恼。我不知道在哪里可以找到解决此问题的源代码或文档。

这是完整的代码,粗略。如果我在撰写这篇文章时引入了错误,我深表歉意:

class Encryptor {
  Key key;

  Encryptor(byte[] key) {
    if (key.length != 32) throw new IllegalArgumentException();
    this.key = new SecretKeySpec(key, "AES");
  }

  // the output is sent to users
  byte[] encrypt(byte[] src) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] iv = cipher.getIV(); // See question #1
    assert iv.length == 12; // See question #2
    byte[] cipherText = cipher.doFinal(src);
    assert cipherText.length == src.length + 16; // See question #3
    byte[] message = new byte[12 + src.length + 16]; // See question #4
    System.arraycopy(iv, 0, message, 0, 12);
    System.arraycopy(cipherText, 0, message, 12, cipherText.length);
    return message;
  }

  // the input comes from users
  byte[] decrypt(byte[] message) throws Exception {
    if (message.length < 12 + 16) throw new IllegalArgumentException();
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec params = new GCMParameterSpec(128, message, 0, 12);
    cipher.init(Cipher.DECRYPT_MODE, key, params);
    return cipher.doFinal(message, 12, message.length - 12);
  }
}

假设用户破解我的密钥=游戏结束。

更详细的问题/相关问题:

  1. cipher.getIV()返回的IV对我来说是否可以这样使用?

    • 它是否避免了在Galois / Counter模式下重复使用IV,组合键的灾难?
    • 当我有多个应用程序同时运行此代码时,它是否仍然安全,所有应用程序都使用相同的src数据(可能在同一毫秒内)向用户显示加密消息?
    • 返回的IV由什么组成?它是一个原子计数器加上一些随机噪声吗?
    • 我是否需要避免cipher.getIV()并使用自己的计数器自行构建IV?
    • 假设我使用的是Oracle JDK 8 + JCE Unlimited Strength扩展,那么在某处实现cipher.getIV()的源代码是否可用?
  2. 那个IV总是长12个字节吗?

  3. 身份验证标记的长度是16字节(128位)吗?

  4. 对于#2和#3,并且缺少填充,这是否意味着我的加密消息总是12 + src.length + 16个字节长? (所以我可以安全地将它们压成一个字节数组,我知道它的长度是正确的吗?)

  5. 根据用户知道的常量src数据,向用户显示无限数量的src数据加密是否安全?

  6. 如果每次src数据不同(例如包括System.currentTimeMillis()或随机数),向用户显示无限数量的src数据加密是否安全?

  7. 如果我在加密前用随机数填充src数据会有帮助吗?在前面和后面说8个随机字节,或者只在一端?或者这根本没有帮助/使我的加密更糟糕?

  8. (因为这些问题都是关于我自己的代码的相同块,并且它们彼此密切相关,而其他人在实现相同的功能时可能/应该有相同的问题集,所以分裂问题分为多个帖子。如果更适合StackOverflow的格式,我可以单独重新发布。请告诉我们!)

1 个答案:

答案 0 :(得分:39)

Q1:cipher.getIV()返回的IV对我来说是否安全?

是的,至少对于Oracle提供的实现而言。它使用默认的SecureRandom实现单独生成。因为它的大小是12个字节(GCM的默认值),所以你有96位的随机性。计数器重复的可能性非常小。您可以在Oracle JDK所基于的OpenJDK(GPL)中查找源代码。

但是我仍然建议您生成自己的12个随机字节,因为其他提供商的行为可能不同。

Q2:那个IV总是长12个字节吗?

极有可能因为它是GCM默认值,但的其他长度对GCM有效。但是,该算法必须对12个字节以外的任何其他大小进行额外的计算。由于存在弱点,强烈建议将其保持在12字节/ 96位,而API的可能会限制您选择IV大小

问题3:身份验证标记的长度是16字节(128位)吗?

不,它可以具有任何大小的字节,范围从64位到128位,增量为8位。如果它更小,它只包含认证标签的最左边的字节。您可以使用GCMParameterSpec作为init来电的第三个参数指定其他尺寸的代码。

请注意,GCM的强度很大程度上取决于标签的大小。我建议将它保持为128位。如果要生成大量密文,则96位应该是最小,尤其是

问题4:对于#2和#3,并且缺少填充,这是否意味着我的加密消息总是12 + src.length + 16个字节长? (所以我可以安全地将它们压成一个字节数组,我知道它的长度是正确的吗?)

见上文。对于Oracle提供商来说就是这种情况。使用GCMParameterSpec来确定它。

问题5:根据用户知道的常量src数据,向用户显示无限数量的src数据加密是否安全?

实际上未绑定,是的。在大约2 ^ 48次加密后我会开始担心。但是,一般情况下你应该设计密钥更改。

问题6:如果每次src数据不同(例如包括System.currentTimeMillis()或随机数),向用户显示无限数量的src数据加密是否安全?

见Q5答案

问题7:如果我在加密前用随机数填充src数据会有帮助吗?在前面和后面说8个随机字节,或者只在一端?或者这根本没有帮助/使我的加密更糟糕?

不,它根本没用。 GCM使用下面的CTR模式,因此它将使用密钥流加密。它充当IV。如果您需要几乎无限数量的密文(高于2 ^ 48!),那么我建议您使用该随机数和密钥来获得密钥派生函数或KDF。 HKDF目前是最佳品种,但您可能需要使用Bouncy Castle或自行实施。