如何处理“AES/GCM/NoPadding"的 IV 和身份验证标签?

时间:2023-01-16
本文介绍了如何处理“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.

      Q4:使用#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.

      Q5:在给定用户知道的恒定 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 数据加密是否安全?

      查看问题 5 的答案 &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 和身份验证标签?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:RSA 块的数据过多失败.什么是 PKCS#7? 下一篇:SecureRandom 与 NativePRNG 与 SHA1PRNG

相关文章

最新文章