将 AES/GCM 的 PBKDF2 盐重新用作 IV:危险吗?
Reusing PBKDF2 salt for AES/GCM as IV: dangerous?
我正在开发一个加密实用程序 class 以重复用于常见操作。
一个非常常见的情况是使用用户提供的密码来加密明文。
在这种情况下,我使用 PBKDF2 派生一个有效的 AES 密钥,然后在 GCM 模式下使用它来加密明文。
一些代码:
// IV_LEN = 96
// ITERATIONS = 1000 ~ 4000
// KEY_LEN = 128 ~ 256
// TAG_LEN = 128
public static String encrypt(byte[] plain, char[] password) throws GeneralSecurityException
{
SecureRandom rng = SecureRandom.getInstanceStrong();
byte[] iv = new byte[IV_LEN / 8];
rng.nextBytes(iv);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
SecretKey derivedKey = factory.generateSecret(new PBEKeySpec(password, iv, ITERATIONS, KEY_LEN));
SecretKey secretKey = new SecretKeySpec(derivedKey.getEncoded(), "AES");
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LEN, iv));
byte[] encrypted = c.doFinal(plain);
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
return encoder.encodeToString(iv) + ":" + encoder.encodeToString(encrypted);
}
目前,我正在使用 PBKDF2 salt(96 位 - SecureRandom)作为 AES/GCM 加密的 IV。
盐和 IV 都可以 public,但它们不应重复使用。
是否可以理解为它们不应在同一个 Feature/Service/Algorithm 中重复使用,或者它们不应在 任何地方 中重复使用?
修改此方法以生成不同的盐和 IV 非常容易,但是有理由这样做吗?
谢谢
请注意,您可能不需要重新生成随机 IV,只要您更改 salt 并因此更改生成的密钥。如果您每次(重新)加密时都没有更改盐,那么您确实需要一个单独的 IV,否则您可能会将信息泄露给对手。
你可以保持盐和IV相同。但通常更容易从密码和盐中导出 IV 和密钥。如果您将 PBKDF2 与 SHA-512 散列一起使用,这很简单:只需从生成的散列中获取 128、192 或 256 位 AES 密钥,然后将另外 128 个后续位作为 IV 并使用它。
如果您需要多个密钥或者如果您使用较小的散列,那么您可能需要从 PBKDF2 的结果中派生出更多的密钥。在那种情况下,最好将 PBKDF2 的结果标记为主密钥并从中执行 N 个密钥派生,一个用于每个密钥,一个用于 IV。你可以使用例如HKDF 为此,Bouncy Castle 有一个实现(我为此提供了初始源代码)。
我正在开发一个加密实用程序 class 以重复用于常见操作。
一个非常常见的情况是使用用户提供的密码来加密明文。
在这种情况下,我使用 PBKDF2 派生一个有效的 AES 密钥,然后在 GCM 模式下使用它来加密明文。
一些代码:
// IV_LEN = 96
// ITERATIONS = 1000 ~ 4000
// KEY_LEN = 128 ~ 256
// TAG_LEN = 128
public static String encrypt(byte[] plain, char[] password) throws GeneralSecurityException
{
SecureRandom rng = SecureRandom.getInstanceStrong();
byte[] iv = new byte[IV_LEN / 8];
rng.nextBytes(iv);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
SecretKey derivedKey = factory.generateSecret(new PBEKeySpec(password, iv, ITERATIONS, KEY_LEN));
SecretKey secretKey = new SecretKeySpec(derivedKey.getEncoded(), "AES");
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LEN, iv));
byte[] encrypted = c.doFinal(plain);
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
return encoder.encodeToString(iv) + ":" + encoder.encodeToString(encrypted);
}
目前,我正在使用 PBKDF2 salt(96 位 - SecureRandom)作为 AES/GCM 加密的 IV。
盐和 IV 都可以 public,但它们不应重复使用。
是否可以理解为它们不应在同一个 Feature/Service/Algorithm 中重复使用,或者它们不应在 任何地方 中重复使用?
修改此方法以生成不同的盐和 IV 非常容易,但是有理由这样做吗?
谢谢
请注意,您可能不需要重新生成随机 IV,只要您更改 salt 并因此更改生成的密钥。如果您每次(重新)加密时都没有更改盐,那么您确实需要一个单独的 IV,否则您可能会将信息泄露给对手。
你可以保持盐和IV相同。但通常更容易从密码和盐中导出 IV 和密钥。如果您将 PBKDF2 与 SHA-512 散列一起使用,这很简单:只需从生成的散列中获取 128、192 或 256 位 AES 密钥,然后将另外 128 个后续位作为 IV 并使用它。
如果您需要多个密钥或者如果您使用较小的散列,那么您可能需要从 PBKDF2 的结果中派生出更多的密钥。在那种情况下,最好将 PBKDF2 的结果标记为主密钥并从中执行 N 个密钥派生,一个用于每个密钥,一个用于 IV。你可以使用例如HKDF 为此,Bouncy Castle 有一个实现(我为此提供了初始源代码)。