- Находки программиста
- среда, 27 мая 2015 г.
- Https в Android: делаем правильно
- 7 комментариев:
- Android – работа с ssl-сертификатами (как организовать передачу данных через HTTPS)
- Подводные камни HTTPS в Android
- Зацепки
- Рытье
- SSL Server Test (Powered by Qualys SSL Labs)
- A comprehensive free SSL test for your public web servers.
- Решение
- Какую библиотеку работы с HTTP в Android выбрать?
- Для чего это вообще?
- Немного истории
Находки программиста
Решения конкретных задач программирования. Java, Android, JavaScript, Flex и прочее. Настройка софта под Linux, методики разработки и просто размышления.
среда, 27 мая 2015 г.
Https в Android: делаем правильно
To что я опишу ниже — не открытие и не тайна. Это есть даже в официальной документации. Проблема в том, что большинство программистов под Android (и я в том числе) пришли из других, более старых платформ, где проблемы с ssl решались иначе или не возникали вообще.
Например, если сертификат вашего сервера почему-то не в порядке (например он самоподписной), то решение отключить проверку сертификата напрашивается само собой. Такое себе «быстрое решение». В результате вроде бы https и все круто, но на публичном wifi ваш клиент рискует пообщаться по «защищенному» каналу с мошенником. Или, к примеру, вы подключаетесь по ip к своему серверу, а в сертификате прописан домен. Отключаем проверку домена? Давайте не будем спешить. Есть решение которое в «особых» случаях не только не снижает безопасность соединения вашего приложения с сервером но и повышает её до воистину параноидального уровня.
Совсем чуть-чуть теории.
Когда мы устанавливаем https-соединение с сервером, происходит такое себе «рукопожатие», в процессе которого клиент получает публичный ключ сервера. Этот ключ используется для шифрования трафика и для проверки подлинности сервера. Как убедиться что сервер действительно тот, за кого себя выдает? Сертификат, который он предоставляет клиентскому приложению должен быть заверен организацией, чей сертификат есть в системе Android как доверенный и в нём должно быть «зашито» имя домена, соответствующее тому, к которому мы обращаемся. Если мы отключаем проверку, то любой сертификат любого мошенника становится валидным, и вы отправляете ему данные. Он же может общаться с вами, проксируя ваши данные на ваш сервер, который ни о чем не подозревая, обслуживает его как нормального клиента.
Если у вас нет заверенного сертификата, вы можете сгенерировать его себе самостоятельно. Это бесплатно. Но после этого ваше приложение должно доверять этому сертификату «без опоры на авторитеты». Это можно сделать двумя способами: быстрым и правильным. Быстрый — отключить проверку. Правильный: «вшить» публичный ключ вашего сервера в клиентское приложение. Так вы сможете доверять соединению с вашим сервером, не доверяя больше никому, даже самым «супернадежным» сертификатам.
И практика.
Допустим, наш сервер telebank.ru. Он https, причем его сертификат заверен GeoTrust, поэтому мы все ему доверяем. Но допустим, мы хотим принимать его не потому что он заверен кем-то а потому, что он «вшит» в наше приложение. Тогда даже конец света не помешает нам установить защищенное соединение 😉
1. Получим свой публичный ключ:
Из того, что приехало в ответ берём строчки начиная с —BEGIN CERTIFICATE— и заканчивая —END CERTIFICATE—, и сохраняем их в файл, к примеру в telebank.ru.cer. Это и будет наш публичный ключ в base64 представлении.
2. Чтобы работать с ключом, сделаем ему хранилище с помощью BouncyCastle Provider. Скачать его можно, например тут. Скачиваем библиотеку, кладём её рядом с нашим файлом сертификата и выполняем:
И, собственно, наш класс, расширяющий стандартный DefaultHttpClient:
7 комментариев:
Конгениальное решение! Особенно когда завтра у сертификата telebank.ru кончится срок или его перевыпустят по любой другой причине — ваше супергениальное решение немедленно перестанет работать. И пока вас не задолбают благодарные пользователи и вы не перевошьёте другой сертификат — не начнёт. С совершенно валидным сетификатом, подписанным доверенной организацией.
Ваша задача решается куда проще — вы смотрите, доверен ли сертификат и на какой домен он выдан. И неваши домены игнорите. Так вы и используете механизмы подписанных (доверенных) сертификатов, и не разрешаете своему 😉 приложению ходить налево.
Внимательно читая пост, можно заметить что решение предлагается как раз для «особых» случаев, когда сертификат нельзя или нет резона использовать традиционным способом.
Например, ваш сервер используется только как точка доступа для запросов мобильного клиента и не обслуживает браузер в принципе. Зачем покупать для него «совершенно валидный сетификат, подписанный доверенной организацией»?
На счет замены сертификата — этот минус описан, но по-моему замена сертификата раз в несколько лет не должна быть проблемой для мобильного приложения, которое штатно обновляется раз в 2-3 недели.
Ваше решение с проверкой домена — хорошее, но это решение другой задачи. Не пускать приложение «налево» в рамках описанного в посте метода скорее бонус, чем главная цель.
Источник
Android – работа с ssl-сертификатами (как организовать передачу данных через HTTPS)
Что делать, если необходимо получать данные в андроиде через защищенное https-соединение? Почему генерируется ошибка SSLException: Not trusted server certificate ? Как добавить сертификат с сервера в локальное хранилище ключей на андроид-устройстве? Если вас волнуют эти вопросы – вам будет полезна инструкция для установки ssl сертификатов и сниппет кода для их загрузки в ваше андроид-приложение.
1. Как узнать, что за сертификат используется сервером (На Linux/Mac Os – как правило, пакет openssl предустановлен, для Windows – актуальные ссылки в разделе бинарниики там):
openssl s_client -connect :
для самоподписанных сертификатов выведется нечто вроде:
если используется сертификат, подписанный сторонней компанией – будут отображены координаты, по которым можно раздобыть публичный сертификат
2. Создаем файл сертификата
А). Для самоподписанного сертификата:
1. создать пустой файл my-certificate-file.pem
2. скопировать в новый файл закодированные данные сертификата:
да, открывающий\закрывающий теги должны быть.
Если сертификат самоподписанный можно использовать данный bash-script
echo | openssl s_client -connect :
2>/dev/null | sed -ne ‘/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p’ > my-certificate-file.pem
Б). Если сертификат используемый сервером подписан сторонней организацией, на предыдущем шаге мы увидим ее контакты по которым его можно запросить. Почитать про разные типы сертификатов и попробовать онлайн-конвертер из одного типа в другой можно тут – https://www.sslshopper.com/ssl-converter.html
3. Создаем файл-хранилище с этим единственным ключом:
keytool -import -v -trustcacerts -alias myalias -file /bcprov-jdk16-146.jar -storepass
-alias myalias – псевдоним для работы с сертификатом
-keystore mystore.bks – говорим какой файл создать для хранения этого сертификата
-storetype BKS – android поддерживает такой тип хранилища, для того чтобы сгенерировать, его мы должны добавить в наш java CLASSPATH соответствующий jar-файл, последнюю версию которого можно взять отсюда – http://www.bouncycastle.org/latest_releases.html
для создания хранилища необходимо отдельно указать поставщика данного типа хранилища, а также путь к java-классу его реализующего.
(если этого не сделать появляется ошибка: keytool error: java.security.KeyStoreException: BKS not found )
4. Файл-хранилище мы можем добавить себе в проект чтобы использовать на этапе тестирования – например в папку assets. В обычном режиме работы, очевидно, его необходимо считывать из определенной директории для того чтобы работать с актуальной версией сертификата.
5. В android-приложении пригодится следующий сниппет кода:
1 этап – загружаем файл-хранилище с нашим сертификатом:
В реальном приложении, очевидно, файл необходимо будет считывать из предопределенной директории.
2 этап – создаем фабрику управления доверительными соединениями, она будет использоваться для проверки сертифкатов всех https соединений (я еще раз подчеркиваю – ВСЕХ)
После загрузки нашего хранилища ключей мы создаем фабрику для управления доверенными соединениями на основе этого хранилища. С помощью этой фабрики получаем экземпляр менеджера соединений специфичного для ssl (X509TrustManager) и инициализируем объект SSLContext, который будет использоваться для создания защищенного соединения.
3 этап – указываем какие настройки будут использоваться при установлении защищенных соединений:
После этого, если вы все сделали правильно, при каждом https соединении будут вызываться соотвествующие проверки, использующие подготовленный файл хранилище.
UPDATE 26.09.2012:
Хорошая статья по теме использования собственных сертификатов безопасности:
UPDATE 04.10.2012:
С помощью одной целеустремленной читательницы удалось приоткрыть завесу тайны над ошибкой: “SSL handshake terminated: ssl=0x1fcf30: Failure in SSL library, usually a protocol error” возникающей при попытке использовать двусторонюю аутентификацию – т.е. если не только клиент проверяет подлинность сервера но и сервер, проверяет что за клиент к нему лезет.
Источник
Подводные камни HTTPS в Android
Проснувшись прекрасным праздничным посленовогодним утром (в два часа дня) я привычным движением подобрал планшет и тыкнул пальцем в мою разработку чтоб посмотреть на картинку Солнца.
Не сказать что я был удивлен, когда вместо диска обнаружилась пустота, все таки данные берутся с https://sdo.gsfc.nasa.gov/ и уже бывало что он был не доступен целиком или в части фотографий. Однако увы в браузере все грузилось и было на месте, более того даже на своем месте, т.е. это не смена url.
Таким образом меня ждал очень интересный день, ибо что может быть лучше чем ловить внезапный баг в уже более месяца опубликованном приложении 🙂
Зацепки
Первое что бросилось в глаза — сайт перешел с http на https.
Второй сюрприз, в версиях android 4.4.2 и выше все работало как надо. Т.е. мне еще повезло что планшет у меня работает на 4.2.
Ну и третье, единственная ошибка в логах: ”Server closed connection”
Рытье
После этого конечно возникло ощущение что какая то беда в протоколах, был нарыт сайт:
SSL Server Test (Powered by Qualys SSL Labs)
A comprehensive free SSL test for your public web servers.
который собственно помог, его основная функция тестирование ssl сайтов, применительно к нашему подопечному https://www.ssllabs.com/ssltest/analyze.html?d=sdo.gsfc.nasa.gov выяснилось что он поддерживает только два протокола: TLS 1.1 и TLS 1.2, что в общем то правильно так как SSL2–3 признаны устаревшими.
И что самое приятное в результатах теста есть часть: “Hadshake Simulation” где сразу видно что сайт нормально работает только с Android 4.4.2 и выше, а так же видна причина, древние версии андроида не работают с протоколами TLS1.1 и 1.2. Затем выяснилось что начиная с android API16 (4.1) данные протоколы встроены, но отключены по умолчанию. И тут мне повезло так как минимальная поддерживаемая версия android моего приложения как раз 4.1.
Решение
Включаем TLS1.1 и TLS1.2
Моя программа стильна модна молодежна 😀 Т.е. написана на kotlin и использует всякие Dagger2, rxJava, Realm, Retrofit2 и т.п. (при этом кстати весит меньше 8Мб)
В данном контексте важно что для поиска и загрузки изображений используется Retrofit2, который в свою очередь использует okHttpClient в котором есть методы для установки своей sslSocketFactory.
Путем гугления https://github.com/square/okhttp/issues/2372 было найдено готовое решение которое на kotlin выглядит следующим образом:
При использовании ProGuard данный класс пришлось добавить в исключения, иначе хоть сборка и проходила без ошибок в процессе исполнения один класс не находил другой
Так как в котлине возможны экстеншены к классам то можно создать такую симпатичную функцию к OkHttpClient.Bilder’у:
Соответственно в коде включение tls выглядит как будто эта функция имеется в самом классе, примерно так:
В этом один из плюсов kotlin, но это уже другая тема.
После этого все заработало как надо.
PS: Небольшая правда уже не сильно полезная ссылка, подробно показывающая какие протоколы в каких версиях андроида используются
Источник
Какую библиотеку работы с HTTP в Android выбрать?
Представляю вашему вниманию перевод статьи «Which Android HTTP library to use?».
Для чего это вообще?
Немного истории
До Froyo HttpURLConnection имел некоторые не очень приятные баги. В частности, вызов close() у читаемого InputStream мог испортить пул соединений.
… большой размер их API мешает нам улучшать эту библиотеку без потери обратной совместимости. Команда Android не работает активно над Apache HTTP Client.
Apache HTTP client имеет мало багов на Eclair и Froyo, поэтому он является лучшим выбором для этих версий. А для Gingerbread и младше лучше подходит HttpURLConnection. Простота API и небольшой вес хорошо подходят для Android. Прозрачное сжатие и кэширование ответов помогают увеличить скорость и сохранить батарею. Новые приложения должны использовать HttpURLConnection.
Даже сегодня, если вы загляните в исходники Volley от Google (о ней я расскажу чуть попозже), вы сможете найти такое наследие:
Это классический пример фрагментации Android, которая заставляет страдать разработчиков. В 2013 году Square обратила внимание на эту проблему, когда выпускала OkHttp. OkHttp была создана для прямой работы с верхним уровнем сокетов Java, при этом не используя какие-либо дополнительные зависимости. Она поставляется в виде JAR-файла, так что разработчики могут использовать ее на любых устройствах с JVM (куда мы включаем, конечно, и Android). Для упрощения перехода на их библиотеку, Square имплементировали OkHttp используя интерфейсы HttpUrlConnection и Apache client.
OkHttp получила большое распространение и поддержку сообществом, и, в конце-концов, Google решили использовать версию 1.5 в Android 4.4 (KitKat). В июле 2015 Google официально признала AndroidHttpClient, основанный на Apache, устаревшим, вместе с выходом Android 5.1 (Lolipop).
Сегодня OkHttp поставляется со следующим огромным набором функций:
- Поддержка HTTP/2 и SPDY позволяет всем запросам, идущим к одному хосту, делиться сокетом
- Объединение запросов уменьшает время ожидания (если SPDY не доступен)
- Прозрачный GZIP позволяет уменьшить вес загружаемой информации
- Кэширование ответов позволяет избежать работу с сетью при повторных запросах.
- Поддержка как и синхронизированных блокирующих вызовов, так и асинхронных вызовов с обратным вызовом (callback)
Моя самая любимая часть OkHttp – как красиво и аккуратно можно работать с асинхронными запросами:
Это очень удобно, так как работа с сетью не должна быть в UI потоке. По-факту, начиная с Android 3.0 (Honeycomb, API 11), работа с сетью в отдельном потоке стала обязательной. Для того, чтобы воплотить что-то похожее с HtttpUrlConnection, вам потребуется построить большую (а может и монструозную) конструкцию с использованием AsyncTask или отдельного потока. Это будет еще более сложным, если вы захотите добавить отмену загрузки, объединение соединений и т.п.
Кстати, не осталась у обочины и HTTP библиотека от Google под названием Volley, которая предоставляет нам следующие бонусы:
- Автоматическое планирование сетевых запросов
- Множество параллельных сетевых соединений
- Прозрачное кэширование в памяти и на диске, в соответствии со стандартной кэш-согласованностью.
- Поддержка приоритизации запросов.
- Отмена API запросов. Вы можете отменить как один запрос, так и целый блок.
- Простота настройки, например, для повторов и отсрочек.
- Строгая очередность, которая делает легким корректное заполнение данными, полученными асинхронно из сети, интерфейса пользователя.
- Инструменты отладки и трассировки
Все, что ни есть в Volley, находится на вершине HttpUrlConnection. Если вы хотите получить JSON или изображение, то Volley имеет на это специальный абстракции, такие как ImageRequest и JsonObjectRequest, которые помогают вам в автоматическом режиме конвертировать полезную нагрузку HTTP. Так же достойно внимания то, что Volley использует жестко запрограммированный размер сетевого пула:
Когда OkHttp использует поток для каждого вызова с ThreadPoolExecutor с максимальным значением Integer.MAX_VALUE:
В результате, в большинстве случаев OkHttp будет действовать быстрее за счет использования бОльшего количества потоков. Если по каким-то причинам вы захотите использовать OkHttp вместе Volley, то есть реализация HttpStack, которая использует API запросов/ответов из OkHttp заместо HttpURLConnection.
HTTP клиенты продолжили развиваться для поддержки приложений с большим количеством картинок, особенно тех, кто поддерживает бесконечную прокрутку и трансформацию изображений. В то же время, REST API стал стандартом в индустрии, и каждый разработчик имел дело с такими типовыми задачами как сериализация в/из JSON и преобразование REST-вызовов в интерфейсы Java. Не прошло много времени, как появились библиотеки, решающие эти задачи:
- Retrofit – типобезопасный HTTP Android клиент для взаимодействия с REST-интерфейсами
- Picasso – мощная библиотека для загрузки и кэширования изображений под Android
Retrofit предоставляет некий мост между Java кодом и REST-интерфейсом. Он позволяет быстро включить в ваш проект HTTP API интерфейсы, и генерирует самодокументирующуюся реализацию.
В дополнение ко всему, Retrofit поддерживает конвертацию в JSON, XML, протокол буферов (protocol buffers).
Picasso, с другой стороны, предоставляет HTTP библиотеку, ориентированную на работу с изображениями. Например, вы можете загрузить изображение в свой View с помощью одной строчки:
Picasso и Retrofi настроены так, чтобы использовать OkHttpClient как стандартный HTTP клиент. Однако, если хотите, вы можете указать клиентом HttpURLConnection.
Glide – что-то похожее на Picasso. Он предоставляет некоторые дополнительные функции, такие как GIF-анимация, генерация миниатюрных эскизов изображения и неподвижные видео. Полное сравнение можно найти здесь.
Facebook недавно открыли общественности исходный код библиотеки Fresco, которую они используют в своем мобильном приложении. Одна из ключевых функций, которая выделяет ее, — кастомная стратегия выделения памяти для bitmap’ов, чтобы избежать работы долгого GC (сборщик мусора). Fresco выделяет память в регионе, который называется ashmem. Используются некие трюки, чтобы иметь доступ к этому региону памяти доступ как из части, написанной на C++, так и из части на Java. Чтобы уменьшить потребление CPU и данных из сети, эта библиотека использует 3 уровня кэша: 2 в ОЗУ, третий во внутреннем хранилище.
Я нашел необходимым показать отношения между библиотеками на одной схеме. Как вы можете увидеть, HTTP всегда остается внизу у высокоуровневых библиотек. Вы можете выбирать между простым HttpUrlConnection или последним OkHttpClient. Мы используем эту совместимость при разработке PacketZoom Android SDK, о котором мы поговорим в следующем посте.
Недавно мы сравнивали между собой HTTP библиотеки по производительности, но эта тема заслужила отдельного рассмотрения. Мы надеемся, что этот обзор дал вам основные идеи о плюсах и минусах каждой библиотеки, и мы помогли вам сделать правильный выбор. Следите за выходом более интересных постов о сетевой работе в Android от нас.
Источник