“AES/GCM/NoPadding"的 IV 和身份验证标签如何处理? [英] How are the IV and authentication tag handled for "AES/GCM/NoPadding"?

查看:28
本文介绍了“AES/GCM/NoPadding"的 IV 和身份验证标签如何处理?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using AES/GCM/NoPadding encryption in Java 8 and I'm wondering whether my code has a security flaw. My code seems to work, in that it encrypts and decrypts text, but a few details are unclear.

我的主要问题是:

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

该 IV 是否满足对于给定的密钥,IV 不得重复"的要求.来自 RFC 4106?

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

I'd also appreciate any answers / insight for my related questions (see below), but that first question is bugging me the most. I don't know where to find source code or documentation that answers this.

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

Here is the full code, roughly. I apologize in case I introduced errors while writing this post:

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);
  }
}

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

Suppose that users cracking my secret key = game over.

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

More detailed questions / related questions:

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

  • 是否避免了在 Galois/Counter 模式下重复使用 IV、组合键的灾难?
  • 当我有多个应用程序同时运行此代码,所有应用程序都向用户显示来自相同 src 数据的加密消息(可能在同一毫秒内)时,它仍然安全吗?
  • 返回的 IV 是由什么制成的?是原子计数器加上一些随机噪声吗?
  • 我是否需要避免使用 cipher.getIV() 并使用自己的计数器自己构建一个 IV?
  • 假设我使用的是 Oracle JDK 8 + JCE Unlimited Strength 扩展,实现 cipher.getIV() 的源代码是否可以在线获取?
    • Does it avoid the catastrophe of reusing the IV,key combination in Galois/Counter Mode?
    • Is it still safe when I have multiple applications running this code at once, all displaying encrypted messages to users from the same src data (possibly in the same millisecond)?
    • What's the returned IV made of? Is it an atomic counter plus some random noise?
    • Do I need to avoid cipher.getIV() and construct an IV myself, with my own counter?
    • Is the source code implementing cipher.getIV() available online somewhere, assuming I'm using Oracle JDK 8 + JCE Unlimited Strength extension?
      1. 那个 IV 总是 12 字节长吗?

      1. Is that IV always 12 bytes long?

      身份验证标签是否总是 16 字节(128 位)长?

      Is the authentication tag always 16 bytes (128 bits) long?

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

      With #2 and #3, and the lack of padding, does that mean my encrypted messages are always 12 + src.length + 16 bytes long? (And so I can safely squish them into one byte array, for which I know the correct length?)

      鉴于用户知道的恒定 src 数据,我向用户显示无限数量的 src 数据加密是否安全?

      Is it safe for me to display an unbounded number of src data encryptions to users, given constant src data that the users know?

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

      Is it safe for me to display an unbounded number of src data encryptions to users, if the src data is different every time (e.g. including System.currentTimeMillis() or random numbers)?

      如果我在加密前用随机数填充 src 数据会有帮助吗?说前后 8 个随机字节,还是仅在一端?或者这根本没有帮助/让我的加密变得更糟?

      Would it help if I padded the src data with random numbers before encryption? Say 8 random bytes in front and back, or only on one end? Or would that not help at all / make my encryption worse?

      (因为这些问题都是关于我自己代码的同一个块,而且它们之间有很强的相关性,其他人在实现相同的功能时可能/应该有相同的问题,所以把这些问题分开来感觉是错误的问题成多个帖子.如果这更适合 StackOverflow 的格式,我可以单独重新发布它们.让我知道!)

      (Because these questions are all about the same block of my own code, and they are strongly related to each other, and others might/should have the same set of questions when implementing the same functionality, it felt wrong to split the questions into multiple posts. I can re-post them separately if that is more appropriate for StackOverflow's format. Let me know!)

      推荐答案

      Q1: cipher.getIV() 返回的 IV 对我来说这样使用安全吗?

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

      Yes, it is at least for the Oracle provided implementation. It is generated separately using the default SecureRandom implementation. As it is 12 bytes in size (the default for GCM) then you have 96 bits of randomness. The chance that the counter repeats is abysmally small. You can look up the source in the OpenJDK (GPL'ed) which the Oracle JDK is based on.

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

      I would however still recommend you to generate your own 12 random bytes as other providers may behave differently.

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

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

      It's extremely likely as it is the GCM default, but other lengths are valid for GCM. The algorithm will however have to do additional calculations for any other size than 12 bytes. Due to weaknesses it is strongly recommended to keep it at 12 bytes / 96 bits and API's may restrict you to that choice of IV size.

      Q3:身份验证标签是否总是 16 字节(128 位)长?

      不,它可以有任何大小的字节,范围从 64 位到 128 位,增量为 8 位.如果它更小,它只是由身份验证标签的最左边的字节组成.您可以使用 GCMParameterSpec 作为 init 调用的第三个参数.

      No, it can have any size in bytes ranging from 64 bits to 128 bits with 8 bit increments. If it is smaller it simply consists of the leftmost bytes of the authentication tag though. You can specify another size of tag using GCMParameterSpec as third parameter for your init call.

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

      Note that the strength of GCM is strongly dependent on the size of the tag. I would recommend keeping it to 128 bits. 96 bits should be the minimum especially if you want to generate a lot of ciphertext.

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

      见上.对于 Oracle 提供商而言,情况就是如此.使用 GCMParameterSpec 来确定.

      See above. For the Oracle provider this is the case. Use GCMParameterSpec to be sure of it.

      问题 5:鉴于用户知道的恒定 src 数据,我向用户显示无限数量的 src 数据加密是否安全?

      几乎未绑定,是的.我会在大约 2^48 次加密后开始担心.但是,通常您应该设计键更改.

      Virtually unbound, yes. I would start worrying after about 2^48 encryptions. In general you should however design for key change.

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

      请参阅对 Q5 的回答 &Q7

      See answer to Q5 & Q7

      Q7:如果我在加密前用随机数填充 src 数据会有帮助吗?说前后 8 个随机字节,还是仅在一端?或者这根本没有帮助/让我的加密变得更糟?

      不,它根本没有帮助.GCM 在下面使用 CTR 模式,所以它只会用密钥流加密.它不会充当 IV.现在,如果您要加密不断变化的消息,您可以查看 AES-GCM-SIV,但请注意,该算法并未在任何 JCA 提供商中实现.

      No, it would not help at all. GCM uses CTR mode underneath, so it would just be encrypted with the key stream. It would not act as an IV. Nowadays you could look at AES-GCM-SIV if you have an ever changing message to encrypt, but note that that algorithm is not implemented in any of the JCA providers.

      如果您需要大量密文(高于 2^48!,或 2^32 - ~40 亿 - 谨慎的)那么我建议您使用该随机数和您的密钥作为密钥推导函数或 KDF.HKDF 目前是同类中最好的,但您可能需要使用 Bouncy Castle 或自行实施.

      If you need a lot of ciphertexts (higher than 2^48!, or 2^32 - ~4 billion - for the cautious) then I would suggest you use that random number and your key for a key derivation function or KDF. HKDF is currently best of breed, but you may need to use Bouncy Castle or implement it yourself.

      这篇关于“AES/GCM/NoPadding"的 IV 和身份验证标签如何处理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆