Шифрование данных на Android, AES-GCM или простой AES?

Моей команде необходимо разработать решение для шифрования двоичных данных (хранящихся как byte[]) в контексте приложения Android, написанного на Java. Зашифрованные данные будут передаваться и храниться различными способами, при этом не исключено повреждение данных. В конце концов другое приложение Android (опять же написанное на Java) должно будет расшифровать данные.

Уже решено, что алгоритм шифрования должен быть AES с ключом 256 бит. Однако я хотел бы принять осознанное решение о том, какую реализацию AES и / или «режим» мы должны использовать. Я читал о том, что называется режимом GCM, и мы провели с ним несколько тестов (с использованием BouncyCastle / SpongyCastle), но мне не совсем понятно, для чего именно AES-GCM и что он «покупает» нас по сравнению с обычным AES - и нужно ли учитывать какие-либо компромиссы.

Вот список проблем / требований / вопросов, которые у нас есть:

  • Заполнение: данные, которые нам нужно зашифровать, не всегда будут кратны 128 битам, поэтому реализация / режим AES должны добавлять заполнение, но только при необходимости. У меня создалось впечатление, что простая реализация AES, такая как предоставленная javax.crypto.Cipher, этого не сделает, но первоначальные тесты показали, что это так. Поэтому я предполагаю, что требование заполнения само по себе не является причиной прибегать к чему-то вроде GCM вместо "простого" AES. Это верно?

  • Аутентификация. Нам нужен надежный способ определения повреждения данных. Однако в идеале мы также хотим обнаруживать попытки дешифрования с неправильным ключом. Следовательно, мы хотим иметь возможность различать оба этих случая. Причина, по которой я в конечном итоге рассмотрел GCM, была связана с этим вопросом о стеке, где один из респондентов, похоже, подразумевает что сделать это различие возможно с помощью AES-GCM, хотя он не дает подробного объяснения (не говоря уже о коде).

  • Минимизация накладных расходов. Нам необходимо ограничить накладные расходы на хранение и передачу зашифрованных данных. Поэтому мы хотим знать, влияет ли выбор конкретной реализации / режима AES на количество накладных расходов и в какой степени.

  • Производительность шифрования / дешифрования. Хотя это не является основной проблемой, мы задаемся вопросом, в какой степени выбор конкретной реализации / режима AES влияет на производительность шифрования и дешифрования как с точки зрения времени процессора, так и объема памяти.

Заранее благодарим за любые советы, разъяснения и / или примеры кода.

РЕДАКТИРОВАТЬ: Делнан услужливо указал, что не существует такой вещи, как "простой AES". Чтобы прояснить, я имел в виду использование встроенной в Java поддержки AES.
Вот так: Cipher localCipher = Cipher.getInstance("AES");


person Matthias    schedule 16.11.2012    source источник
comment
Что такое простой AES? Если только вы не обрабатываете только 256 бит данных. Если у вас больше данных, вы обязательно используете режим some. Возможно, ваша библиотека по умолчанию выбирает один режим (из того, что я слышал, многие библиотеки делают здесь довольно небезопасный выбор). Таким образом, вы должны проверить, какой режим действительно используется, когда вы используете простой AES.   -  person    schedule 16.11.2012
comment
Спасибо за комментарий @delnan, я внес правку, чтобы прояснить, что я имел в виду.   -  person Matthias    schedule 23.11.2012


Ответы (1)


В 2012 году ответ - использовать GCM, если у вас нет серьезных проблем с совместимостью.

GCM - это режим аутентифицированного шифрования. Он обеспечивает конфиденциальность (шифрование), целостность и аутентификацию (MAC) за один раз.

До сих пор нормальными режимами работы был ECB (который используется по умолчанию для Java ), CBC, CTR, OFB и некоторые другие. Все они обеспечивали только шифрование. Однако конфиденциальность сама по себе редко бывает полезной без целостности; приходилось сочетать такие классические режимы с проверками целостности специальным образом. Поскольку криптографию сложно реализовать правильно, часто такие комбинации были небезопасными, медленнее, чем необходимо, или даже и то, и другое.

Режимы аутентифицированного шифрования были (сравнительно недавно) созданы криптографами для решения этой проблемы. GCM - один из самых успешных: его выбрал NIST, он эффективен, не содержит патентов и может нести дополнительные проверенные данные (то есть данные, которые остаются в открытом виде, но для которых вы можете проверить подлинность). Описание других режимов см. В этой замечательной статье Мэтью Зеленый.

Что касается ваших проблем:

  • Заполнение: по умолчанию Java использует заполнение PKCS # 7. Это работает, но часто уязвимо для атак оракула с дополнением, которые лучше всего победить с помощью MAC. GCM уже встраивает MAC (называемый GMAC).

  • Аутентификация: AES-GCM принимает в качестве входных данных только один ключ AES, а не пароли. Он сообщит вам, неверен ли ключ AES или была подделана полезная нагрузка, но такие условия рассматриваются как одно. Вместо этого вам следует рассмотреть возможность использования соответствующего алгоритма деривации ключа, например PBKDF2 или bcrypt, чтобы получить ключ AES из пароля. Я не думаю, что всегда можно определить, неверен ли пароль или была ли изменена полезная нагрузка, потому что данные, необходимые для проверки первого, всегда могут быть повреждены. Вы можете зашифровать небольшую известную строку (с помощью ECB AES), отправить ее и использовать для проверки правильности пароля.

  • Сведите к минимуму накладные расходы. В конце концов, все режимы приводят к одинаковым накладным расходам (около 10–20 байт), если вам нужна аутентификация. Если вы не работаете с очень маленькими полезными нагрузками, этот момент можно игнорировать.

  • Производительность: GCM довольно хорош в том, что это онлайн режим (нет необходимости буферизовать всю полезную нагрузку, поэтому меньше памяти), он распараллеливается и требует одного AES. операция и одно умножение Галуа на блок открытого текста. Классические режимы, такие как ECB, быстрее (только одна операция AES на блок), но - опять же - вы должны также учитывать логику целостности, которая может оказаться медленнее, чем GMAC.

При этом следует помнить, что безопасность GCM зависит от хорошей генерации случайных чисел для создания IV.

person SquareRootOfTwentyThree    schedule 16.11.2012
comment
Я только что протестировал код Android с использованием AES / EAX на Jelly Bean 4.2. Это не удалось с исключением NoSuchAlgorithmException. Это заставляет меня задуматься. Есть ли список официально поддерживаемых алгоритмов? - person tiguchi; 17.11.2012
comment
@ Nobu-games Вы пробовали код из этот security.SO ответ? - person SquareRootOfTwentyThree; 17.11.2012
comment
В итоге я получил SpongyCastle. Таким образом, я знаю, что мой код шифрования будет работать на любом устройстве Android. Меня только насторожило то, что последний официальный выпуск Android избавился от чего-то, что содержалось в более старых версиях (я мог без проблем использовать AES / EAX на Samsung Galaxy S с Android 2.3). - person tiguchi; 17.11.2012
comment
Спасибо за информативный ответ @SquareRootOfTwentyThree. У меня следующий вопрос: мы хотим использовать SHA-256, чтобы превратить пароль в ключ AES. Чтобы различать неверный пароль и подделанную полезную нагрузку, я рассматривал возможность отправить повторно хешированный пароль (то есть применить SHA-256 к ключу) вместе с полезной нагрузкой, чтобы получатель мог проверить, что он использует правильный пароль. Я понимаю, что сам повторно хешированный пароль также мог быть поврежден во время передачи, так что это, вероятно, не будет полностью надежным. Но тем не менее, как вы думаете, в этом есть смысл? И будет ли это безопасно (т. Е. Не пригодно для использования)? - person Matthias; 23.11.2012
comment
Что касается накладных расходов, наши полезные данные будут варьироваться от 1000 до 3000 байтов. Действительно, такой маленький. - person Matthias; 23.11.2012
comment
Я бы не стал использовать SHA2 для получения пароля. Он слишком быстр и поэтому чувствителен к атакам по словарю и радуге. Предпочтительно PBKDF2 со случайной солью и достаточно большим количеством итераций. Я думаю, что можно взять полученный ключ, хэшировать его (даже с SHA2), чтобы получить контрольное значение и отправить его (с солью). - person SquareRootOfTwentyThree; 24.11.2012