Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Отгремел Google I/O 2019 и пришла пора переписывать проекты на новую архитектуру изучать новинки. Так как я интересуюсь безопасностью мобильных приложений, то в первую очередь обратил внимание на новую библиотеку в семействе JetPack — security-crypto. Библиотека помогает правильно организовывать шифрование данных и при этом ограждает разработчиков от всех нюансов, которые сопровождают этот процесс.
Историческая справка
Шифрование данных в Android всегда порождало много дискуссий. Какой алгоритм выбрать? Какой режим шифрования использовать? Что такое padding? Где хранить ключи? Для обычного разработчика изучать всё это и поддерживать знания в актуальном состоянии может быть трудно. Поэтому история чаще всего заканчивалась одним из трех сценариев:
- копипаст первого попавшегося решения со stackoverflow
- поиск «подходящего мануала» с последующей имплементацией и сбором граблей
- активация протокола «И так сойдет!»
По мере развития сообщества android-разработчиков начали появляться библиотеки, помогающие решать эту задачу. Качество этих решений было очень разным: из всего этого многообразия я могу выделить только java-aes-crypto, которую мы и использовали в Redmadrobot. Довольно качественная имплементация, но с ней была пара проблем.
Во-первых, это было просто шифрование строк. Само по себе это не плохо, но ведь эти строки нужно где-то хранить, в БД или SharedPreferences. А значит, нужно писать обертку над источником данных, чтобы все шифровалось на лету (что мы когда-то и делали). Но это код, который нужно поддерживать, таскать из проекта в проект или оформлять в библиотеку для удобства использования. В конечном итоге это тоже было сделано, но это не принесло успокоения пытливым умам.
Во-вторых, это решение ничего не предлагало для решения проблемы управления ключами. Их можно было сгенерить, но вот хранение полностью ложилось на плечи разработчика. Со всеми приседаниями вокруг AndroidKeystore на разных версиях ОС и устройствах, пришедших с Mainland China.
Этому городу нужен новый герой
Все шло своим чередом, пока летом 2018-го я не обнаружил, что есть такая замечательная библиотека от Google как Tink. Она достаточно проста в освоении и ограждает разработчика от огромного количества нюансов, касающихся криптографии. Используя эту библиотеку, практически невозможно сделать что-то неправильно. Более того, Tink полностью берет на себя управление ключами и абстрагирует все операции с AndroidKeystore от разработчика.
Но это все еще было просто шифрование строк. И тут очень удачно подвернулась Binary Preferences — библиотека от отечественного производителя, на которую давно хотелось посмотреть. Она позволяет шифровать все сохраняемые данные любых алгоритмов — для этого было достаточно написать реализацию двух интерфейсов, KeyEncryption и ValueEncryption (для ключей и значений соответственно).
В итоге мы стали применять две этих библиотеки в связке и были счастливы, что наш код стал чище и проще для понимания.
security-crypto
Теперь Google в очередной раз решил пойти навстречу разработчикам и упростить их жизнь в области шифрования сохраняемых данных. Была анонсирована еще одна JetPack библиотека, которая призвана с этим помочь. Мне стало интересно, что же они такого революционного там написали, и я полез искать документацию (спойлер: ее нет). Нашел только javadoc-и по входящим в состав библиотеки классам, но и на том спасибо. Оказалось, что возможностей там негусто: шифрование файлов, SharedPreferences и работа с ключами.
Для проверки работоспособности библиотеки написал пару снипетов:
val file = File(filesDir, "super_secure_file")
val encryptedFile = EncryptedFile.Builder(file, this, "my_secret_key", EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB)
.setKeysetAlias("my_test_keyset_alias")
.setKeysetPrefName("keyset_pref_file")
.build()
val outputStream = encryptedFile.openFileOutput()
outputStream.use {
it.write("secret info".toByteArray())
}
val encryptedPreferences = EncryptedSharedPreferences.create(
"super_secret_preferences",
"prefrences_master_key",
this,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
encryptedPreferences.edit().putString("secret", "super secret token")
К моему великому удивлению, все заработало с первого раза, и я полез смотреть, что за код они написали для этой библиотеки. Провалившись в исходники, я увидел, что это фактически обертка вокруг уже известной нам библиотеки Tink, а написанный код почти один в один как мы написали для шифрованных BinaryPreferences.
Меня очень обрадовало, что Google в этот раз не стал изобретать веломопед, а использовал свои же хорошо зарекомендовавшие себя наработки. Будем надеяться, что пришедший в JetPack пакет security
не ограничится только этой библиотекой, а будет развиваться дальше.
Демонстрация работы связки BinaryPreferences + Tink
Исходный код библиотеки security-crypto
Демонстрация работы security-crypto