Java AES / GCM / NoPadding - 什么是cipher.getIV()给我? [英] Java AES/GCM/NoPadding - What is cipher.getIV() giving me?

查看:374
本文介绍了Java AES / GCM / NoPadding - 什么是cipher.getIV()给我?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我的主要问题是这样的: / p>

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

IV满足对于给定的键,IV不得重复。来自 RFC 4106



我也会感谢有关我的相关问题的答案(见下文),但是第一个问题是最让我失望的。我不知道在哪里可以找到解决这个问题的源代码或文档。






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

  class Encryptor {
Key key;

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

//输出发送给用户
byte [] encrypt(byte [] src)throws异常{
加密密码= Cipher.getInstance( AES / GCM / NoPadding);
cipher.init(Cipher.ENCRYPT_MODE,key);
byte [] iv = cipher.getIV(); //见问题#1
assert iv.length == 12; //见问题#2
byte [] cipherText = cipher.doFinal(src);
assert cipherText.length == src.length + 16; //见问题#3
byte [] message = new byte [12 + src.length + 16]; //见问题#4
System.arraycopy(iv,0,message,0,12);
System.arraycopy(cipherText,0,message,12,cipherText.length);
返回消息;
}

//输入来自用户
byte [] decrypt(byte [] message)throws异常{
if(message.length< 12 + 16)抛出新的IllegalArgumentException();
密码密码= 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()与我自己的计数器?

    • 源代码实现 cipher.getIV()在某个地方可用,假设我使用的是Oracle JDK 8 + JCE无限力量扩展?


  2. 这个IV总是长12个字节吗?


  3. 认证标签是否始终为16字节(128位)长?


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


  5. 我可以安全地显示一个无界数给用户的src数据加密,给定用户知道的常量src数据?


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


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


(因为这些问题都是相同的块我们自己的代码,并且它们彼此密切相关,而在实现相同的功能时,其他人可能/应该有同样的问题,将问题分解成多个帖子是错误的,我可以单独重新发布它们这更适合于StackOverflow的格式,让我知道!)

解决方案

Q1: getIV()可以安全地使用这种方式吗?



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



然而,我仍然建议您生成自己的12个随机字节因为其他提供商可能会有不同的表现。






Q2:IV总是长12个字节?



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






Q3:认证标签是否始终为16字节(128位)?



否,它可以具有从64位到128位(以8位为增量)的字节大小。如果它更小,它只是由认证标签的最左边的字节组成。您可以使用 <$ c指定其他尺寸的标签$ c> GCMParameterSpec 作为 init 调用的第三个参数。



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






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



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






问题5:给定用户知道的常量src数据,是否可以显示无缝数量的src数据加密?



几乎未绑定,是的。大约2 ^ 48加密后,我会开始担心。一般来说,您应该设计更改密钥。






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



查看Q5的答案






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



不,这根本不会有帮助。 GCM使用下面的CTR模式,因此它将只使用密钥流进行加密。它不会作为IV。如果您需要几乎无限数量的密文(高于2 ^ 48!),那么我建议您使用该随机数字和密钥作为密钥导出函数或KDF。 HKDF目前是最好的,但您可能需要使用Bouncy Castle或自行实施。


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.

My main question is this:

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

Does that IV satisfy the requirement of "For a given key, the IV MUST NOT repeat." from 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. Is the IV returned by cipher.getIV() safe for me to use in this way?

    • 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?
  2. Is that IV always 12 bytes long?

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

  4. 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?)

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

  6. 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)?

  7. 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?

(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: Is the IV returned by cipher.getIV() safe for me to use in this way?

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.

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


Q2: Is that IV always 12 bytes long?

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: Is the authentication tag always 16 bytes (128 bits) long?

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.

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: 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?)

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


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

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


Q6: 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)?

See answer to Q5


Q7: 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?

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. If you need a virtually boundless number of ciphertexts (higher than 2^48!) 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.

这篇关于Java AES / GCM / NoPadding - 什么是cipher.getIV()给我?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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