Я создаю шифровальный шифр следующим образом (в Scala, используя bouncy-castle)
def encryptCipher(secret:SecretKeySpec, iv:IvParameterSpec):Cipher = {
val e = Cipher.getInstance("AES/GCM/NoPadding")
e.init(Cipher.ENCRYPT_MODE, secret, iv)
}
Вы видите, что медленная операция генерации ключевой спецификации уже обрабатывается. Однако вызов init для каждого сообщения слишком медленный.
В настоящее время я обрабатываю 50K сообщений, а вызов метода init добавляет почти 4 секунды.
Есть ли способ повторно инициализировать новый IV, который не так интенсивен?
Там нет стандартного способа сделать это в стандартной библиотеке, но есть хороший обходной путь, если вы используете AES:
Цель IV - исключить возможность того, что одни и те же простые тексты шифруются в одни и те же шифрованные тексты.
Вы можете просто "обновить" (как в Cipher.update(byte[])
) со случайным блочным байтовым массивом до шифрования (и с тем же блоком при расшифровке). Это почти точно так же, как с использованием того же случайного блока, что и IV.
Чтобы убедиться в этом, запустите этот фрагмент (который использует вышеуказанный метод для генерации точно такого же текста шифрования), но это только для совместимости с другими платформами, нет необходимости вычислять конкретный IV, чтобы он был безопасным.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom secureRandom = new SecureRandom();
byte[] keyBytes = new byte[16];
secureRandom.nextBytes(keyBytes);
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
byte[] plain = new byte[256];
secureRandom.nextBytes(plain);
// first init using random IV (save it for later)
cipher.init(Cipher.ENCRYPT_MODE, key, secureRandom);
byte[] realIv = cipher.getIV();
byte[] expected = cipher.doFinal(plain);
// now init using dummy IV and encrypt with real IV prefix
IvParameterSpec nullIv = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.ENCRYPT_MODE, key, nullIv);
// calculate equivalent iv
Cipher equivalentIvAsFirstBlock = Cipher.getInstance("AES/CBC/NoPadding");
equivalentIvAsFirstBlock.init(Cipher.DECRYPT_MODE, key, nullIv);
byte[] equivalentIv = equivalentIvAsFirstBlock.doFinal(realIv);
cipher.update(equivalentIv);
byte[] result = cipher.doFinal(plain);
System.out.println(Arrays.equals(expected, result));
Часть расшифровки проще, потому что результат блочного дешифрования XORed с предыдущим зашифрованным текстом (см. Режим работы блочного шифрования), вам просто нужно добавить реальный IV в шифрованный текст и затем его бросить:
// Encrypt as before
IvParameterSpec nullIv = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.DECRYPT_MODE, key, nullIv);
cipher.update(realIv);
byte[] result = cipher.doFinal(encrypted);
// result.length == plain.length + 16
// just throw away the first block