問題描述
我在 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:
- 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? 那個 IV 總是 12 字節長嗎?
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 和身份驗證標簽?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!