"AES/GCM/NoPadding" 的 IV 和身份验证标签是如何处理的?
How are the IV and authentication tag handled for "AES/GCM/NoPadding"?
我在 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?
对于我的相关问题(见下文)的任何答案/见解,我也很感激,但第一个问题最让我烦恼。我不知道在哪里可以找到回答这个问题的源代码或文档。
这是完整的代码。如果我在写这篇文章时引入了错误,我深表歉意 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);
}
}
假设用户破解了我的秘钥=游戏结束。
更详细的问题/相关问题:
- 我以这种方式使用 cipher.getIV() 返回的 IV 安全吗?
- 它是否避免了在 Galois/Counter 模式下重用 IV、组合键的灾难?
- 当我同时有多个应用程序 运行 这段代码时,它仍然安全吗,所有应用程序都从相同的 src 数据(可能在同一毫秒内)向用户显示加密消息?
- 返回的 IV 是由什么制成的?它是一个原子计数器加上一些随机噪声吗?
- 我是否需要避免
cipher.getIV()
并使用我自己的计数器自己构建 IV?
- 假设我使用的是 Oracle JDK 8 + JCE Unlimited Strength 扩展,实现
cipher.getIV()
的源代码是否可以在线获取?
IV 总是 12 字节长吗?
身份验证标记是否始终为 16 字节(128 位)长?
使用#2 和#3,并且没有填充,这是否意味着我的加密消息总是 12 + src.length + 16
字节长? (这样我就可以安全地将它们压缩成一个字节数组,我知道正确的长度?)
给定用户知道的恒定 src 数据,我向用户显示无限数量的 src 数据加密是否安全?
如果 src 数据每次都不同(例如包括 System.currentTimeMillis()
或随机数),我向用户显示无限数量的 src 数据加密是否安全?
如果我在加密前用随机数填充 src 数据会有帮助吗?说前后8个随机字节,还是只在一端?还是那根本没有帮助/使我的加密变得更糟?
(因为这些问题都是关于我自己代码的同一个block,并且彼此之间有很强的相关性,而其他人might/should在实现相同功能时也有同一套问题,所以感觉将问题拆分为多个 post 是错误的。如果这更适合 Whosebug 的格式,我可以单独重新 post 它们。让我知道!)
Q1:cipher.getIV() 返回的 IV 对我来说安全吗?
是的,它至少是为 Oracle 提供的实现。它是使用默认 SecureRandom
实现单独生成的。因为它的大小是 12 个字节(GCM 的默认值),所以你有 96 位的随机性。计数器重复的机会非常小。您可以在 Oracle JDK 所基于的 OpenJDK(GPL'ed)中查找源代码。
不过,我仍然建议您生成自己的 12 个随机字节,因为其他提供商的行为可能有所不同。
问题 2:IV 总是 12 字节长吗?
极有可能,因为它是 GCM 默认值,但其他长度 对 GCM 有效。但是,该算法必须对 12 字节以外的任何其他大小进行额外计算。由于弱点,强烈建议将其保持在 12 字节/96 位,并且 API 的 可能会限制您选择 IV 大小 。
Q3:认证标签总是16字节(128位)长吗?
不,它可以有任何字节大小,从 64 位到 128 位,递增 8 位。如果它更小,它只包含身份验证标记的最左边的字节。您可以使用 GCMParameterSpec
作为您的 init
调用的第三个参数指定另一个标签大小。
请注意,GCM 的强度在很大程度上取决于标签的大小。我建议将其保留为 128 位。 96 位应该是最小值 特别是 如果你想生成大量密文。
Q4:有了#2 和#3,并且没有填充,这是否意味着我的加密消息总是 12 + src.length + 16 字节长? (所以我可以安全地将它们压缩成一个字节数组,我知道正确的长度?)
见上文。对于 Oracle 提供商来说就是这种情况。使用GCMParameterSpec
来确定它。
Q5:给定用户知道的恒定 src 数据,我向用户显示无限数量的 src 数据加密是否安全?
几乎 未绑定,是的。大约 2^48 次加密后我会开始担心。不过,一般来说,您应该 为 密钥更改进行设计。
Q6:如果 src 数据每次都不同(例如包括 System.currentTimeMillis() 或随机数),我向用户显示无限数量的 src 数据加密是否安全)?
查看问题 5 和问题 7 的答案
Q7:如果我在加密前用随机数填充src数据会有帮助吗?说前后8个随机字节,还是只在一端?还是那根本没有帮助/使我的加密变得更糟?
不,这根本没有帮助。 GCM 在底层使用 CTR 模式,所以它只会用密钥流加密。它不会充当IV。现在,如果您要加密不断变化的消息,您可以查看 AES-GCM-SIV,但请注意,该算法未在任何 JCA 提供程序中实现。
如果您需要大量密文(高于 2^48!或 2^32 - ~40 亿 - 谨慎起见)那么我建议您使用该随机数和您的密钥作为密钥派生函数或 KDF。 HKDF 目前是最好的,但您可能需要使用 Bouncy Castle 或自己实现它。
我在 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?
对于我的相关问题(见下文)的任何答案/见解,我也很感激,但第一个问题最让我烦恼。我不知道在哪里可以找到回答这个问题的源代码或文档。
这是完整的代码。如果我在写这篇文章时引入了错误,我深表歉意 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);
}
}
假设用户破解了我的秘钥=游戏结束。
更详细的问题/相关问题:
- 我以这种方式使用 cipher.getIV() 返回的 IV 安全吗?
- 它是否避免了在 Galois/Counter 模式下重用 IV、组合键的灾难?
- 当我同时有多个应用程序 运行 这段代码时,它仍然安全吗,所有应用程序都从相同的 src 数据(可能在同一毫秒内)向用户显示加密消息?
- 返回的 IV 是由什么制成的?它是一个原子计数器加上一些随机噪声吗?
- 我是否需要避免
cipher.getIV()
并使用我自己的计数器自己构建 IV? - 假设我使用的是 Oracle JDK 8 + JCE Unlimited Strength 扩展,实现
cipher.getIV()
的源代码是否可以在线获取?
IV 总是 12 字节长吗?
身份验证标记是否始终为 16 字节(128 位)长?
使用#2 和#3,并且没有填充,这是否意味着我的加密消息总是
12 + src.length + 16
字节长? (这样我就可以安全地将它们压缩成一个字节数组,我知道正确的长度?)给定用户知道的恒定 src 数据,我向用户显示无限数量的 src 数据加密是否安全?
如果 src 数据每次都不同(例如包括
System.currentTimeMillis()
或随机数),我向用户显示无限数量的 src 数据加密是否安全?如果我在加密前用随机数填充 src 数据会有帮助吗?说前后8个随机字节,还是只在一端?还是那根本没有帮助/使我的加密变得更糟?
(因为这些问题都是关于我自己代码的同一个block,并且彼此之间有很强的相关性,而其他人might/should在实现相同功能时也有同一套问题,所以感觉将问题拆分为多个 post 是错误的。如果这更适合 Whosebug 的格式,我可以单独重新 post 它们。让我知道!)
Q1:cipher.getIV() 返回的 IV 对我来说安全吗?
是的,它至少是为 Oracle 提供的实现。它是使用默认 SecureRandom
实现单独生成的。因为它的大小是 12 个字节(GCM 的默认值),所以你有 96 位的随机性。计数器重复的机会非常小。您可以在 Oracle JDK 所基于的 OpenJDK(GPL'ed)中查找源代码。
不过,我仍然建议您生成自己的 12 个随机字节,因为其他提供商的行为可能有所不同。
问题 2:IV 总是 12 字节长吗?
极有可能,因为它是 GCM 默认值,但其他长度 对 GCM 有效。但是,该算法必须对 12 字节以外的任何其他大小进行额外计算。由于弱点,强烈建议将其保持在 12 字节/96 位,并且 API 的 可能会限制您选择 IV 大小 。
Q3:认证标签总是16字节(128位)长吗?
不,它可以有任何字节大小,从 64 位到 128 位,递增 8 位。如果它更小,它只包含身份验证标记的最左边的字节。您可以使用 GCMParameterSpec
作为您的 init
调用的第三个参数指定另一个标签大小。
请注意,GCM 的强度在很大程度上取决于标签的大小。我建议将其保留为 128 位。 96 位应该是最小值 特别是 如果你想生成大量密文。
Q4:有了#2 和#3,并且没有填充,这是否意味着我的加密消息总是 12 + src.length + 16 字节长? (所以我可以安全地将它们压缩成一个字节数组,我知道正确的长度?)
见上文。对于 Oracle 提供商来说就是这种情况。使用GCMParameterSpec
来确定它。
Q5:给定用户知道的恒定 src 数据,我向用户显示无限数量的 src 数据加密是否安全?
几乎 未绑定,是的。大约 2^48 次加密后我会开始担心。不过,一般来说,您应该 为 密钥更改进行设计。
Q6:如果 src 数据每次都不同(例如包括 System.currentTimeMillis() 或随机数),我向用户显示无限数量的 src 数据加密是否安全)?
查看问题 5 和问题 7 的答案
Q7:如果我在加密前用随机数填充src数据会有帮助吗?说前后8个随机字节,还是只在一端?还是那根本没有帮助/使我的加密变得更糟?
不,这根本没有帮助。 GCM 在底层使用 CTR 模式,所以它只会用密钥流加密。它不会充当IV。现在,如果您要加密不断变化的消息,您可以查看 AES-GCM-SIV,但请注意,该算法未在任何 JCA 提供程序中实现。
如果您需要大量密文(高于 2^48!或 2^32 - ~40 亿 - 谨慎起见)那么我建议您使用该随机数和您的密钥作为密钥派生函数或 KDF。 HKDF 目前是最好的,但您可能需要使用 Bouncy Castle 或自己实现它。