Ни одна из реализаций, которые вы даете в своем вопросе, не является полностью правильной, и ни одна из реализаций, которую вы даете, не должна использоваться как есть. В дальнейшем я буду обсуждать аспекты шифрования на основе паролей в Android.
Ключи и хэши
Я начну обсуждение парольной системы с солями.Соль - это случайное число.Это не "вывод".Реализация 1 включает метод generateSalt()
, который генерирует криптографически сильное случайное число.Поскольку соль важна для безопасности, она должна храниться в секрете после генерации, хотя ее нужно генерировать только один раз.Если это веб-сайт, то хранить секретность относительно легко, но для установленных приложений (для настольных и мобильных устройств) это будет гораздо сложнее.
Метод getHash()
возвращает хэшданный пароль и соль, объединенные в одну строку.Используется алгоритм SHA-512, который возвращает 512-битный хэш.Этот метод возвращает хеш, который полезен для проверки целостности строки, поэтому его также можно использовать, вызывая getHash()
только с паролем или просто солью, поскольку он просто объединяет оба параметра.Поскольку этот метод не будет использоваться в системе шифрования на основе пароля, я не буду обсуждать его дальше.
Метод getSecretKey()
, получает ключ из массива char
пароля ишестнадцатеричная соль, возвращенная из generateSalt()
.Используемый алгоритм - это PBKDF1 (я думаю) от PKCS5 с SHA-256 в качестве хэш-функции и возвращает 256-битный ключ.getSecretKey()
генерирует ключ путем многократного генерирования хэшей пароля, соли и счетчика (вплоть до числа итераций, указанного в PBE_ITERATION_COUNT
, здесь 100), чтобы увеличить время, необходимое для проведения атаки методом перебора.Длина соли должна составлять не менее длины генерируемого ключа, в данном случае не менее 256 бит.Счетчик итераций должен быть установлен как можно дольше, не вызывая необоснованной задержки.Для получения дополнительной информации о солях и количестве итераций при получении ключа см. Раздел 4 в RFC2898 .
Однако реализация в PBE Java имеет недостатки, если пароль содержит символы Unicode, то естьте, которые требуют более 8 бит для представления.Как указано в PBEKeySpec
, «механизм PBE, определенный в PKCS # 5, рассматривает только младшие 8 бит каждого символа».Чтобы обойти эту проблему, вы можете попытаться сгенерировать шестнадцатеричную строку (которая будет содержать только 8-битные символы) из всех 16-битных символов в пароле перед передачей его на PBEKeySpec
.Например, «ABC» становится «004100420043».Также обратите внимание, что PBEKeySpec «запрашивает пароль как массив символов, поэтому его можно переписать [с clearPassword()
] после завершения».(Что касается "защиты строк в памяти", см. этот вопрос .) Однако я не вижу никаких проблем с представлением соли в виде шестнадцатеричной строки.
Шифрование
Как только ключ сгенерирован, мы можем использовать его для шифрования и дешифрования текста.В реализации 1 используется алгоритм шифрования AES/CBC/PKCS5Padding
, то есть AES в режиме шифрования Cipher Block Chaining (CBC) с заполнением, определенным в PKCS # 5.(Другие режимы шифрования AES включают режим счетчика (CTR), режим электронной кодовой книги (ECB) и режим счетчика Галуа (GCM). Еще один вопрос о переполнении стека содержит ответы, в которых подробно обсуждаются различные режимы шифрования AES ирекомендуемые для использования. Также имейте в виду, что существует несколько атак на шифрование в режиме CBC, некоторые из которых упомянуты в RFC 7457.)
Если зашифрованный текст будет доступен посторонним, то для защиты его целостности рекомендуется применять код аутентификации сообщения или MAC к зашифрованным данным (и, необязательно, дополнительные параметры) (метод, известный как аутентифицированное шифрование)со связанными данными , AEAD, описанными в RFC 5116).Здесь популярны MAC-адреса на основе хеш-функции или HMAC, основанные на SHA-256 или других безопасных хеш-функциях.Однако, если используется MAC, рекомендуется использовать секрет, который, как минимум, вдвое дольше обычного ключа шифрования, чтобы избежать атак с использованием соответствующего ключа: первая половина служит ключом шифрования, а вторая половина служит ключом дляMAC.(То есть в этом случае сгенерируйте один секрет из пароля и соли и разделите этот секрет на два.)
Реализация Java
Различные функции вВ реализации 1 используют определенный поставщик, а именно «BC», для своих алгоритмов.В целом, однако, не рекомендуется запрашивать конкретных поставщиков, так как не все поставщики доступны во всех реализациях Java, будь то из-за отсутствия поддержки, чтобы избежать дублирования кода или по другим причинам.Этот совет стал особенно важным после выпуска предварительного просмотра Android P в начале 2018 года, потому что некоторые функции от поставщика «BC» там устарели - см. Статью «Изменения в криптографии в Android P» в блоге разработчиков Android.См. Также Введение в провайдеров Oracle .
Таким образом, PROVIDER
не должно существовать и строка -BC
должна быть удалена из PBE_ALGORITHM
.В этом отношении реализация 2 верна.
Неуместно, чтобы метод перехватывал все исключения, а обрабатывал только те исключения, которые он может.Реализации, приведенные в вашем вопросе, могут создавать различные проверенные исключения.Метод может выбрать обтекание только тех проверенных исключений с помощью CryptoException или указать эти проверенные исключения в предложении throws
.Для удобства здесь может быть уместно оборачивать исходное исключение с помощью CryptoException, поскольку существует потенциально много проверенных исключений, которые могут выдавать классы.
SecureRandom
в Android
Asподробно описано в статье «Некоторые мысли о SecureRandom» в блоге разработчиков Android. Реализация java.security.SecureRandom
в выпусках Android до 2013 года имеет недостаток, который уменьшает силу случайных чисел, которые она доставляет.Этот недостаток можно устранить, передав непредсказуемый и случайный блок данных (например, вывод /dev/urandom
) методу setSeed
этого класса.