Безопасно подписываем Android сборки из Jenkins

  • Tutorial

Перевод https://www.detroitlabs.com/blog/2017/05/24/securely-signing-jenkins-android-builds/



Безопасная подпись Android сборок в Jenkins CI (Continuous Integration, далее просто CI) это общая проблема. Мы попробовали несколько вариантов за всё время разработки и каждый из них выглядел немного грязновато… кроме одного.


Сначала, небольшое вступление: CI это очень значимый инструмент для команды разработки. Когда он используется правильно и часто, он может значительно уменьшить время тратящееся на отладку и разрешение конфликтов. Проблемы видны всей команде и могут быть быстро решены, результаты сборки помогают определить корень проблемы, а тестирование помогает предотвратить появление регрессионных ошибок. CI также автоматизирует доставку ваших сборок, так что тестировщики или клиенты мгновенно получают последние результаты работы.


Итак, наши варианты:


Включить подписывающий сертификат для сборки, непосредственно в сам репозиторий: Плохо!


Только если вы не используете закрытый репозиторий, вы на самом деле открываете свой сертификат всему миру. Даже если вы используете закрытый репозиторий, вы размещаете сертификат на машине каждого разработчика, что сильно увеличивает шансы что сертификат утечёт.


Загрузить подписывающий сертификат в файловую систему Jenkins и ссылаться на него из Gradle: Неплохо...


Это неплохо потому что это позволяет нам использовать минимальное количество мест и может быть защищено теми же средствами, что регулируют доступ к Jenkins. Один нюанс, это то что вы всё ещё должны будете сохранять свой пароль и имя ключа где-то в файловой системе. Поиск способа сделать это безопасно может быть сложным. Данное решение также может оказаться не самым простым в использовании, если у вас есть несколько проектов и несколько сборок и каждой из них нужен сертификат.


Использование плагина для подписи Android приложений(Android Signing Plugin): Отлично!


Это замечательное, чистое решение для хранения и подписи приложений на вашем Jenkins. Плагин использует стандартное хранилище Jenkins для работы с вашими сертификатами. Что ограничивает число разработчиков которым нужно знать пароли, помогает безопасно хранить сертификаты, и позволяет всем сборкам легко получать доступ к сертификату.


Как настроить и использовать Jenkins Android Signing Plugin:


Первое, что вам нужно это убедиться что в вашем Jenkins установлен Credentials Plugin.
Это легко можно проверить взглянув на левую панель в вашем Jenkins. Если вы видите "Credentials" секцию, как показано ниже, то он установлен.
Если у вас нет секции "Credentials", вам нужно не забыть установить Credentials Plugin, это можно сделать одновременно с установкой Android Signing Plugin.


Установка плагина происходит в несколько не сложных шагов:


  1. Выбирете “Manage Jenkins” секцию как показано ниже.


  2. Выберете "Manage Plugins."


  3. Вы должны будете увидеть доступные обновления для текущих плагинов. В верху экрана выберете вкладку "Available".


  4. Теперь вы можете использовать поиск в правом верхнем углу для того, чтобы найти "Credentials Plugin" (Если он не установлен) и "Android Signing Plugin". Поставьте галочки на левой стороне для каждого плагина, затем нажмите "Download now and install after restart" внизу экрана.

Отлично!
Теперь, когда всё установлено, вы можете добавить как минимум один сертификат для подписи приложений. Детальные инструкции по использованию "Credentials Plugin" не являются темой этой статьи, но могут быть легко найдены в интернете. Вам нужно добавить один новый сертификат, как показано ниже.


Как видно, плагин поддерживает только PKCS12 сертификаты. К сожалению, последняя версия Android Studio выдаёт JKS сертификаты, которые оказываются несовместимы с плагином.
Хорошо, что мы имеем удобную утилиту для командной строки "keytool", которая может превратить наш "JKS" в "PKCS12".


keytool -importkeystore -srckeystore {REPLACE_WITH_JKS_FILE} -srcstoretype JKS -deststoretype PKCS12 -destkeystore ConvertedCertificate.p12

Как только у вас будет "PKCS12" файл, вы можете загрузить его. Убедитесь, что ввели пароль, до того как загрузили сертификат, иначе Jenkins не сможет загрузить файл. Теперь вы готовы использовать этот сертификат для любых Android сборок.


Единственно требование к исходному коду, это то что вам нужно оставить "signingConfig" пустым для "buildType", который будет использоваться при сборке Jenkins'ом. Тогда будет создаваться неподписанный APK, который может быть подписан плагином. Имейте в виду, стандартный debug билд подписывается автоматически сгенерированным сертификатом.


Теперь всё готово для подписи приложений, вам нужно добавить шаг "Sign Android APKs" в вашу сборку. Ниже простой пример, сначала мы запускаем Gradle команду для сборки неподписанного релизного билда. После этого, на следующем шаге мы можем подписать билд. Вы должны выбрать сертификат, который вы хотели бы использовать из хранилища сертификатов, указать алиас ключа и путь к неподписанному APK.


Это всё! У вас теперь есть подписанный билд, готовый к распространению сразу же после сборки.


К слову у Google есть своё хранилище сертификатов, которое частично решает проблему с безопасным хранением ключа.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Где я храню резервную копию сертификата
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 15
    –2
    Это всё конечно безумно интересно, но есть ли возможность какого-то практического применения этой подписи? Например для защиты приложения от изменения? Только без этих всяких «мы ща обратимся к java… что бы проверить...» это бред. Что-то реальное?
      +2
      Она везде практически применяется. И защищает приложение от изменения. Изменить что-то в подписанном файле уже не получится. Соответсвенно не получится, например, добавить какой-то код чтобы следить за пользователем в приложение известной социальной сети и установить его не приложение жертвы. За подленностью сертификата и подписанного им кода будет следить система Android, которая в итоге и не даст изменённое приложение запустить.
        0
        Это известная рекламная телега от гугла, которая ни как не связана с реалиями. Ведь если бы это было так, то не наводнили 4пда и прочие сайты взломанные версии ПО с гугльплея.
        У меня тот же скайп в телефоне мало того, что отвязан от рекламы, да ещё добавлены ништяки типа кнопки выхода, которых в оригинальной версии нет.
        Понятно, что если писать проверку подписи на интерпретируемых языках, то любой школьник её может убрать за пол часа. Что бы проверить целостность apk нужно писать проверку на c++ иди Delphi. Но всё равно этот код обращается к функциям ОС через java прослойку, которую не сложно изменить, и возвращать требуемое значение, а не реальное. Вот мне и интересно: есть ли способы борьбы с этим — что бы ndk обращалось к ос непосредственно, а не через Dalvik/ART. Что бы хоть кулхацкеров отсеять.
          0

          Всё дело в том, что Android в принципе разрешает ставить приложения из сторонних источников. Чтобы это убрать нужно делать как на iOS, но я до конца не знаю как там всё устроено. Во всяком случае как раз таки jailbreak ломает проверку устанавливающихся приложений.


          В андроид проверку сертификата никто никуда не убирает. Приложения на 4пда так-же подписаны, только каким-то левым сертификатом. Т.е. для системы это получается совершенно другое приложение, хоть и созданное на основе оригинального.

        +1
        В общем случае нет. Вы можете запутывать (obfuscate) код в том числе код проверки подписи приложния. Есть dexguard, например. Еще можете реализовать проверку лицензии developer.android.com/google/play/licensing/overview.html. Последняя выполняется с помощью google play services, в другом процессе. Конечно можно декомпильнуть и вырезать эти проверки, но тогда часть функционала приложения(платные фичи) могут перестать работать. И каждый новый релиз придется взламывать заново. В итоге забивают на часть юзеров, что юзает 4пда.
          0
          Обфускация — это лишь замена настоящих названий функций/переменных на слабо воспринимаемое человеком. Плюс запутывание кода. Она полезна для защиты от читеров в играх, но обращения к api запутать нельзя.
          Вот хочу я, например, показать баннер. За это отвечает пара классов. Взломщик их просто вычищает, не разбираясь с хитросплетениями запутанного кода. Тоже самое с вызовом проверки лицензии, или проверки подписи. Надо просто вызов функции одного класса заменить на вызов другого. Время работы взломщика увеличится минут на 20, не более.
          Поэтому было бы интересно подобные вещи опустить на уровень ndk, который полезет не к java-прослойке, а сразу к api OS. Специалистов, которые поломают .so на много порядков меньше, чем школьников, которые могут поменять пару байт в интерпретируемом языке.
            0
            Пишите под ios)
              0

              Можно запутать обращение к api:


              • весь трафик пускаем по доверенному каналу;
              • модуль api шифруем и загружаем через кастомный класслоадер;
              • ключ от шифрованного блока можно запрятать с помощью стеганографии

              либо


              • сгенерировать открытый\закрытый ключ, обменяться с сервером по доверенному каналу связи и каждый раз получать новый, зашифрованный открытым ключём блок с модулем api.
                0

                Нет.
                При желании, программу на любом языке можно защитить, например, засунув в виртуалку.
                Единственный вопрос — в скорости (медленности)) работы защиты.

                  0
                  Во-первых таких технологий под андроид нет. Это вам не винда. Все способы все равно сведутся к обфускации. Во-вторых всё равно обращения пойдут через Java-прослойку, что выламывает любой школьник.
                    0

                    1) Пишем код на C++.
                    2) Накатываем Denuvo или что там сейчас модно)
                    3) Пишем на Java симулятор процессора.
                    4) PROFIT — Java-прослойку ломать бессмысленно (сломается примерно все приложение).

                      0
                      API всё равно на Java. Всё равно из этой «коробочки» будут вызовы понятных вещей. Максимум можно обфускацировать этот «симулятор процессора», но всё равно всё сведётся к замене пары байт обращения к api, только байты будут не стандартные. Ну потратит человек 2 вечера, а не пол часа как в других случаях. И опять написать защиту будет дольше, чем её сломать.
              0

              Почему нет, вполне себе существует очень простой вариант проверить текущие сигнатуры (подписи):


              Signature[] signatures = getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
                  for (Signature signature : signatures) {
                      // checkig
                  }

              Для некоторых проектов этого даже достаточно. Само собой для большей безопасности необходим комплексный подход, и чем он оригинальнее, тем больше человеко-секунд такой подход продержится перед китайским кружком очумелых ручек.

              +2
              У jenkins есть система управления credentials. С андроидными подписями я не сталкивался, а всё остальное, что мы делаем, идёт либо через inject environment variables, либо через ssh-ключи на слейвы, которые могут что-то делать (например, подписывать пакеты).

              Вообще, если говорить про сертификаты, то логично, чтобы jenkins всего лишь делал request, а сертификат ему делал сторонний CA.
                0
                Мне нравится идея выдавать Jenkins временный токен для отдельного сервера подписывания.

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое