Почему для помещения тега аутентификации GCM в конец потока шифрования требуется внутренняя буферизация во время дешифрования?
В Java провайдер AES / GCM по умолчанию SunJCE будет - во время процесса дешифрования - внутренне буферизовать 1)зашифрованные байты, используемые в качестве входных данных или 2)дешифрованные байты, полученные в результате, Код приложения, выполняющий расшифровку, заметит, чтоCipher.update(byte[])
вернуть пустой байтовый массив иCipher.update(ByteBuffer, ByteBuffer)
вернуть записанную длину 0. Затем, когда процесс завершится,Cipher.doFinal()
вернусьвсе декодированные байты.
Первый вопрос: какие байты буферизуются, номер 1 или номер 2 выше?
Я предполагаю, что буферизация происходит только во время дешифрования, а не шифрования, потому что, во-первых, проблемы, которые возникают из-за этой буферизации (кратко описано), не возникают в моем клиенте Java, выполняющем шифрование файлов, считываемых с диска, это всегда происходит на стороне сервера, получать эти файлы и делать расшифровку. Во-вторых, так сказаноВот, Судя только по собственному опыту, я не могу быть уверен, потому что мой клиент используетCipherOutputStream
, Клиент явно не использует методы в экземпляре Cipher. Следовательно, я не могу определить, используется ли внутренняя буферизация или нет, потому что я не вижу, что возвращает метод update и final.
Мои настоящие проблемы возникают, когда зашифрованные файлы, которые я передаю от клиента к серверу, становятся большими. Я имею в виду более 100 МБ.
Затем происходит то, что Cipher.update () генерируетOutOfMemoryError
, Очевидно, из-за внутреннего буфера растет и растет.
Кроме того, несмотря на внутреннюю буферизацию и отсутствие байтов результата, полученных от Cipher.update (),Cipher.getOutputSize (целое) постоянно сообщать о постоянно растущей целевой длине буфера. Следовательно, мой код приложения вынужден выделять постоянно растущийByteBuffer
это подача в Cipher.update (ByteBuffer, ByteBuffer). Если я попытаюсь обмануть и передать в байтовый буфер с меньшей емкостью, то метод обновления выбрасываетShortBufferException
# 1, Знание того, что я создаю огромные байтовые буферы без толку, довольно деморализует.
Учитывая, что внутренняя буферизация является корнем всего зла, то очевидное решение, которое я могу применить здесь, состоит в том, чтобы разбить файлы на куски, скажем, по 1 МБ каждый - у меня никогда не возникает проблем при отправке небольших файлов, только больших. Но я изо всех сил пытаюсь понять, почему внутренняя буферизация происходит в первую очередь.
Ранее связанныйТак ответь говорит, что тег аутентификации GCM: «добавляется в конец зашифрованного текста», но «его не нужно ставить в конце», и именно эта практика «портит онлайн-характер дешифрования GCM».
Почему размещение тега в конце только портит работу сервера по расшифровке?
Вот как мои рассуждения. Чтобы вычислить тег аутентификации или MAC, если хотите, клиент использует какую-то хеш-функцию. По-видимому,MessageDigest.update()
не использует постоянно растущий внутренний буфер.
Тогда на принимающей стороне сервер не может сделать то же самое? Для начала он может расшифровать байты, хотя и не прошедшие проверку подлинности, передать их в свою функцию обновления алгоритма хеширования, а когда тег прибудет, завершить дайджест и проверить MAC, отправленный клиентом.
Я не крипто-парень, поэтому, пожалуйста, говорите со мной, как будто я и тупой, и сумасшедший, но достаточно любящий, чтобы заботиться о некоторых =) Я от всего сердца благодарю вас за время, потраченное на чтение этого вопроса и, возможно, даже пролить немного света!
ОБНОВЛЕНИЕ № 1Я не использую AD (связанные данные).
ОБНОВЛЕНИЕ № 2Написал программное обеспечение, демонстрирующее шифрование AES / GCM с использованием Java, а такжеБезопасный удаленный протокол (SRP) и передача двоичных файлов в Java EE. Клиентский интерфейс написан на JavaFX и может использоваться для динамического изменения конфигурации шифрования или отправки файлов с использованием кусков. В конце передачи файла представлена некоторая статистика времени, использованного для передачи файла, и времени, которое сервер расшифровывает. В хранилище также есть документ с некоторыми моими собственными исследованиями, связанными с GCM и Java.
Наслаждаться:https://github.com/MartinanderssonDotcom/secure-login-file-transfer/
# 1Интересно отметить, что если мой сервер, который выполняет расшифровку, не обрабатывает сам шифр, вместо этого он используетCipherInputStream
, тогда OutOfMemoryError не выбрасывается. Вместо этого клиенту удается передать все байты по проводам, но где-то во время расшифровки поток запроса зависает на неопределенное время, и я вижу, что один поток Java (может быть одним и тем же потоком) полностью использует ядро ЦП, все время оставляя файл включенным. диск недоступен и с сообщенным размером файла 0. Затем по истечении чрезвычайно длительного промежутка времениCloseable
источник закрыт, и моему предложению catch удается перехватить IOException, вызванное: «javax.crypto.AEADBadTagException: ввод слишком короткий - тег тега».
Что делает эту ситуацию странной, так это то, что передача файлов меньшего размера работает безупречно с одним и тем же фрагментом кода, поэтому очевидно, что тег можно правильно проверить. Проблема должна иметь ту же основную причину, что и при явном использовании шифра, то есть постоянно растущий внутренний буфер. Я не могу отследить на сервере, сколько байтов было успешно прочитано / расшифровано, потому что, как только начинается чтение входного потока шифра, переупорядочивание компилятора или другие JIT-оптимизации заставляют все мои операторы журналирования испаряться. Они [очевидно] не казнены вообще.
Обратите внимание, чтоэтот проект GitHub и его связанныйСообщение блога говорит, что CipherInputStream не работает. Но тесты, предоставленные этим проектом, не являются для меня провальными при использовании Java 8u25 и поставщика SunJCE. И, как уже было сказано, у меня все работает, если я использую только небольшие файлы.