Подтвердить что ты не робот

Java AES/GCM/NoPadding - Что мне дает cipher.getIV()?

Я использую шифрование AES/GCM/NoPadding в Java 8, и мне интересно, имеет ли мой код недостаток безопасности. Мой код, похоже, работает, поскольку он шифрует и расшифровывает текст, но некоторые детали неясны.

Мой главный вопрос:

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

Соответствует ли это требованиям IV требованиям "Для данного ключа IV НЕ ДОЛЖЕН повторять". от RFC 4106

Я также благодарен за любые ответы/понимание моих связанных вопросов (см. ниже), но этот первый вопрос вызывает больше всего. Я не знаю, где найти исходный код или документацию, которая отвечает на это.


Вот полный код, грубо говоря. Приносим извинения в случае, если я ввел ошибки при написании сообщения:

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);
  }
}

Предположим, что пользователи взломали мой секретный ключ = игра.


Более подробные вопросы/связанные вопросы:

  • Является ли IV, возвращенный cipher.getIV() безопасным для меня таким образом?

    • Помогает ли это избежать катастрофы повторного использования комбинации IV, клавиш в режиме Galois/Counter?
    • Является ли это еще безопасным, когда у меня есть несколько приложений, которые запускают этот код сразу, все отображают зашифрованные сообщения пользователям из одних и тех же данных src (возможно, в том же миллисекунде)?
    • Из чего вернулся возвращенный IV? Это атомный счетчик плюс некоторый случайный шум?
    • Нужно ли избегать cipher.getIV() и построить сам IV, со своим собственным счетчиком?
    • Является ли исходный код, реализующий cipher.getIV() доступным в Интернете, если я использую расширение Oracle JDK 8 + JCE Unlimited Strength?
  • Является ли это IV всегда длиной в 12 байтов?

  • Является ли тег аутентификации всегда длиной в 16 байтов (128 бит)?

  • С# 2 и # 3 и отсутствием заполнения, означает ли это, что мои зашифрованные сообщения всегда 12 + src.length + 16 bytes long? (И поэтому я могу смело просунуть их в один байт-массив, для которого я знаю правильную длину?)

  • Безопасно ли мне отображать неограниченное количество шифрованных данных src для пользователей, учитывая постоянные данные src, которые знают пользователи?

  • Безопасно ли мне отображать неограниченное количество шифрованных данных src для пользователей, если данные src различаются каждый раз (например, включая System.currentTimeMillis() или случайные числа)?

  • Помогло бы, если бы я дополнил данные src случайными числами до шифрования? Скажите 8 случайных байтов спереди и сзади или только на одном конце? Или это вообще не поможет/ухудшит шифрование?

(Поскольку эти вопросы касаются одного и того же блока моего собственного кода, и они сильно связаны друг с другом, а другие могут/должны иметь одинаковый набор вопросов при реализации одной и той же функциональности, было бы неправильно разделять вопросы в несколько сообщений.Я могу повторно опубликовать их отдельно, если это более подходит для формата StackOverflow. Сообщите мне!)

4b9b3361

Ответ 1

Q1: Является ли IV, возвращенный cipher.getIV() безопасным для меня таким образом?

Да, это, по крайней мере, для реализации Oracle. Он генерируется отдельно, используя реализацию по умолчанию SecureRandom. Поскольку размер 12 байтов (по умолчанию для GCM), у вас есть 96 бит случайности. Вероятность повторения счетчика крайне мала. Вы можете найти источник в OpenJDK (GPL'ed), на котором основан Oracle JDK.

Тем не менее я бы порекомендовал вам генерировать ваши собственные 12 случайных байтов, поскольку другие поставщики могут вести себя по-другому.


Q2: Этот 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 для пользователей, если данные src различаются каждый раз (например, включая System.currentTimeMillis() или случайные числа)?

См. ответ на Q5


Q7: Помогло бы мне, если бы я заполнил данные src случайными числами до шифрования? Скажите 8 случайных байтов спереди и сзади или только на одном конце? Или это вообще не поможет/ухудшит мое шифрование?

Нет, это не помогло бы вообще. GCM использует режим CTR внизу, поэтому он просто будет зашифрован с помощью ключевого потока. Он не будет действовать как IV. Если вам нужно практически безграничное количество зашифрованных текстов (выше 2 ^ 48!), Я бы предложил вам использовать это случайное число и ваш ключ для функции деривации ключа или KDF. HKDF в настоящее время является лучшим в породе, но вам, возможно, придется использовать Bouncy Castle или реализовать его самостоятельно.