Термины и определения, описание сущностей
Тип | Описание |
---|---|
СДК | myDSS SDK версии 2.1 для встраивания в Android-приложения, поставляемая в виде набора AAR-библиотек. |
DSSUser (Пользователь) | Объект, содержащий всю необходимую информацию для выполнения действий от имени пользователя. Содержит, в том числе, информацию для доступа к экземпляру DSS (URL для взаимодействия, флаги, определяющие параметры взаимодействия), "вектор аутентификации" для подтверждения действий и пр. С точки зрения DSS данный объект является устройством, подключенным к учетной записи пользователя DSS. |
DSSDevice (Устройство) | Объект, содержащий информацию об устройствах, подключенных к той же учетной записи, что и объект DSSUser. Данный объект не содержит "вектор аутентификации" и не может использоваться для подтверждения операций или выполнения других действий. |
DSSOperation (Операция) | Операция DSS, для которой требуется подтверждение/отклонение. Операция может содержать несколько документов или может не содержать документов вообще. Операция создается на сервере DSS. СДК позволяет подписывать или отклонять как и все документы, входящие в операцию, целиком, так и часть документов. |
DSSOperation.DSSDocument (Документ) | Документ, входящий в операцию, либо обрабатываемый самостоятельно. |
DSSCertificate (Сертификат) | Сертификат, привязанный к ключу подписи в учетной записи на DSS. |
Включение myDSS SDK в проект
Рекомендуемый способ подключения СДК к проекту Android-приложения - это добавление maven-зависимости.
Добавление maven-репозиториев
В корневой файл build.gradle
проекта необходимо добавить четыре maven-репозитория:
№ | Адрес | Пояснение |
---|---|---|
1 | https://repo.paycontrol.org/mydss/android/maven |
СДК |
2 | https://repo.paycontrol.org/android/maven |
Для подключения необходимых для работы в токенами Rutoken |
3 | mavenCentral() |
Для добавления библиотеки-сканера QR-кода (обычно уже прописан) |
4 | https://www.jitpack.io |
Для добавления вспомогательных библиотек, используемых СДК |
Примерный корневой build.gradle
будет выглядеть следующим образом:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:8.2.1"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://repo.paycontrol.org/mydss/android/maven" } // Для версии с поддержкой токенов Rutoken
maven { url "https://repo.paycontrol.org/android/maven" }
maven { url "https://www.jitpack.io" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Добавление maven-зависимости
Далее необходимо добавить maven-зависимость в файлы build.gradle
модулей приложения, использующие функции СДК. До версии СДК 2.1.647 и для базовых версии после:
implementation 'ru.cryptopro.sdk:mydss:2.1.<Код последней версии>'
После версии СДК 2.1.647 для поддержки токенов Rutoken необходимо использовать версию СДК с суффиксом -nfc:
implementation 'ru.cryptopro.sdk:mydss:2.1.<Код последней версии>-nfc'
Настройка правил минификации и обфускации
Если в проекте используются инструменты минификации и обфускации proguard
или D8
(R8
), то для корректной работы библиотеки в файл правил proguard
(например, proguard-rules.pro
) необходимо добавить следующие ограничения:
-keep public class ru.cryptopro.mydss.** { *; }
-dontwarn ru.cryptopro.mydss.**
-keep public class ru.CryptoPro.** { *; }
-dontwarn ru.CryptoPro.**
-keep public class ru.cprocsp.** { *; }
-dontwarn ru.cprocsp.**
-keep public class org.ini4j.spi.** { *; }
-dontwarn org.ini4j.spi.**
-keep public class java.awt.event.** { *; }
-dontwarn java.awt.event.**
-keep public class javax.swing.** { *; }
-dontwarn javax.swing.**
-keep public class com.objsys.asn1j.runtime.** { *; }
-dontwarn com.objsys.asn1j.runtime.**
# Если есть поддержка токенов Rutoken:
-keep class ru.rutoken.**
-keepclassmembers class ru.rutoken.** {*;}
Требуемые разрешения
СДК объявляет в манифесте следующие разрешения, необходимые для корректной работы:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.NFC" />
<uses-feature
android:name="android.hardware.nfc"
android:required="false"
tools:replace="required" />
Разрешения INTERNET
и ACCESS_NETWORK_STATE
необходимы для взаимодействия с сервером DSS и проверки доступности сети Интернет на устройстве, разрешение CAMERA
используется при сканировании QR-кодов (предварительно запрашивается у пользователя внутри СДК при необходимости), разрешение USE_BIOMETRIC
применяется при защите ключевой информации при помощи отпечатка пальца. Разрешение 'NFC' необходимо только для работы с токенами Rutoken.
Настройки безопасности
При создании TLS-соединения с сервером DSS осуществляется проверка сертификата сервера по спискам отзывов сертификатов (CRL). Доступ к этим спискам осуществляется по незащищённому протоколу http
. Для аппаратов с версией ОС Android 9 и новее, использование http-соединений (clear text traffic) по умолчанию запрещено. Однако, для корректной обработки списков отзывов, http-соединение должно быть разрешено. Наиболее простым (и наименее безопасным) решением в этом случае является разрешение http-трафика на уровне всего приложения через настройку в файле AndroidManifest.xml
:
...
<application
android:usesCleartextTraffic="true"
...>
Однако, более правильным решением будет включение в манифест опции android:networkSecurityConfig
:
...
<application
android:networkSecurityConfig="@xml/network_security_config"
...>
Файл ресурсов res/xml/network_security_config.xml
нужно настроить так, чтобы разрешить http-трафик для доменов, по которым ожидается размещение списков CRL. Более подробные сведения можно найти в Документации разработчика.
Прочие настройки
Минимальная поддерживаемая СДК версия ОС - это Android 7. Поэтому модули, использующие СДК, должны включать в build.gradle
строчку:
minSdk = 24
Начиная с версии СДК 2.1.713 необходимо добавить в build.gradle
на уровне модулей приложения, использующие функции СДК, в раздел packaging
для jniLibs
строку:
gradle
...
useLegacyPackaging = true
...
Для более старых(<2.1.713) версий СДК в манифесте модуля (файл AndroidManifest.xml
) для элемента application
должен быть задан атрибут android:extractNativeLibs
со значением true
:
...
<application
android:extractNativeLibs="true"
...>
Для более старых(<2.1.713) версий СДК при распространении приложения в виде Android App Bundle в файл gradle.properties
проекта должна быть включена строка:
android.bundle.enableUncompressedNativeLibs = false
Обновление с версии myDSS SDK 2.0
СДК версии 2.1 поддерживает обратную совместимость с СДК версии 2.0, однако при переходе на новую версию в исходный код приложения потребуется внести незначительные изменения, по большей части механические.
Изменения, затрагивающие процесс компиляции
При простой замене maven-зависимости ru.cryptopro.sdk:mydss:2.0.x
в файле build.gradle
модуля на ru.cryptopro.sdk:mydss:2.1.x
, модуль не скомпилируется. Для устранения ошибок компиляции нужно предпринять следующие шаги:
Проставить опцию
minSdkVersion 24
, если стоит версия ниже. СДК версии 2.0 была совместима с аппаратами под управлением Android 4.1+ (API 16), в то время как СДК версии 2.1 рассчитана на использование на аппаратах с Android 7+ (API 24).Добавить в
build.gradle
проекта репозиторийgradle maven { url "https://www.jitpack.io" }
если он не был добавлен ранее. СДК использует зависимости из этого репозитория.В реализации интерфейса
DSSSignResultNetworkCallback
в дополнение к методуerror(DSSNetworkError)
нужно переопределитьerror(DSSError)
, который срабатывает при возникновении ошибки типаDSSError
(ошибка, не связанная с сетевыми запросами).Метод класса
Appearance
, выполняющий настройку вторичной кнопки модальных окон переименован изAppearance.getButtons().getModalSecond()
в Appearance.getButtons().getModalSecondary()
. В приложении необходимо ссылаться на новое имя.Методы классов
DSSUsersManager
иDSSUsersManagerNonQual
, возвращавшие код ошибки типаDSSError
в виде целого числа, теперь возвращают сам объектDSSError
. Эти изменения затрагивают методrename
классаDSSUsersManager
и методыsubmitPassword
,changePassword
классаDSSUsersManagerNonQual
. Если приложение вызывает эти методы, то код обработки возвращаемого значения нужно поправить соответствующим образом.Все входные и выходные параметры методов СДК были снабжены аннотациями
androidx.annotation.Nullable
иandroidx.annotation.NonNull
. Если приложение написано на языке Kotlin, то по мере необходимости нужно изменить типы объектов на допускающие и недопускающиеnull
в соответствии с аннотациями из СДК. Самый простой способ определить, в каких местах потребуются изменения - это попробовать запустить компиляцию и пройтись по всем местам, где возникнут ошибки. Для приложений на Java необходимо проверить предупреждения Lint и внести правки по мере надобности, чтобы не допустить возникновение NPE во время выполнения.
Изменения, влияющие на поведение во время выполнения
Все свойства-размеры объектов класса Appearance
теперь задаются не в единицах px
как было ранее, а в виде ссылок на ресурсы типа dimen
. Это относится к свойствам Appearance.ButtonAppearance.cornerRadius
, Appearance.ViewAppearance.cornerRadius
и Appearance.LabelAppearance.size
. Если в приложении вы переопределяли значения этих свойств для каких-либо элементов пользовательского интерфейса СДК, то необходимо заменить абсолютные значения идентификаторами R.dimen.*
. В противном случае оформление "слетит": СДК не найдёт нужный ресурс и у элемента сохранится оформление по умолчанию.
Прочие изменения
Следующие изменения СДК не должны влиять на ход компиляции и выполнения приложения, если предыдущая версия СДК была встроена в соответствии с рекомендациями по встраиванию myDSS SDK 2.0. Однако, для улучшения качества взаимодействия СДК и приложения, вы можете захотеть адаптировать ваш исходный код под эти изменения.
Все коллбэки, переданные в СДК, вызываются в главном потоке приложения, в не зависимости от того, в каком потоке был вызван метод СДК, принимающий коллбэк. В версии СДК 2.0, в зависимости от ситуации, коллбэк мог быть вызван либо в UI-потоке, либо в том же потоке, где был вызван метод, принимающий коллбэк. С целью достижения большей детерминированности поведения в версии 2.1 схема вызова коллбэков была переделана так, чтобы код коллбэка всегда исполнялся в UI-потоке.
Классы ошибок
DSSError
иDSSNetworkError
обновлены так, чтобы предоставлять больше полезной информации о возникшей ошибке. Добавлены новые типы ошибок, для некоторых типов методgetMessage()
может возвращать разные сообщения, в зависимости от ситуации. В частности, это касается ошибокDSSError.DSS_ERROR_CRYPTOGRAPHY_UTILS_UNINITIALIZED
иDSSError.DSS_ERROR_NATIVE_FUNCTION_FAILED
.Методы СДК, открывающие пользовательский интерфейс, теперь сами могут запрашивать пароль при необходимости. Выполнять проверку вида
if (!user.isReadyToSign()) { DSSUsersManager.submitPassword(user, callback); }
перед выполнением этих методов больше не нужно. К таким методам относятся:
- DSSUsersManager.acceptAccountChanges(DSSUser, DSSUserCallback)
- DSSUsersManager.changePassword(DSSUser, boolean, DSSUserCallback)
- DSSOperationsManager.confirmOperation(DSSUser, DSSOperation, SignMode, boolean, boolean, DSSApproveRequestNetworkCallback)
- DSSOperationsManager.signDocuments(DSSUser, ArrayList
, SignParams, DSSSignResultNetworkCallback) - DSSOperationsManager.signDocumentsOffline(DSSUser, ArrayList
, SignParams, DSSApproveRequestNetworkCallback) - DSSDevicesManager.processAwaitingDevice(DSSUser, DSSDeviceApprovalCallback)
- DSSCertificatesManagerNonQual.signCertificateRequest(DSSUser, DSSCertificate, DSSSignCertificateRequestCallback)
- DSSUsersManager.acceptAccountChanges(DSSUser, DSSUserCallback)
В коллбэк инициализации СДК
DSSInitCallack
добавлен метод c реализацией по умолчанию:default void onAppearanceReady(@NonNull Appearance appearance) { }
Он вызывается ещё до момента окончания инициализации СДК, но перед показом диалоговых окон с предупреждениями о проблемах безопасности (если таковые имеются). Таким образом, вид этих диалоговых окон можно также настроить в соответствии со стилем приложения.
Структура СДК
СДК может работать в режиме УКЭП
и УНЭП
. В режиме УКЭП
СДК предоставляет графический интерфейс для регистрации устройства, смены способа защиты ключей, подтверждения операций и подписания документов. В режиме УНЭП
приложение может вызывать методы СДК без задействования графического интерфейса, а использовать свой интерфейс для всех процессов.
Для использования СДК в режиме УКЭП
приложение может обращаться к методам следующих классов:
Класс | Предназначение |
---|---|
DSSUsersManager | Создание учётных записей, привязка устройства к существующим учётным записям, управление учётными записями на устройстве (проверка статуса, смена пароля, обновление, удаление с устройства и т.д.) |
DSSOperationsManager | Получение данных операций, подтверждение операций, подписание документов |
DSSCertificatesManager | Управление сертификатами пользователя (создание запросов на сертификат, получение сведений о сертификатах и запросах, приостановка и удаление сертификата и .д.) |
DSSDevicesManager | Управление устройствами, привязанными к учётной записи (получение списка устройств, подтверждение запросов на добавление нового устройства, удаление устройств и т.д.) |
DSSPolicyManager | Получение информации о сервере DSS |
MyDss | Инициализация и деинициализация СДК, регулировка параметров работы (метод initNonQual предназначен для инициализации СДК в режиме УНЭП ) |
Appearance | Управление внешним видом СДК |
Кроме того, СДК предоставляет классы-модели (DSSUser
, DSSDevice
, DSSCertificate
, DSSOperation
, DSSOperation.DSSDocument
, DSSParams
, DSSSignServerParams
, PushNotificationData
) для работы с данными пользователя, устройства, сертификата. операции, документа, параметров сервера, параметров сервера подписи и настроек пуш-уведомлений соответственно.
При использовании СДК в режиме УНЭП
приложение дополнительно может использовать классы с суффиксом NonQual
:
Класс | Предназначение |
---|---|
DSSUsersManagerNonQual | Управление учётными записями без использования графического интерфейса |
DSSOperationsManagerNonQual | Работа с операциями и документами без использования графического интерфейса |
DSSCertificatesManagerNonQual | Работа с сертификатами, хранимыми на устройстве (некоторые методы вовлекают использование графического интерфейса) |
DSSDevicesManagerNonQual | Работа с привязанными устройствами без использования графического интерфейса |
DSSKeysManagerNonQual | Управление ключами, хранимыми на устройстве (некоторые методы вовлекают использование графического интерфейса) |
Инициализация СДК
Инициализация СДК задаёт параметры её дальнейшего использования: уровень логирования, набор корневых сертификатов, до которых будет строиться цепочка проверки сертификатов серверов, с которыми работает приложение, а также режим работы: для разработки (development
) или для конечного пользователя (production
). Режим development
позволяет отключить проверку отзыва сертификата сервера, что может быть полезным на этапе разработки, если адреса CRL не настроены должным образом.
Инициализация СДК должна выполняться один раз в главном потоке приложения. При необходимости реинициализации СДК должна проводиться предварительная деинициализация.
Инициализация в режиме УКЭП
Инициализация СДК для работы в режиме УКЭП должна выполняться методом MyDss.init(...)
как показано в примере ниже.
public class MainActivity extends AppCompatActivity {
static MyDss myDSS = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyDss.init( getApplicationContext(),
MyDss.RootCertificateType.Development, // Тип корневого сертификата
MyDss.DSS_LOG_DEBUG, // Уровень логирования СДК в LogCat
null, // Список "доверенных" приложений, убран из аргументов начиная с версии 2.1.616
new DSSInitCallback()
{
@Override
public void error(@NonNull DSSError dssError) {
// Инициализация прошла неудачно. Библиотекой пользоваться нельзя
Log.e("ERROR", dssError.getMessage());
}
@Override
public void success(@NonNull MyDss myDssInstance) {
// Библиотека инициализирована успешно
// Необходимо сохранить созданный экземпляр myDssInstance для дальнейшего использования
myDSS = myDssInstance;
}
@Override
public void onAppearanceReady(@NonNull Appearance appearance) {
// Инициализация ещё не завершена, но уже доступно управление внешним видом СДК
// через экземпляр класса Appearance
}
});
}
}
Первый параметр метода init
- контекст приложения.
Следующий параметр - перечисление MyDss.RootCertificateType
- определяет одновременно набор корневых сертификатов, с которым будет работать приложение, и режим работы СДК - для разработки (без проверки CRL) и для конечного использования (с проверкой CRL). В примере выше использован режим "Для разработки".
Третий параметр - уровень логирования - определяет, какую информацию будет записывать СДК в LogCat. Константа MyDss.DSS_NO_LOGGING
предотвращает запись в логи каких-либо сообщений СДК (но не внутренних библиотек СДК). Значение MyDss.DSS_LOG_INFO
включает логирование сообщений об ошибках, предупреждений и информационных сообщений, в то время как значение MyDss.DSS_LOG_DEBUG
позволяет также включить в логи содержимое тел сетевых запросов и промежуточные состояния различных объектов.
Четвёртый параметр (в примере выше - null
) задаёт список доверенных приложений, устарел и был убран начиная с версии СДК 2.1.616. Во время инициализации SDK выполняет поиск установленных приложений с привилегиями android.permission.PACKAGE_USAGE_STATS
и android.permission.SYSTEM_ALERT_WINDOW
. Такие приложения являются потенциально опасными, так как могут рисовать окна "поверх" окон других процессов, в результате чего пользователь может видеть на экране искажённую информацию. Прежде чем процесс инициализации SDK будет завершён, пользователю будет показано диалоговое окно со списком потенциально опасных программ. При этом в SDK есть предопределённый белый список приложений с вышеуказанными привилегиями, которые не являются опасными (этот список включает системные приложения и некоторые другие). В силу непрерывного развития мобильной индустрии данный список не является исчерпывающим, поэтому вы можете пополнять его другими приложениями, которые на ваш взгляд SDK ошибочно определяет как вредоносные.
Подробнее о формировании списка "доверенных" приложений смотрите в разделе Инициализация SDK со списком доверенных приложений.
В качестве последнего параметра в метод init
нужно передавать реализацию интерфейса DSSInitCallback
для получения результатов инициализации.
Инициализация в режиме УНЭП
При работе с неквалифицированной электронной подписью инициализацию СДК можно проводить вызовом метода MyDss.initNonQual(...)
. Этот метод принимает только четыре параметра: список доверенных приложений передавать не нужно, так как в режиме УНЭП СДК не выполняет проверку наличия потенциально опасных приложений. Кроме того, в режиме УНЭП не проверяется наличие прав суперпользователя (root) на устройстве и не выполняется проверка на наличие доверенного антивируса.
Размещение набора корневых сертификатов
Второй параметр методов MyDss.init(...)
и MyDss.initNonQual(...)
определяет не только режим работы СДК, но и ожидаемое расположение списка корневых сертификатов. При использовании значения MyDss.RootCertificateType.Development
корневые сертификаты должны быть размещены в папке "сырых" ресурсов в файле с именем development_root_cert и любым расширением (например, res/raw/development_root_cert.crt) - СДК будет обращаться к ресурсу списка сертификатов как R.raw.development_root_cert
. При использовании режима для конечного пользователя - MyDss.RootCertificateType.Production
- список корневых сертификатов должен располагаться в аналогичном файле, именованном production_root_cert.
В независимости от режима инициализации сертификаты должны быть сохранены в формате PEM
. В общем случае, в одном файле ресурса можно разместить несколько сертификатов. Тогда файл ресурса со списком сертификатов будет выглядеть следующим образом:
-----BEGIN CERTIFICATE-----
Base64 encoded certificate 1
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
Base64 encoded certificate 2
-----END CERTIFICATE-----
...
-----BEGIN CERTIFICATE-----
Base64 encoded certificate N
-----END CERTIFICATE-----
Регистрация устройства
Отправным шагом при работе с СДК является регистрация устройства (привязка устройства к существующей учётной записи или регистрация первого анонимного устройства с последующим созданием новой учётной записи).
СДК предоставляет три сценария регистрации:
- Регистрация нового устройства онлайн.
- Регистрация нового устройства с использованием QR-кода, содержащего предварительные сведения регистрации.
- Регистрация второго или последующего устройства с подтверждением присоединения этого устройства на одном из ранее зарегистрированных устройств.
Результатом каждого из способов регистрации является создание нового объекта DSSUser
, содержащего уникальный набор ключей аутентификации (векторы аутентификации), используемых данным устройством, при выполнении действий от имени пользователя DSS, к которому было привязано устройство.
Регистрация в режиме УКЭП
Ниже приведены примеры регистрации в режиме УКЭП для каждого из трёх сценариев. Регистрация инициируется вызовом одного из методов createDSSUser*
класса DSSUsersManager
.
Онлайн-регистрация
Онлайн-регистрация устройства выполняется в три логических шага, два из которых инициируются приложением на мобильном устройстве.
Шаг 1. Регистрация анонимного устройства
Первый шаг - регистрация анонимного устройства методом DSSUsersManager
:
DSSUsersManager.createDSSUser(
@Nullable String externalId, // Идентификатор сущности вызывающего приложения,
// к которому будет "привязан" объект
@Nullable String alias, // Удобочитаемый идентификатор устройства
@NonNull String name, // Уникальное имя профиля в рамках приложения
@NonNull String serviceUrl, // URL для взаимодействия с myDSS
@NonNull String deviceName, // Читаемое название устройства
@Nullable PushNotificationData pushData, // Данные для получения PUSH-уведомлений
boolean requirePassword, // Требуется ли установка пароля
@Nullable DSSUserCallback callback // Callback для обработки результатов
)
Как правило, параметры externalId
и alias
не указываются (передаётся значение null
) и назначаются сервером DSS. Однако, в зависимости от настроек сервера, эти параметры могут быть заданы из приложения.
Параметр name
задаёт уникальное имя будущего объекта DSSUser
в рамках приложения и не передаётся на сервер. СДК использует это имя в качестве уникального признака при выполнении некоторых операций. Предполагается, что имя задаётся вручную и применяется пользователем приложения для различения ключей. Однако, СДК не накладывает на это ограничений, и приложение может генерировать этот параметр иным образом и использовать в иных целях.
Параметр serviceUrl
должен быть известен приложению и определяет адрес сервиса mDAG, на который СДК будет отправлять запросы.
Значение параметра deviceName
именует данное устройство и должно носить уникальный характер, чтобы при работе со списком привязанных устройств все устройства были различимы.
Необходимые данные для получения PUSH-уведомлений передаются в параметре pushData
, представляющем экземпляр класса PushNotificationData
, например. Создать такой экземпляр можно через конструктор, передав в него token, полученный от сервиса FireBase:
PushNotificationData pushData = new PushNotificationData("my firebase push token");
Если же приложение запущено на устройстве, где получение уведомлений планируется осуществлять посредствам сервиса HMS
, то объект типа PushNotificationData
следует получить следующим вызовом:
PushNotificationData pushData = new PushNotificationData("my HMS push token", 3);
Второй аргумент задаёт тип устройства (1 - устройство на базе iOS, 2 - устройство на базе Android с использованием FCM, 3 - устройство на базе Android с использованием HMS (обычно, аппараты Huawei)).
Логический параметр requirePassword
позволяет пропустить ручное задание пароля (если установлено значение false
) при условии, что параметры сервера и флаги ключа это допускают. В этом случае, ключи аутентификации будут защищены неким паролем по умолчанию.
Последний параметр - реализация интерфейса DSSUserCallback
для обработки результатов онлайн-регистрации:
new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser user) {
// Пользователь создан успешно, данные сохранены в объекте user
}
@Override
public void error(@NonNull DSSError error) {
// Возникла внутренняя ошибка СДК - регистрация не пройдена
}
@Override
public void error(DSSNetworkError error) {
// Возникла ошибка сетевого характера - регистрация не пройдена
}
}
Созданный объект DSSUser
уже сохранён в долгосрочную память приложения и может быть восстановлен из памяти при последующих запусках приложения.
Шаг 2. Подтверждение регистрации оператором
После успешного выполнения метода createDSSUser(...)
созданный объект DSSUser
, а также объект DSSDevice
, соответствующий данному устройству, находятся в статусе Installed
(значение перечисления DSSDevice.DSSDeviceStatus
). Это означает, что ключи аутентификации, полученные от сервера, были сохранены на устройстве. Далее, оператор переводит устройство в статус NotVerified
путём привязки устройства к учётной записи по значению alias
.
Шаг 3. Подтверждение привязки устройства к учётной записи
Заключительный шаг онлайн-регистрации - подтверждение привязки устройства к учётной записи на стороне приложения. Данная операция может быть выполнена при достижении статуса NotVerified
, поэтому предварительным действием будет получение текущего статуса устройства методом updateStatus(...)
:
DSSUsersManager.updateStatus(user, new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser user) {
// Статус успешно обновился, теперь можно проверить текущий статус
if (user.getStatus() == DSSDevice.DSSDeviceStatus.NotVerified) {
// Статус сменился, можно подтверждать привязку устройства
}
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка при запросе статуса
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Ошибка при запросе статуса
}
});
Как только статус объекта DSSUser
станет DSSDevice.DSSDeviceStatus.NotVerified
, можно запустить процесс подтверждения присоединения устройства к учётной записи методом acceptAccountChanges(...)
:
DSSUsersManager.acceptAccountChanges(user, new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser user) {
// Подтверждение успешно.
// Статус пользователя - DSSDevice.DSSDeviceStatus.Active
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка подтверждения
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Ошибка подтверждения
}
});
При вызове этого метода будет показан экран с данными учётной записи (профиля пользователя DSS), к которой привязывается устройство. При успешном выполнении метода объект DSSUser
переходит в статус DSSDevice.DSSDeviceStatus.Active
- это означает, что устройство подтверждено и может использоваться для работы с сертификатами, операциями, документами и другими устройствами.
Регистрация с помощью QR-кода
Регистрация с помощью QR-кода, содержащего предварительные данные для регистрации, выполняется в два шага.
Шаг 1. Сканирование QR-кода и запуск регистрации
Для сканирования QR-кода и начала регистрации необходимо вызвать метод
DSSUsersManager.createDSSUserWithInitQR(
@Nullable String externalId, // Идентификатор сущности вызывающего приложения,
// к которому будет "привязан" объект
@Nullable String alias, // Удобочитаемый идентификатор устройства
@NonNull String name, // Уникальное имя профиля в рамках приложения
@NonNull String deviceName, // Читаемое название устройства
@Nullable PushNotificationData pushData, // Данные для получения PUSH-уведомлений
boolean requirePassword, // Требуется ли установка пароля
@Nullable DSSUserCallback callback // Callback для обработки результатов
)
В отличие от метода createDSSUser
параметр serviceUrl
не передаётся - адрес сервера извлекается из QR-кода. При успешном выполнении метода результат аналогичен - в коллбэк возвращается созданный объект DSSUser
, сохранённый в долгосрочную память.
Шаг 2. Подтверждение привязки устройства к учётной записи
После успешного выполнения метода createDSSUserWithInitQR
объект DSSUser
будет сразу иметь статус NotVerified
(но нужно выполнить синхронизацию с сервером методом updateStatus(...)
), поэтому далее можно переходить к вызову acceptAccountChanges(...)
. Таким образом, шаг 2 полностью аналогичен шагу 3 сценария онлайн-регистрации.
Присоединение нового устройства
Данный способ применяется, когда для учётной записи уже есть привязанное подтверждённое устройство (объект DSSUser
в статусе Active
) и требуется привязать к этой же учётной записи новое устройство. Чтобы выполнить эту операцию, потребуется передать идентификатор данного пользователя на новое устройство.
Получить идентификатор можно у экземпляра пользователя:
// Получить идентификатор подтверждённого пользователя, к которому привязываем новое устройство
String uid = user.getDSSUserId();
Передать идентификатор с одного устройства на другое можно любым удобным способом. Один из вариантов - сформировать на подтверждённом устройстве QR-код и отсканировать его на добавляемом устройстве.
Процесс добавления нового устройства выполняется по следующему алгоритму:
- На добавляемом устройстве вызывается метод
createDSSUserWithApproval(...)
классаDSSUsersManager
, который создаст нового пользователя со статусомDSSDevice.DSSDeviceStatus.ApproveRequired
. - На добавляемом устройстве отображается QR-код, содержащий необходимую информацию о данном устройстве. Этот экран показывается при выполнении метода
createDSSUserWithApproval(...)
. Если его закрыть до завершения процесса присоединения нового устройства, то повторное открытие экрана нужно инициировать методомDSSUsersManager.checkApprovalStatus(...)
. - На основном устройстве вызывается метод
processAwaitingDevice(...)
классаDSSDevicesManager
для выполнения процедуры подтверждения или отклонения добавляемого устройства. При вызове этого метода пользователю будет предложено отсканировать QR-код с экрана добавляемого устройства. - После сканирования QR-кода и просмотра информации о добавляемом устройстве пользователь выполняет подтверждение или отклонение добавляемого устройства.
- На добавляемом устройстве на экране c QR-кодом отображается кнопка проверки статуса добавляемого устройства.
Пользователь может покинуть экран, дождавшись подтверждения с основного устройства, или сделать это сразу.
В зависимости от этого, в приложение будет возвращён объект
DSSUser
со статусомDSSDevice.DSSDeviceStatus.ApproveRequired
илиDSSDevice.DSSDeviceStatus.Active
. Кроме того, на добавляем устройстве будет производиться автоматическая проверка статуса, если в методcreateDSSUserWithApproval(...)
(илиDSSUsersManager.checkApprovalStatus(...)
при повторном открытии экрана) был передан ненулевой параметрcheckingInterval
.
Таким образом, сначала на новом устройстве вызываем метод:
DSSUsersManager.createDSSUserWithApproval(
@Nullable String externalId, // Идентификатор сущности вызывающего приложения,
// к которому будет "привязан" объект
@Nullable String alias, // Удобочитаемый идентификатор устройства
@NonNull String name, // Уникальное имя профиля в рамках приложения
@NonNull String serviceUrl, // URL для взаимодействия с myDSS
@NonNull String deviceName, // Читаемое название устройства
@Nullable PushNotificationData pushData, // Данные для получения PUSH-уведомлений
@NonNull String uid, // Идентификатор пользователя, к которому привязываем устройство
checkingInterval, // Интервал проверки статуса в секундах
boolean requirePassword, // Требуется ли установка пароля
@Nullable DSSUserCallback callback // Callback для обработки результатов
)
При вызове метода создаётся новый объект DSSUser
со статусом DSSDevice.DSSDeviceStatus.ApproveRequired
. Теперь требуется получить подтверждение с основного устройства. На основном устройстве вызываем метод processAwaitingDevice(...)
класса DSSDevicesManager
:
DSSDevicesManager.processAwaitingDevice(currentUser, new DSSDeviceApprovalCallback() {
@Override
public void success(@NonNull DSSDevicesManager.Action action) {
// Устройство успешно подтверждено или отклонено
}
@Override
public void error(@NonNull DSSError error) {
// При обработке запроса возникла ошибка
}
@Override
public void error(@NonNull DSSNetworkError error) {
// При обработке запроса возникла ошибка
}
});
При вызове метода processAwaitingDevice(...)
будет открыт экран для сканирования QR-кода с добавляемого устройства, а после сканирования будет открыт экран с информацией о добавляемом устройстве (полученной из QR-кода) и кнопки для добавления устройства и отказа в добавлении. В зависимости от того, какую кнопку нажмёт пользователь, в метод success(...)
коллбэка будет возвращено либо значение DSSDevicesManager.Action.Approve
либо DSSDevicesManager.Action.Reject
.
Регистрация в режиме УНЭП
Режим УНЭП допускает использование методов регистрации из класса DSSUsersManagerNonQual
. Эти методы не открывают пользовательский интерфейс СДК.
Онлайн-регистрация
Онлайн-регистрация нового устройства осуществляется в четыре логических шага.
Шаг 1. Регистрация анонимного устройства
Для запуска регистрации анонимного устройства необходимо вызвать метод createDSSUser(...)
класса DSSUsersManagerNonQual
:
DSSUsersManagerNonQual.createDSSUser(
@Nullable String externalId,
@Nullable String alias,
@NonNull String serviceUrl,
@NonNull String deviceName,
@Nullable PushNotificationData pushData,
@Nullable DSSUserCallback callback
)
Все параметры аналогичны параметрам метода createDSSUser(...)
класса DSSUsersManager
, но параметры name
и requirePassword
не используются - они потребуются на следующем шаге.
После успешного выполнения метода возвращается объект DSSUser
со статусом DSSDevice.DSSDeviceStatus.Created
- это означает, ключи аутентификации ещё не были сохранены в долгосрочную память устройства - теперь их нужно сохранить.
Шаг 2. Сохранение в долгосрочную память
Теперь необходимо сохранить объект DSSuser
в долгосрочную память. Для сохранения необходимо задать уникальное имя объекта и пароль, при помощи которого будут зашифрованы ключи. Сохранение осуществляется вызовом метода store(...)
:
DSSUsersManagerNonQual.store(
@NonNull DSSUser user,
@NonNull String name,
@NonNull String password,
@Nullable DSSUserCallback callback
)
После успешного выполнения метода объект DSSUser
имеет статус DSSDevice.DSSDeviceStatus.Installed
.
Передаваемый пароль должен соответствовать парольной политике (требованиям, предъявляемым к сложности пароля), возвращаемой методом
DSSUser.getPasswordPolicy()
.
Шаг 3. Подтверждение регистрации оператором
Аналогично онлайн-регистрации в режиме УКЭП, на этом этапе на стороне сервера статус устройства должен быть переведён из Installed
в NotVerified
.
Шаг 4. Подтверждение привязки устройства к учётной записи
После того, как вызов метода DSSUsersManager.updateStatus(...)
обновил статус объекта на NotVerified
, необходимо выполнить подтверждение привязки устройства вызовом метода DSSUsersManagerNonQual.acceptAccountChanges(...)
:
DSSUsersManagerNonQual.acceptAccountChanges(
@NonNull DSSUser user,
@Nullable DSSQRCodeVerification qrCodeVerification,
@Nullable DSSUserCallback callback
)
Параметр qrCodeVerification
должен принимать значение null
, когда процедура подтверждения привязки устройства к профилю не требует сканирования QR-кода, т.е. выполняется условие:
user.qRVerificationRequired() == false
для заданного объекта DSSUser
.
В противном случае необходимо отсканировать QR-код, содержащий данные для подтверждения привязки устройства к учётной записи и передать в метод acceptAccountChanges
валидный объект типа DSSQRCodeVerification
. Чтобы получить экземпляр такого объекта, нужно вызвать метод MyDss.analyzeQr(String)
:
DSSQRCode qrCode = MyDss.analyzeQR(qrContent);
Далее, необходимо проверить, что QR-код был отсканирован корректно, и он действительно имеет заданный тип, т.е. должны выполняться условия:
qrCode != null && qrCode.isCorrect() && qrCode instanceof DSSQRCodeVerification
Успешное выполнение метода acceptAccountChanges
завершает процесс онлайн-регистрации, статус объекта DSSuser
меняется на Active
.
Регистрация с помощью QR-кода
Шаг 1. Сканирование QR-кода
При регистрации с помощью QR-кода приложение сначала сканирует QR-кода типа DSSQRCodeKinit
:
DSSQRCode qrCode = MyDss.analyzeQR(qrContent);
if (qrCode != null && qrCode.isCorrect() && qrCode instanceof DSSQRCodeKinit) {
// Сканирован корректный QR-код - можно начинать регистрацию
}
Шаг 2. Регистрация устройства
Запуск процесса регистрации выполняется методом createDSSUserWithInitQR(...)
:
DSSUsersManagerNonQual.createDSSUserWithInitQR(
@Nullable String externalId,
@Nullable String alias,
@NonNull DSSQRCodeKinit qrCodeKinit,
@NonNull String serviceUrl,
@NonNull String deviceName,
@Nullable PushNotificationData pushData,
@Nullable DSSUserCallback callback
)
Все параметры аналогичны параметрам метода DSSUsersManagerNonQual.createDSSUser(...)
, но ещё добавляется параметр с данными отсканированного ранее QR-кода.
Шаг 3. Сохранение в долгосрочную память
Как и для случая онлайн-регистрации, сохранить созданный объект DSSUser
необходимо методом DSSUsersManagerNonQual.store(...)
.
Шаг 4. Подтверждение привязки устройства к учётной записи
После выполнения процедуры сохранения объекта DSSUser
статус устройства меняется с Created
на Installed
. После синхронизации статуса методом DSSUsersManager.updateStatus(...)
и получения статуса NotVerified
нужно подтвердить привязку устройства к учётной записи вызовом DSSUsersManagerNonQual.acceptAccountChanges(...)
, как это делается в сценарии онлайн-регистрации.
Присоединение нового устройства
Для режима УНЭП присоединение нового устройства требует большего числа вызовов, чем аналогичный сценарий для УКЭП.
Шаг 1. Запуск регистрации на добавляемом устройстве
Сначала на добавляемом устройстве вызывается метод createDSSUserWithApproval
класса DSSUsersManagerNonQual
:
DSSUsersManagerNonQual.createDSSUserWithApproval(
@Nullable String externalId,
@Nullable String alias,
@NonNull String serviceUrl,
@NonNull String deviceName,
@Nullable PushNotificationData pushData,
@NonNull String uid,
@Nullable DSSUserCallback callback
)
Как и для режима УКЭП, параметр uid
должен быть предварительно передан на присоединяемое устройство. При успешном выполнении в метод коллбэка DSSUserCallback.success(DSSUser)
будет возвращён объект DSSUser
в статусе Created
- его нужно сохранить в память.
Шаг 2. Сохранение в долгосрочную память
Как и при других способах регистрации, сохранение происходит методом DSSUsersManagerNonQual.store(...)
.
Шаг 3. Поиск устройства, ожидающего подтверждения присоединения
Этот шаг выполняется на устройстве, с которого планируется выполнить подтверждение регистрации нового устройства. Для начала, необходимо найти неподтверждённое устройство в списке устройств:
DSSDevicesManager.listDevices(user, new DSSDevicesNetworkCallback() {
@Override
public void success(@NonNull DSSDevice[] devices) {
for (DSSDevice device: devices) {
// Ищем неподтверждённое устройство
if (device.getStatus() == DSSDevice.DSSDeviceStatus.ApproveRequired) {
// Неподтверждённое устройство найдено
}
}
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Ошибка при запросе списка устройств
}
});
Шаг 4. Подтверждение присоединения устройства
После того как устройство, ожидающее подтверждения, было найдено, его необходимо подтвердить методом DSSDevicesManagerNonQual.approve(...)
:
DSSDevicesManagerNonQual.approve(user, device, new DSSNetworkCallback() {
@Override
public void success() {
// Присоединение устройства успешно подтверждено
}
@Override
public void error(@NonNull DSSNetworkError error) {
// При подтверждении возникла ошибка
}
});
Для отклонения запроса на регистрацию необходимо вызывать метод DSSDevicesManagerNonQual.reject(...)
с аналогичной сигнатурой.
Шаг 5. Проверка статуса на присоединяемом устройстве
Чтобы проверить, было ли устройство подтверждено, необходимо воспользоваться методом DSSUsersManagerNonQual.checkApprovalStatus(...)
:
DSSUsersManagerNonQual.checkApprovalStatus(user, new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser approvedUser) {
// Присоединение данного устройства подтверждено на другом устройстве
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка проверки статуса
}
@Override
public void error(@NonNull DSSNetworkError error) {
if (error.getType() == DSSNetworkError.DSS_ERROR_AWAITING_USER_CONFIRMATION) {
// Запрос на присоединение ещё не обработан
} else if (error.getType() == DSSNetworkError.DSS_ERROR_ACTION_REJECTED_BY_USER) {
// Запрос на добавление этого устройства был отклонён на другом устройстве
} else {
// Ошибка проверки статуса
}
}
});
При успешном выполнении метода, объект approvedUser
(является ссылкой на тот же объект, что и переданный параметр user
) будет иметь статус Active
, как и при завершении регистрации другими способами.
Работа с пользователями и устройствами
Работа с пользователями (помимо описанных выше процедур регистрации) включает:
- Извлечение пользователей из долгосрочной памяти
- Предъявление пароля
- Смену пароля
- Смену имени пользователя
- Синхронизацию статуса пользователя
- Обновление профиля пользователя
- Получение истории действий пользователя
- Удаление пользователя с устройства
Напомним, что под пользователем подразумевается объект DSSUser
, включающий вектор аутентификации, установленный на данном устройстве, информацию о статусе устройства и другую сопутствующая информацию, описывающую параметры данного устройства и учётной записи, к которой устройство привязано.
Работа с привязанными к учётной записи устройствами включает:
- Запрос информации об устройствах
- Отзыв "вектора аутентификации" - делает невозможным дальнейшее взаимодействие с сервером с отзываемого устройства
- Подтверждение и отклонение устройства, ожидающего присоединения (как было описано в примерах выше)
Извлечение пользователей из памяти
Для того, чтобы получить список сохранённых на устройстве пользователей, необходимо вызвать метод DSSUsersManager.listStorage()
:
List<DSSUser> users = DSSUsersManager.listStorage();
for (DSSUser user: users) {
// Выполняем необходимые действия с каждым пользователем
}
Предъявление пароля
Некоторые методы СДК требуют выполнения условия user.isReadyToSign()
, т.е. требуют, чтобы перед их вызовом был предъявлен пароль. В документации по API для всех таких методов есть соответствующая пометка.
При работе в режиме УКЭП предъявление пароля осуществляется вызовом метода DSSUsersManager.submitPassword(...)
. Допустим, приложение хочет вызвать метод удаления сертификата пользователя (требует ввода пароля). Тогда процедура вызова метода будет выглядеть следующим образом:
if (user.isReadyToSign()) {
// Предъявлять пароль не нужно - можно вызывать метод
DSSCertificatesManager.deleteCertificate(...)
} else {
// Требуется ввод пароля
DSSUsersManager.submitPassword(user, new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser user) {
// Предъявлен верный пароль - можно вызывать метод
DSSCertificatesManager.deleteCertificate(...)
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка при предъявлении пароля
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Сетевая ошибка - для случая ввода пароля она не возникает
// и этот метод не вызывается
}
});
}
Следует учитывать, что метод error(DSSError)
коллбэка будет вызван только при возникновении каких-либо критичных ошибок (либо при закрытии экрана СДК). При вводе неправильного пароля будет отображено соответствующее сообщение на экране СДК, коллбэк вызываться не будет.
Следует также иметь в виду, что если регистрация в режиме УКЭП проходила без ручного задания пароля (было передано значение false
в качестве параметра requirePassword
), и объект DSSUser
был сохранён с использованием пароля по умолчанию, то метод user.isReadyToSign()
всегда будет возвращать значение true
.
В режиме УНЭП предъявление пароля осуществляется без открытия пользовательского интерфейса путём передачи значения пароля напрямую в метод submitPassword(...)
класса DSSUsersManagerNonQual
:
if (user.isReadyToSign()) {
// Предъявлять пароль не нужно - можно вызывать метод
DSSCertificatesManager.deleteCertificate(...)
} else {
// Требуется ввод пароля
DSSError result = DSSUsersManagerNonQual.submitPassword(user, password);
if (result.getType() == DSSError.DSS_ERROR_OK) {
// Предъявлен верный пароль - можно вызывать метод
DSSCertificatesManager.deleteCertificate(...)
} else {
// При предъявлении пароль возникла ошибка (в т.ч., пароль мог оказаться неверным)
}
}
В независимости от того используется ли метод ввода пароля из класса DSSUsersManager
или DSSUsersManagerNonQual
, после однократного ввода пароля нет необходимости вводить его заново до тех пор, пока приложение находится на переднем плане. При сворачивании приложения более чем на 15 секунд, однако, потребуется снова вводить пароль.
Смена пароля
Смена пароля для режима УКЭП осуществляется вызовом метода changePassword
класса DSSUsersManager
:
DSSUsersManager.changePassword(user, true, new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser user) {
// Пароль успешно изменён
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка смены пароля
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Не вызывается при смене пароля (нет сетевых запросов)
}
});
Этот метод предложит ввести текущий пароль (если он не был введён ранее), а затем откроет экран задания нового пароля.
В режиме УНЭП для смены пароля нужно передать новое значение пароля в метод changePassword
класса DSSUsersManagerNonQual
:
if (user.isReadyToSign()) {
// Можно сразу вызывать метод смены пароля
DSSError result = DSSUsersManagerNonQual.changePassword(user, newPassword);
if (result.getType() != DSSError.DSS_ERROR_OK) {
// При смене пароля возникла ошибка
}
} else {
// Сначала требуется ввести старый пароль
DSSError submissionResult = DSSUsersManagerNonQual.submitPassword(user, oldPassword);
if (submissionResult.getType() == DSSError.DSS_ERROR_OK) {
// Предъявлен верный пароль - можно вызывать метод смены пароля
DSSError result = DSSUsersManagerNonQual.changePassword(user, newPassword);
if (result.getType() != DSSError.DSS_ERROR_OK) {
// При смене пароля возникла ошибка
}
} else {
// При предъявлении пароль возникла ошибка (в т.ч., пароль мог оказаться неверным)
}
}
В режиме УКЭП при задании и смене пароля в пользовательском интерфейсе СДК может быть предложено использовать отпечаток пальца вместо пароля. При самостоятельной реализации такой функциональности в режиме УНЭП следует учитывать, допускают ли флаги ключа использование отпечатка пальца: метод user.isDenyStoreWithOSProtection()
должен возвращать false
.
Смена имени пользователя
Имя пользователя задаётся либо параметром name
методов регистрации класса DSSusersManager
, либо одноимённым параметром метода store
класса DSSUsersManagerNonQual
при работе в режиме УНЭП. В любом случае, для последующей смены этого имени необходимо вызывать метод rename
класса DSSUsersManager
:
DSSError result = DSSUsersManager.rename(user, newName);
if (result.getType() != DSSError.DSS_ERROR_OK) {
// Ошибка при смене имени
}
Синхронизация статуса пользователя
Для получения актуального статуса пользователя используется метод DSSUsersManager.updateStatus(...)
, примеры использования которого описаны выше в сценариях регистрации.
Обновление профиля пользователя
Обновление профиля необходимо для того, чтобы не пришлось заново регистрировать устройство методами DSSUsersManager.createDSSUser(...)
после истечения срока действия ключей, привязанных к устройству. Время окончания срока действия ключей можно получить через вызов DSSUser.getNotAfter()
.
Обновление профиля для режима УКЭП осуществляется вызовом метода renew
класса DSSUsersManager
:
DSSUsersManager.renew(
user,
newName,
deviceName,
new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser renewedUser) {
// Профиль успешно обновлён
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка обновления профиля
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Сетевая ошибка обновления профиля
}
});
Этот метод предложит ввести текущий пароль (вне зависимости от того, был он введён ранее или нет), а затем обновит профиль.
В режиме УНЭП для обновления профиля нужно передать значение пароля в метод renew
класса DSSUsersManagerNonQual
:
DSSUsersManagerNonQual.renew(
user,
newName,
deviceName,
password,
new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser renewedUser) {
// Профиль успешно обновлён
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка обновления профиля
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Сетевая ошибка обновления профиля
}
});
Получение истории действий пользователя
Получить историю действия пользователя на сервере DSS позволяет метод DSSUsersManager.getOperationsHistory(...)
:
DSSUsersManager.getOperationsHistory(
@NonNull DSSUser user,
int count,
int bookmark,
@Nullable int[] operationCodes,
@Nullable DSSOperationsHistoryCallback callback
)
Параметры count
, bookmark
и operationCodes
позволяют сузить множество возвращаемых записей. Более подробно работа с объектами журнала действий пользователя описана в Документации по API.
Удаление пользователя с устройства
Удаление объекта DSSUser
из памяти приложения делает невозможным использование настоящего устройства для взаимодействия с сервером DSS от имени учётной записи, к которой привязано устройство (однако, в дальнейшем устройство можно привязать заново).
Удаление объекта DSSUser
может быть произведено либо с уведомлением сервера (вызовом DSSUsersManager.revoke(DSSuser, DSSUserCallback)
), либо без уведомления сервера (вызовом DSSusersManager.delete(DSSUser)
).
В первом случае устройство будет удалено и из памяти приложения и на стороне сервера, однако выполнение метода revoke()
требует предъявления пароля. Тем временем, вызов delete()
просто стирает данные из памяти приложения, но синхронизация с сервером не производится, пароль в этом случае предъявлять не требуется.
Работа с устройствами
Для работы со всеми текущими привязанными к учётной записи устройствами используются классы DSSDevicesManager
и DSSDevicesManagerNonQual
.
Пример получения списка всех привязанных устройств методом DSSDevicesManager.listDevices()
, а также примеры обработки запроса на присоединение нового устройства в режимах УКЭП и УНЭП были описаны выше в разделах Присоединение нового устройства в режиме УКЭП и Присоединение нового устройства в режиме УНЭП.
Метод DSSDevicesManager.revoke(...)
позволяет отозвать вектор аутентификации с заданного устройства (делает невозможным использование заданного устройства для взаимодействия с сервером DSS). Этот метод имеет смысл применять только при отзыве некоторого устройства отличного от данного (на котором выполняется вызов), для отзыва данного устройства необходимо вызывать метод DSSUsersManager.revoke(DSSuser, DSSUserCallback)
.
Работа с сертификатами
Для работы с сертификатами используется класс DSSCertificatesManager
. Кроме того, есть класс DSSCertificatesManagerNonQual
, который предоставляет функционал для работы с ключами и сертификатами, располагающимися на мобильном устройстве и на рутокенах. Подробнее об этом классе можно прочесть в разделе Работа с клиентской подписью.
СДК работает с тремя типами сущностей:
- Сертификатами, которыми оперирует СДК;
- Запросами на выпуск сертификата;
- Сертификатами, прочитанными из внешних хранилищ (с Рутокена) с помощью вызова метода
DSSCertificatesManagerNonQual.getExternalCertificates(DSSExternalCertificatesCallback)
. После вызоваDSSCertificatesManager.setCertificate(DSSUser, DSSCertificate, DSSCertificateNetworkCallback)
, с таким "внешнем сертификатом", он преобразуется в сертификат, которым оперирует СДК.
Все эти сущности представлены экземплярами класса DSSCertificate
. Для сертификата метод DSSCertificate.getType()
возвращает значение DSSCertificate.Type.Crt
, в то время как для запроса на сертификат тип будет иметь значение DSSCertificate.Type.Req
. Метод DSSCertificate.getState()
возвращает текущее состояние сертификата или запроса на сертификат. Возможные возвращаемые значения этого метода будут разными для сертификатов и запросов. Подробно о возможных состояниях можно прочесть в описании перечисления DSSCertificate.State.
Получение списка сертификатов
Для получения списка сертификатов пользователя и запросов на выпуск сертификата используется метод listCertificates(...)
:
DSSCertificatesManager.listCertificates(user, new DSSCertificatesNetworkCallback() {
@Override
public void success(@NonNull DSSCertificate[] certificates) {
if (certificates.length > 0) {
// Список сертификатов и запросов непустой - можно выполнять какие-либо действия
for (DSSCertificate item: certificates) {
if (item.getType() == DSSCertificate.Type.Crt) {
// Выполняем действия с сертификатом
} else {
// Выполняем действия с запросом на сертификат
}
}
}
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Ошибка запроса списка
}
});
Создание запроса на сертификат
СДК поддерживает сценарий создания запроса на сертификат из мобильного приложения. Для этого необходимо указать идентификатор обработчика УЦ, идентификатор шаблона сертификата и различительное имя субъекта, состоящее из набора ключей и значений компонентов имени.
Для получения этих данных необходимо запросить политики сервиса подписи:
DSSPolicyManager.getDSSSignParams(user, new DSSPolicySignServerNetworkCallback() {
@Override
public void success(@NonNull DSSSignServerParams params) {
// Параметры сервиса подписи получены, извлекаем необходимые данные
// Дальнейший код дан ДЛЯ ПРИМЕРА:
// Берётся первый доступный УЦ и первый доступный шаблон
HashMap<String, String> dn = new HashMap<>();
// Для примера в качестве Distinguished name задаём только
// компонент Common name (OID 2.5.4.3)
dn.put("2.5.4.3", "TestCommonName");
int caId = 0;
String tid = "";
// Ищем УЦ и шаблон
for (DSSSignServerParams.CaPolicies policies :params.getCaPolicies()) {
caId = policies.getId();
Set<String> keys = policies.getEkuTemplates().keySet();
for (String key : keys) {
String[] list = policies.getEkuTemplates().get(key);
if (list != null && list.length > 0)
tid = list[0];
break;
}
break;
}
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Ошибка запроса параметров
}
});
Затем нужно передать собранные параметры методу DSSCertificatesManager.createCertificate(...)
:
DSSCertificatesManager.createCertificate(user, caId, tid, dn, new DSSCertificateNetworkCallback() {
@Override
public void success(DSSCertificate request) {
// Запрос на сертификат создан.
// Объект request содержит данные запроса
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Ошибка создания запроса
}
});
Управление сертификатами и запросами
СДК предоставляет функциональность для управления существующими сертификатами и запросами. Методы для управления сертификатами и запросами расположены в классе DSSCertificatesManager
.
Название метода | Предназначение |
---|---|
deleteCertificate | Удаление сертификата или запроса на сертификат |
setCertificate | Установка сертификата пользователя |
setCertificateFriendlyName | Установка дружественного имени сертификата |
revokeCertificate | Отзыв сертификата |
suspendCertificate | Приостановка действия сертификата |
unSuspendCertificate | Возобновление действия сертификата |
setDefaultCertificate | Установка сертификата по умолчанию |
Подробнее о каждом методе можно прочесть в документации на класс DSSCertificatesManager.
Работа с операциями и документами
Работа с операциями и документами осуществляется с помощью класса DSSOperationsManager
. Класс DSSOperationsManagerNonQual
предоставляет средства работы с операциями и документами в режиме УНЭП без запуска пользовательского интерфейса.
Запрос списка операций
Получить список операций, требующих обработки, можно вызовом метода DSSOperationsManager.getOperationsList(...)
:
DSSOperationsManager.getOperationsList(
@NonNull DSSUser user,
@Nullable DSSOperationsManager.OperationType operationType,
@Nullable String operationId,
@Nullable DSSOperationsNetworkCallback callback
)
Параметры operationType
и operationId
позволяют отфильтровать список либо по типу операции, либо по её идентификатору (тогда будет возвращён список из одной операции). Для возвращения полного списка на обеих позициях нужно передать значение null
.
Подтверждение операции
Подтверждение операции в режиме УКЭП
Для подтверждения или отклонения операции в режиме УКЭП достаточно вызвать один метод DSSOperationsManager.confirmOperation(...)
:
DSSOperationsManager.confirmOperation(
@NonNull DSSUser user,
@NonNull DSSOperation operation,
@Nullable DSSOperationsManager.SignMode signMode,
boolean isSelectionEnabled,
boolean skipSnippet,
@Nullable DSSApproveRequestNetworkCallback callback
)
Данный метод запустит экран отображения операции с кнопками подтверждения и отклонения. При необходимости будет также запрошен пароль.
Параметр signMode
определяет способ обработки: онлайн (с отправкой запроса на сервер) и оффлайн (создание запроса на подтверждение без отправки самого запроса). При указании null
используется онлайн-подтверждение.
Параметр isSelectionEnabled
определяет, разрешено ли отмечать галочкой документы для обработки, либо же все документы обрабатываются (подтверждаются или отклоняются) целиком.
При передаче true
в качестве параметра skipSnippet
при наличии одного единственного документа в операции будет сразу отображён этот документ (вместо списка документов, как это происходит по умолчанию).
Если при выполнении операции возникает ошибка, то вызывается метод
DSSApproveRequestNetworkCallback.error(DSSOperationsManager.ApproveRequest)
Информацию о возникшей ошибке можно получить вызовом методов getOnlineConfirmationResult()
(возвращает объект сетевой ошибки DSSNetworkError
) и getOfflineError()
(возвращает объект внутренней ошибки DSSError
). Так как возникает ошибка либо одного, либо другого типа, один из методов вернёт значение null
, а другой - объект ошибки.
При успешной обработке вызывается
DSSApproveRequestNetworkCallback.success(DSSOperationsManager.ApproveRequest)
Информацию о том, какие документы были подтверждены и отклонены можно получить вызовом методов getConfirmedDocuments()
и getDeclinedDocuments()
полученного объекта ApproveRequest
.
Подтверждение операции в режиме УНЭП
В режиме УНЭП приложение может самостоятельно отобразить данные операции и интерфейс выбора документов для подтверждения и отклонения, а также запросить пароль. После этого необходимо вызвать метод DSSOperationsManagerNonQual.confirmOperation(...)
и передать туда список документов на подтверждение и отклонение:
DSSOperationsManagerNonQual.confirmOperation(
@NonNull DSSUser user,
@NonNull DSSOperation operation,
@Nullable List<DSSOperation.DSSDocument> documentsToConfirm,
@Nullable List<DSSOperation.DSSDocument> documentsToDecline,
@Nullable DSSOperationsManager.SignMode signMode,
@NonNull KeysSource keysSource,
@Nullable DSSApproveRequestNetworkCallback callback
)
Если нет документов на подтверждение, то в качестве documentsToConfirm
нужно передать null
(не нужно передавать пустой список). Таким же образом нужно действовать и при отсутствии документов на отклонение. Параметры documentsToConfirm
и documentsToDecline
не должны иметь значение null
одновременно.
Загрузка документов на сервер
Для загрузки документа на сервер необходимо использовать метод uploadDocument(...)
класса DSSOperationsManager
:
DSSOperationsManager.uploadDocument(
@NonNull DSSUser user,
@NonNull String title,
@Nullable String snippetTemplate,
@Nullable String previewTemplate,
@NonNull byte[] content,
@Nullable DSSDocumentIdNetworkCallback callback
)
Параметры title
и content
задают название и содержимое документа соответственно. Параметры snippetTemplate
и previewTemplate
задаются в виде HTML-текста и определяют шаблон отображения сниппета документа (краткое описание для отображения документа в списке других документов) и шаблон предварительно просмотра документа. Оба параметра могут не задаваться (иметь значение null
) - формат отображения будет определяться настроенными на сервере шаблонами.
Получение данных документа
Скачать с сервера описание документа (представлено экземплярами класса DSSOperation.DSSDocument
) можно методом getDocumentDescription(...)
класса DSSOperationsManager
:
DSSOperationsManager.getDocumentDescription(
@NonNull DSSUser user,
@NonNull String documentId,
@Nullable DSSDocumentNetworkCallback callback
)
Получение содержимого документа
Для получения оригинального содержимого документа необходимо вызвать метод:
DSSOperationsManager.getDocumentBinaryData(
@NonNull DSSUser user,
@NonNull String docId,
@NonNull DSSGetDocumentBinaryDataCallback callback
)
Когда содержимое документа будет полностью скачано, будет вызван метод:
DSSGetDocumentBinaryDataCallback.success(File)
Кроме того, для отслеживания прогресса скачивания в реализации интерфейса DSSGetDocumentBinaryDataCallback
можно переопределить метод с реализацией по умолчанию:
default void onSomeBytesDownloaded(int downloaded, int total) {
// Скачано downloaded байт из всего total байт
}
При работе в режиме УНЭП возможно также получить представление документа для предварительного просмотра (метод DSSOperationsManagerNonQual.getDocumentPreview(...)
) и "сырое" представление документа в формате PDF (метод DSSOperationsManagerNonQual.getDocumentRawPDF(...)
).
Подписание документов
Подписание документов в режиме УКЭП
В режиме УКЭП подписание документов осуществляется с помощью вызова метода signDocuments(...)
класса DSSOperationsManager
:
signDocuments(
@NonNull DSSUser user,
@NonNull ArrayList<String> documentIds,
@NonNull DSSOperationsManager.SignParams params,
@Nullable DSSSignResultNetworkCallback callback
)
Данный метод отобразит экран со списком подписываемых документов и кнопкой "Подписать", по нажатию на которую на сервер будет отправлен запрос на подписание (а также предварительно будет запрошен пароль при необходимости).
Данный метод требует передачи параметров подписания в виде объекта params
. Параметры подписания включают идентификатор сертификата для подписания и идентификатор шаблона подписи.
Идентификатор шаблона подписи можно получить из параметров сервиса подписи:
DSSPolicyManager.getDSSSignParams(user, new DSSPolicySignServerNetworkCallback() {
@Override
public void success(@NonNull DSSSignServerParams params) {
// Параметры сервиса подписи получены, извлекаем необходимые данные
// Дальнейший код дан ДЛЯ ПРИМЕРА:
// Берётся первый доступный шаблон подписи
String templateId = params.getProcessingTemplates()[0].getId();
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Ошибка запроса параметров
}
});
Подписание документов в режиме УНЭП
В режиме УНЭП подписание документов можно выполнить методом signDocuments(...)
класса DSSOperationsManagerNonQual
:
DSSOperationsManagerNonQual.signDocuments(
@NonNull DSSUser user,
@NonNull List<DSSOperation.DSSDocument> confirmedDocuments,
@NonNull DSSOperationsManager.SignParams params,
@NonNull KeysSource keysSource,
@Nullable DSSSignResultNetworkCallback callback
)
В отличие от метода signDocuments
класса DSSOperationsManager
данный метод принимает список самих документов (а не их идентификаторов), не отображает пользовательский интерфейс для просмотра документов и не отображает экран ввода пароля (на момент вызова этого метода пароль уже должен быть введён).
Работа с клиентской подписью
СДК поддерживает возможность подписания и расшифрования документов с помощью ключей, установленных на мобильном устройстве. При этом для мобильного приложения использование API СДК для подтверждения операций и подписания документов не меняется. Для использования функционала клиентской подписи приложению достаточно сгенерировать ключи на мобильном устройстве, подписать запрос на сертификат и установить сертификат в память устройства после его выпуска.
Создание неподписанного запроса на сертификат
Процесс установки ключей подписи на мобильном устройстве начинается с создания неподписанного запроса на сертификат. В наиболее типичном сценарии этот запрос будет создан оператором на сервере DSS. Однако, СДК поддерживает возможность создания такого запроса на стороне приложения. Для создания неподписанного запроса нужно вызвать метод createCertificate
класса DSSCertificatesManagerNonQual
:
DSSCertificatesManagerNonQual.createCertificate(
@NonNull DSSUser user,
int caId,
@NonNull String templateId,
@NonNull Map<String, String> dn,
boolean isClient,
@Nullable DSSCertificateNetworkCallback callback
)
Использование метода аналогично применению одноимённого метода из класса DSSCertificatesManager
, за исключением того, что необходимо передать значение true
на месте параметра isClient
(передача параметра false
инициирует создание обычного запроса на сертификат, как при использовании DSSCertificatesManager.createCertificate(...)
).
Подписание запроса на сертификат
На этом шаге в приложении создаётся ключевая пара и подписывается созданный запрос, после чего подписанный запрос отправляется на сервер. Далее этот запрос передаётся в УЦ для обработки.
Найти неподписанный запрос можно, запросив список запросов и сертификатов и найдя запрос со статусом sign_wait
:
DSSCertificatesManager.listCertificates(user, new DSSCertificatesNetworkCallback() {
@Override
public void success(DSSCertificate[] certificates) {
for (DSSCertificate item: certificates) {
if (item.getType() == DSSCertificate.Type.Req && item.getState() == DSSCertificate.State.sign_wait) {
// Неподписанный запрос найден
}
}
}
@Override
public void error(DSSNetworkError dssNetworkError) {
// Ошибка запроса списка сертификатов и запросов
}
});
Далее, запрос нужно подписать при помощи метода signCertificateRequest(...)
класса DSSCertificatesManagerNonQual
:
DSSCertificatesManagerNonQual.signCertificateRequest(user, request, keysSource, new DSSSignCertificateRequestCallback() {
@Override
public void success(@NonNull DSSUser user, @NonNull DSSCertificate request) {
// Запрос успешно подписан
// Объект request содержит информацию о подписанном запросе
}
@Override
public void error(@NonNull DSSError error) {
// При подписании запроса возникла ошибка
}
@Override
public void error(@NonNull DSSNetworkError error) {
// При отправке подписанного запроса возникла ошибка
}
});
Данный метод откроет экран ввода пароля (при необходимости), после чего запустит биологический датчик случайных чисел (пользователю будет предложено выполнить серию касаний экрана), после чего будет сгенерирована ключевая пара и подписан запрос, результат подписания будет отправлен на сервер. Если в качестве хранилища будет указан рутокен, тогда будет запрошен пин-код токена.
Установка сертификата
После выпуска сертификата удостоверяющим центром новый сертификат должен быть установлен в память приложения.
Для начала необходимо найти неустановленный сертификат в списке запросов и сертификатов:
DSSCertificatesManager.listCertificates(currentUser, new DSSCertificatesNetworkCallback() {
@Override
public void success(DSSCertificate[] certificates) {
for (DSSCertificate crt: certificates) {
if (crt.getType() == DSSCertificate.Type.Crt
&& crt.getState() == DSSCertificate.State.active
&& crt.isClient()
&& DSSCertificatesManagerNonQual.isCertificateAccessibleOnThisDevice(user, crt)
&& !DSSCertificatesManagerNonQual.isCertificateInstalled(user, crt)) {
// Найден неустановленный сертификат, необходимо выполнить установку
}
}
}
@Override
public void error(DSSNetworkError error) {
// Ошибка получения списка сертификатов и запросов
}
});
Логическая конструкция включает проверку следующих обязательных для установки сертификата условий:
- Объект
DSSCertificate
является сертификатом, а не запросом - Сертификат является активным (может использоваться для подписания)
- Сертификат выпущен по запросу, подписанному локальными (хранящимися на мобильном устройстве) ключами
- Создание ключей и подписание запроса производилось на этом устройстве
- Сертификат ещё не был установлен
Далее, можно устанавливать сертификат. Для этого нужно вызвать метод installCertificate
класса DSSCertificatesManagerNonQual
:
DSSCertificatesManagerNonQual.installCertificate(
@NonNull DSSUser user,
@NonNull DSSCertificate certificate,
@Nullable DSSUserCallback callback
)
После установки сертификата, хранимые на устройстве ключи могут использоваться при подписании и расшифровании документов наравне с ключами, хранимыми на сервере DSS.
Подтверждение операций клиентскими ключами
Операции, созданные на сервере DSS, содержат идентификатор сертификата, который будет использоваться при подписании. При вызове методов confirmOperation(...)
классов DSSOperationsManager
и DSSOperationsManagerNonQual
СДК самостоятельно определит, требуется ли использовать локальные ключи для обработки операции, поэтому никаких дополнительных действий со стороны приложения не нужно.
Подписание документов клиентскими ключами
Для подписания документов клиентскими ключами необходимо при вызове методов signDocuments(...)
классов DSSOperationsManager
и DSSOperationsManagerNonQual
в параметре типа DSSOperationsManager.SignParams
указать идентификатор сертификата, ранее установленного в приложение методом DSSCertificatesManagerNonQual.installCertificate(...)
.
Особенности работы с несколькими устройствами
Если к учётной записи привязано несколько устройств, то при подписании запроса на сертификат на одном устройстве, установка сертификата и дальнейшее его использование возможно только на этом же устройстве. Поэтому, перед подтверждением операций и подписанием документов для целевого сертификата необходимо проводить проверку вида:
if (!certificate.isClient() || DSSCertificatesManagerNonQual.isCertificateAccessibleOnThisDevice(user, certificate)) {
// Сертификат либо облачный (ключи хранятся на сервере DSS),
// либо установлен на этом устройстве,
// то есть им можно пользоваться
}
Если сертификатом нельзя воспользоваться на этом устройстве, то методы подписания документов и подтверждения операций завершаться ошибкой типа DSSError.DSS_ERROR_KEY_PAIR_DOES_NOT_EXIST
.
Изменение внешнего вида БиоДСЧ
При вызове метода DSSCertificatesManagerNonQual.signCertificateRequest(...)
открывается экран с биологическим датчиком случайных чисел. Чтобы изменить внешний вид этого экрана под стиль приложения, необходимо переопределить следующие ресурсы цветов:
<!-- Цвет полосы состояния (StatusBar) -->
<color name="brand_dark_blue">#002f6e</color>
<!-- Цвет кнопки "Отмена" и заполненной части полосы прогресса -->
<color name="brand_blue">#22579D</color>
<!-- Цвет полосы состояния для ночного режима (StatusBar) -->
<color name="night_brand_dark_blue">#121E29</color>
<!-- Цвет кнопки "Отмена" и заполненной части полосы прогресса для ночного режима -->
<color name="night_brand_light_blue">#66A3D8</color>
<!-- Цвет фона ДСЧ для ночного режима -->
<color name="night_brand_color_background">#101A23</color>
<!-- Цвет ячейки, где было касание экрана (для обычного и ночного режима) -->
<color name="bio_cell_color">#888888</color>
Кроме того, можно задать следующие размеры:
<!-- Размер заголовка окна ДСЧ -->
<dimen name="dialog_title_text_size">15sp</dimen>
<!-- Размер кнопки отмены -->
<dimen name="mng_sub_text_size">14sp</dimen>
Следующие строки формируют заголовок окна, центральный текст и текст кнопки отмены соответственно:
<string name="CompanyName">КриптоПро CSP</string>
<string name="BioRnd">Биологический датчик случайных чисел.\n Нажимайте на экран, пока ключ \n не будет создан.</string>
<string name="Cancel">Отмена</string>
Дополнительные параметры
Приведённые выше примеры описывают работу с клиентской подписью с использованием параметров по умолчанию (СДК самостоятельно задаёт имя контейнера для хранения ключей и самостоятельно генерирует ПИН-код для доступа к контейнеру). Такой режим работы СДК является рекомендованным (и наиболее простым в использовании). Однако при необходимости можно использовать класс DSSKeysManagerNonQual
для самостоятельного управления ключами, а также использовать перегрузки методов signCertificateRequest(...)
и installCertificate(...)
класса DSSCertificatesManagerNonQual
с расширенным набором параметров.
Автоматическая подпись
Автоматическая подпись представляет собой механизм подтверждения серии операций без необходимости взаимодействия с приложением.
Для реализации такого механизма приложение должно выполнить следующие шаги.
1. Обнаружение первой операции в цепочке автоматического подписания
При запросе списка операций классическим методом DSSOperationsManager,getOperationList(...) для возвращаемых операций необходимо проверить значение статуса автоподписи, возвращаемое методом DSSOperation.getAutoSignState(). Первая операция в цепочке автоподписи будет иметь значение Aware
. С подтверждения этой операции запускается процесс автоподписания.
2. Подтверждение первой операции
Первая операция в цепочке автоподписания (со статусом Aware
) подтверждается обычным образом, как и все другие операции, через метод confirmOperation(...)
. Содержимое операции будет отображено пользователю, при необходимости будет запрошен пароль (т.е. визуально подтверждение такой операции ничем не отличается).
3. Запрос подтверждения перехода в режим автоматического подписания
После подтверждения операции со статусом Aware
приложение должно отобразить пользователю экран с предложением подтверждать дальнейшие операции из цепочки в автоматическом режиме. При показе этого экрана может быть использована информация о прикладной системе, создавшей операцию. Получить эту информацию можно при помощи метода DSSOperation.getAppSystemInfo(). В частности, приложению необходимо сохранить идентификатор прикладной системы, возвращаемый методом AppSystemInfo.getClientId(). Данный идентификатор нужен для получения остальных операций из цепочки.
4. Получение последующих операций на автоподписание
После согласия пользователя перейти в режим автоматического подписания приложение должно отобразить экран, информирующий, что пользователь находится в режиме автоматической подписи. В этом режиме приложение должно периодически опрашивать сервер на наличие следующих операций в цепочке. Для этого нужно вызывать перегрузку метода DSSOperationsManager.getOperationList(...)
, принимающую идентификатор прикладной системы (должен быть сохранён из первой операции вызовом AppSystemInfo.getClientId()
) в качестве параметра фильтрации. В качестве результата сервер будет возвращать операции, для которых DSSOperation.getAutoSignState() == Enabled
, что означает возможность подтверждения этих операций автоматически без отображения пользователю.
5. Подтверждение полученных операций
Полученные операции со статусом DSSOperation.getAutoSignState() == Enabled
должны быть подтверждены методом DSSOperationsManagerNonQual.confirmOperation(...)
. При этом, перед вызовом необходимо проверить выполнение условия user.isReadyToSign() == true
, так как данный метод подтверждает операцию в фоновом режиме и не запрашивает пароль. При невыполнении условия user.isReadyToSign() == false
(может возникнуть, например, вследствие сворачивания приложения более чем на 15 секунд) следует вывести приложение из режима автоподписи и вернуться в точку, из которой операции запрашиваются классическим образом через метод DSSOperationsManager.getOperationList(...) без указания clientId
для фильтрации. Кроме того, следует выводить приложение из режима автоподписи по желанию пользователя, например, через возврат на предыдущий экран при нажатии системной кнопки "Назад".
Создание и восстановление резервных копий
Создание и восстановление резервных копий может быть полезно при смене устройства или если пользователь забыл пароль к своему профилю, при этом он создавал резервную копию и запомнил пароль для восстановления (recovery password). Профиль пользователя и ключи пользователя можно экспортировать и/или восстановить с помощью методов из таблицы ниже.
Наименование | Предназначение |
---|---|
DSSUsersManagerNonQual.createBackup(...) | Экспорт данных пользователя (объекта DSSUser ) в JSON с шифрованием при помощи указанного пароля. Резервная копия возвращается в приложение в виде JSON-строки. |
DSSUsersManagerNonQual.restoreBackup(...) | Восстановление объекта DSSUser из резервной копии. Резервная копия представляет собой ранее созданную JSON-строку. |
DSSCertificatesManagerNonQual.exportPfx(...) | Создание резервной копии (архивация) ключей подписи, хранимых на мобильном устройстве, на сервере DSS. Опционально может быть указан ПИН-код для шифрования резервной копии. Если он не указан, сервером будет использован ПИН-код по умолчанию. |
DSSCertificatesManagerNonQual.importPfx(...) | Восстановление резервной копии ключей подписи, ранее архивированных на сервере DSS. Если при архивации был задан ПИН-код, его потребуется предъявить при восстановлении. |
DSSCertificatesManagerNonQual.deletePfx(...) | Удаление ранее созданной резервной копии ключей с сервера DSS |
DSSKeysManagerNonQual.createBackup(...) | Создание резервной копии ключей подписи, хранимых на мобильном устройстве с шифрованием при помощи указанного пароля. Резервная копия возвращается в приложение в виде JSON-строки. |
DSSKeysManagerNonQual.restoreBackup(...) | Восстановление локальных ключей подписи из резервной копии. Резервная копия представляет собой ранее созданную JSON-строку. |
Резервное копирование данных профиля
Данные профиля (информация об устройстве и симметричные ключи аутентификации, хранимые в объекте DSSUser
) могут быть архивированы в зашифрованном виде для дальнейшего восстановления на том же самом устройстве (например, при переустановке приложения), либо на другом устройстве (например, если пользователь решил сменить устройство или вынужден временно воспользоваться другим устройством).
Резервная копия профиля создаётся в виде JSON-строки, которая возвращается в приложение. JSON-строка содержит данные, зашифрованные при помощи указанного при создании копии пароля, и может безопасно быть сохранена приложением в желаемом месте.
Примером хранилища для резервных копий может служить личный Google Drive пользователя. Приложение может создать в Google Drive свою служебную директорию, доступную только самому приложению, и сохранять в неё резервную копию.
Пример экспорта профиля пользователя:
DSSUsersManagerNonQual.createBackup(user, recoveryPassword, new DSSUserBackupCallback() {
@Override
public void success(@NonNull String backup) {
// Профиль пользователя был экспортирован в JSON-строку.
// Приложение должно сохранить эту сроку таким образом, чтобы можно было отдать
// её назад в СДК при восстановлении профиля на этом же или другом устройстве
}
@Override
public void error(@NonNull DSSError error) {
// Не удалось экспортировать профиль пользователя
}
});
Когда потребуется восстановить данные профиля из резервной копии, приложение должно передать ту же самую JSON-строку и тот же пароль в метод восстановления резервной копии.
Пример импорта (восстановления) профиля пользователя:
DSSUsersManagerNonQual.restoreBackup(backupString, recoveryPassword, new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser restoredUser) {
// Пользователь успешно импортирован, далее его необходимо сохранить в долгосрочную память
DSSUsersManagerNonQual.store(restoredUser, restoredUser.getName(), new DSSUserCallback() {
@Override
public void success(@NonNull DSSUser user) {
// Пользователь готов к работе
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка при сохранении восстановленного профиля пользователя
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Сетевая ошибка при сохранении восстановленного профиля пользователя
}
});
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка при импортировании профиля пользователя
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Сетевая ошибка при импортировании профиля пользователя
}
});
Архивирование ключей подписи на сервере DSS
На сервере DSS могут быть архивированы только ключи, созданные с флагом exportable
. Все ключи подписи создаются автоматически с таким флагом, начиная с версии СДК 2.1.392, если только приложение само не использует метод DSSKeysManagerNonQual.createKeyPair(...)
с передачей объекта KeyInfo
с явно заданным параметром isExportable
как false
. До версии СДК 2.1.392 все ключи создавались как неэкспортируемые.
Для создания резервной копии ключей подписи в приложении нужно выбрать соответствующий сертификат и передать его в метод DSSCertificatesManagerNonQual.exportPfx(...)
. В примере ниже выполняется резервное копирование всех доступных на устройстве ключей подписи:
DSSCertificatesManager.listCertificates(user, new DSSCertificatesNetworkCallback() {
@Override
public void success(@NonNull DSSCertificate[] certificates) {
// Успешно получили все запросы и сертификаты
for (DSSCertificate entity: certificates) {
// Проверяем каждый объект, чтобы найти сертификаты, пригодные для архивации ключей
if (entity.getType() == DSSCertificate.Type.Req) {
// Пропускаем все запросы на сертификаты
continue;
}
if (!entity.isClient()) {
// Пропускаем сертификаты, для которых ключи хранятся на сервере
continue;
}
if (entity.isArchived()) {
// Пропускаем ключи, которые уже были архивированы
continue;
}
if (!DSSCertificatesManagerNonQual.isCertificateAccessibleOnThisDevice(user, entity)) {
// Пропускаем ключи, которые хранятся на другом устройстве
continue;
}
// Все необходимые условия проверены - можем архивировать ключи
DSSCertificatesManagerNonQual.exportPfx(user, entity, null, "My PIN", new DSSExportPfxCallback() {
@Override
public void success() {
// Ключи успешно архивированы на сервере DSS
}
@Override
public void error(@NonNull DSSError error) {
// Архивация ключей неуспешна из-за внутренней ошибки СДК
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Архивация ключей неуспешна из-за сетевой ошибки
}
});
}
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Ошибка получения списка сертификатов
}
});
В качестве третьего параметра метод exportPfx(...)
требует ПИН-кода от контейнера с ключевой информацией, подлежащей архивации. В приведённом выше примере передается null
, так как СДК использует ПИН-код по умолчанию для защиты контейнеров. Если вы используете свой ПИН-код при создании объектов KeyInfo
, то вместо null
необходимо указывать его.
После архивации ключей их можно восстановить на том же самом устройстве (либо на другом устройстве, где есть объект DSSUser
, привязанный к той же учётной записи). Для этого необходимо вызвать метод importPfx(...)
:
if (certificate.isArchived()) {
// Ключи были архивированы ранее и могут быть восстановлены
DSSCertificatesManagerNonQual.importPfx(user, certificate, null, "My PIN", new DSSImportPfxCallback() {
@Override
public void success() {
// Ключи успешно восстановлены на устройстве
}
@Override
public void error(@NonNull DSSError error) {
// Восстановление ключей невозможно из-за внутренней ошибки СДК
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Восстановление ключей невозможно из-за сетевой ошибки
}
});
}
Третий параметр задаёт ПИН-код для защиты контейнера с ключевой информацией. При указании null
будет использован ПИН-код по умолчанию.
Созданную ранее архивную копию ключей можно удалить с сервера DSS:
if (certificate.isArchived()) {
// Ключи были архивированы ранее - резервная копия может быть удалена
DSSCertificatesManagerNonQual.deletePfx(user, certificate, new DSSDeletePfxCallback() {
@Override
public void success() {
// Копия ключей удалена с сервера DSS
}
@Override
public void error(@NonNull DSSError error) {
// Удаление ключей провалилось из-за внутренней ошибки СДК
}
@Override
public void error(@NonNull DSSNetworkError error) {
// Удаление ключей провалилось из-за сетевой ошибки
}
});
}
Архивирование ключей подписи с созданием резервной копии на устройстве
Альтернативно, приложение может выполнить резервное копирование ключей подписи аналогично данным профиля: создать резервную копию в виде JSON-строки с зашифрованными на пароле данными и сохранить эту строку в нужном месте средствами самого приложения, после чего восстановить ключи из этой же строки.
Пример создания резервной копии данных ключа подписи:
// Получить данные о локальных ключах пользователя
List<KeyInfo> keys = DSSKeysManagerNonQual.getKeysForUser(user);
// Взять ключ, для которого будет создана резервная копия
// для примера - только первый ключ, но чаще всего необходимо обойти всю коллекцию и для каждого локального ключа создать резервную копию
KeyInfo key = keys.get(0);
DSSKeysManagerNonQual.createBackup(key, recoveryPass, new DSSKeyInfoBackupCallback() {
@Override
public void success(@NonNull String keysBackupJson) {
// Данные ключа подписи были экспортированы в строку keysBackupJson, далее её можно сохранить
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка при экспортировании данных ключа подписи
}
});
Пример восстановления данных ключа подписи:
DSSKeysManagerNonQual.restoreBackup(keysBackupJson, recoveryPass,
new DSSKeyInfoRestorationCallback() {
@Override
public void success(@NonNull KeyInfo keyInfo) {
// Данные ключа подписи были успешно импортированы
}
@Override
public void error(@NonNull DSSError error) {
// Ошибка при импортировании данных ключа подписи
}
});
Кастомизация пользовательского интерфейса
При работе в режиме УКЭП СДК показывает пользовательский интерфейс при инициализации, регистрации устройства, подтверждении операций, подписании документов и других действиях.
Кастомизация с помощью класса Appearance
Класс Appearance
может быть использован для кастомизации пользовательского интерфейса, отображаемого СДК. Приложение может менять следующие параметры:
- Цвет фона экранов и отдельных элементов (кнопок, галочек и др.)
- Цвета, шрифты и размеры отдельных надписей
- Иконки, используемые в интерфейсе
- Параметры панели действий (ActionBar)
- Анимации смены фрагментов, диалоговых окон, некоторых представлений.
Полный список настроек можно посмотреть в документации на класс Appearance.
Ниже приведено несколько примеров применения класса Appearance
:
// Изменение цвета основных кнопок
MyDss.getAppearance().getButtons().getPrimary().backgroundColor = R.color.some_color_in_my_app;
// Изменение ресурса иконки информации об операции
MyDss.getAppearance().getImages().getOperationInfoIcon().icon = R.drawable.some_icon_in_my_app;
// Изменение фона под иконкой информации об операции
MyDss.getAppearance().getImages().getOperationInfoIcon().iconTint = R.color.some_color_in_my_app;
// Изменение цвета заголовков модальных окон
MyDss.getAppearance().getLabels().getModalTitle().color = R.color.some_color_in_my_app;
// Изменение начертания заголовков модальных окон
MyDss.getAppearance().getLabels().getModalTitle().fontWeight = Appearance.LabelAppearance.FontWeight.Medium;
// Изменение размера кнопки виртуальной клавиатуры
MyDss.getAppearance().getButtons().getKeyboard().getAppearance().size = R.dimen.some_dimen_in_my_app;
// Изменение фона индикатора прогресса
MyDss.getAppearance().getViews().getProgressBar().backgroundColor = R.color.some_color_in_my_app;
// Изменение цвета заголовка на панели инструментов
MyDss.getAppearance().getBaseAppearance().getMain().getTitle().color = R.color.some_color_in_my_app;
Кастомизация с помощью класса LayoutMapper
Класс LayoutMapper
может быть использован для замены файлов разметки, использованных в СДК. В таблице ниже представлены важные классы для кастомизации.
Наименование | Предназначение |
---|---|
FragmentsLayouts | Кастомизация фрагментов. |
DialogsLayouts | Кастомизация диалоговых окон. |
ListsItems | Кастомизация элементов списков. |
CustomViews | Кастомизация прочих представлений (клавиатура для ввода пина, визуализация пинов). |
По умолчанию используется разметка из макетов SDK, поэтому необязательно заменять их все. Однако, использование своей разметки экранов отключает возможность управления их внешним видом через класс Appearance
.
Макеты должны содержать необходимые для функционирования SDK идентификаторы, если их не будет (или их тип не будет совпадать), тогда SDK во время выполнения будет генерировать ошибки (для фрагментов) либо не выводить их на экран (для всего остального). В случае ошибки в лог будет сделана запись, начинающаяся с Error custom mapping view
.
Для упрощения замены вёрстки через класс LayoutMapper
разметки рекомендуется:
- Скачать aar-файл SDK своей версии из репозитория.
- Разархивировать файл SDK mydss-*.aar любой программой-архиватором, поддерживающем формат архивации файлов zip.
- Перейти в папку
\res\layout
и скопировать оттуда файлы с префиксамиdsssdk_fragment_
,dsssdk_dialog_
,dsssdk_item_
,dsssdk_layout_
,dsssdk_include_toolbar_
, а так же файлdsssdk_menu_document_view_options.xml
в свой проект. - Переименовать префикс
dsssdk_
в названии файлов на другой, например наcustom_
. При этом необходимо поправить ссылки на переименованные файлы с префиксомdsssdk_include_toolbar_
в строках<include layout="@layout/dsssdk_include_toolbar_"
. - Произвести необходимые изменения в вёрстке макетов, при этом необходимо сохранить обязательные идентификаторы, их типы. Полный список обязательных полей можно посмотреть в документации на класс LayoutMapper.
- Перед инициализацией SDK получить объект класса
LayoutMapper
изMyDss.getLayoutsMapper()
и установить идентификаторы файлов со своей вёрсткой.
Пример установки идентификатора кастомного диалога загрузки:
MyDss.getLayoutsMapper().getDialogs().setLoaderDialogLayoutId(R.layout.custom_dsssdk_dialog_loader);
Дополнительная информация
Альтернативное логирование
По умолчанию СДК записывает логи в LogCat. Это поведение можно изменить, передав в метод MyDss.setAlternativeLogger(AlternativeLogger)
свою реализацию интерфейса AlternativeLogger
.
Базовая реализация интерфейса AlternativeLogger
выглядит следующим образом:
import android.util.Log;
public interface AlternativeLogger {
default int v(String tag, String msg) {
return Log.v(tag, msg);
}
default int v(String tag, String msg, Throwable tr) {
return Log.v(tag, msg, tr);
}
default int d(String tag, String msg) {
return Log.d(tag, msg);
}
default int d(String tag, String msg, Throwable tr) {
return Log.d(tag, msg, tr);
}
default int i(String tag, String msg) {
return Log.i(tag, msg);
}
default int i(String tag, String msg, Throwable tr) {
return Log.i(tag, msg, tr);
}
default int w(String tag, String msg) {
return Log.w(tag, msg);
}
default int w(String tag, String msg, Throwable tr) {
return Log.w(tag, msg, tr);
}
default int e(String tag, String msg) {
return Log.e(tag, msg);
}
default int e(String tag, String msg, Throwable tr) {
return Log.e(tag, msg, tr);
}
}
Таким образом, приложение может переопределить только те методы, которые нужно.
Работа с Рутокеном
При выполнении основных сценариев SDK самостоятельно выполняет необходимые взаимодействия с токенами (проверяет или запрашивает включение модуля NFC, установку ПУР (панель управления Рутокен), ожидает, когда пользователь приложит токен).
Для того, чтобы получить информацию о хранилище ключей, необходимо использовать метод KeysSourceIdentifier.getKeysSourceIdentifier(DSSUser, certificateId)
с идентификатором сертификата.
Создание ключевой пары
Для создания ключевой пары и подписи запроса на Рутокене необходимо вызвать DSSCertificatesManagerNonQual.signCertificateRequest(dssUser, request, selectedKeysSourceIdentifier)
с параметром selectedKeysSourceIdentifier
равным предопределённому значению KeysSourceIdentifier.rutokenNFC
.
Если было успешное взаимодействие с Рутокеном (независимо, метод DSSCertificatesManagerNonQual.signCertificateRequest
отработал штатно или с ошибкой), то будет означен параметр KeysSource
в обратном вызове. Если при этом токен был непроинициализирован, то SDK его проинициализирует и установит на него случайный PUK-код. Чтобы получить его в приложении из KeysSource
, необходимо преобразовать KeysSource
в KeysSourcePukInitializable
и вызвать getPuk()
.
Использование существующих на Рутокене сертификатов
Для подключения к СДК существующих сертификатов на Рутокене, необходимо:
1. Вызвать DSSCertificatesManagerNonQual.getExternalCertificates(DSSExternalCertificatesCallback)
и получить список сертификатов на токене
2. На выбранном из списка сертификате вызвать DSSCertificatesManager.setCertificate(DSSUser user, DSSCertificate, DSSCertificateNetworkCallback)
Инициализация SDK со списком доверенных приложений
При инициализации SDK функцией init
класса MyDss
выполняются проверки (с отображением соответствующих предупреждений) устройства на наличие прав суперпользователя (root), отсутствие антивируса, а также наличие приложений с правами android.permission.PACKAGE_USAGE_STATS
и android.permission.SYSTEM_ALERT_WINDOW
.
Если по вашему мнению SDK ошибочно определило некоторое приложение как вредоносное и показывает пользователю предупреждение, которое вы считаете избыточным, вы можете передать в функцию init
список "доверенных" приложений в виде параметра HashMap<String, String[]> trustedApps
, где ключом выступает идентификатор приложения, а значением - список хэшей (полученных по алгоритму SHA-256) подписей разработчика приложения.
В подавляющем большинстве случаев разработчики используют один ключ для подписания приложения перед публикацией в Play Market, поэтому чаще всего массив
String[]
будет содержать один элемент.
Получение подписи приложения и передача её в SDK
Рассмотрим алгоритм включения некоторого приложения в белый список приложений MyDSS SDK на примере приложения Google Photos.
Шаг 1. Установка приложения на устройство
Сначала необходимо установить из Play Market требуемое приложение на любой аппарат под управлением ОС Android, после чего подключить устройство к компьютеру по USB-кабелю в режиме отладки.
Шаг 2. Получение доступа к файлу apk приложения
Далее необходимо скопировать apk-архив из директории приложения на компьютер. Для этого необходимо использовать утилиту adb
. При этом следует учесть, что к компьютеру не должно быть подключено по USB-кабелю других Android-устройств.
Утилита adb
может быть установлена через Android Studio посредством инструмента SDK Manager
. Подробнее на сайте разработчиков.
Команда
adb shell pm list packages
перечислит все установленные на подключённом устройстве приложения в виде пакетов. При желании можно отфильтровать полученный список удобным образом. В полученном списке будет строка package:com.google.android.apps.photos
, где com.google.android.apps.photos
- имя пакета (очевидно, соответствующее приложению Google Photos), которое понадобится для получения apk-файла, а также в качестве ключа в параметре trustedApps
.
Выполнение команды
adb shell pm path com.google.android.apps.photos
позволяет получить пути ко всем apk-файлам приложения Google Photos. Результат выполнения команды будет примерно следующим:
package:/data/app/com.google.android.apps.photos-2/base.apk
package:/data/app/com.google.android.apps.photos-2/split_config.en.apk
package:/data/app/com.google.android.apps.photos-2/split_config.ru.apk
package:/data/app/com.google.android.apps.photos-2/split_config.uk.apk
package:/data/app/com.google.android.apps.photos-2/split_config.xxxhdpi.apk
Подпись приложения можно получить из файла base.apk
, поэтому именно его необходимо скопировать на компьютер.
Для этого можно выполнить команду
adb pull /data/app/com.google.android.apps.photos-2/base.apk
Она скопирует base.apk
в текущую директорию на вашем ПК.
Шаг 3. Извлечение подписи разработчика
Откройте apk-файл любым архиватором и перейдите в папку META-INF
. Внутри этой папки будет один файл с расширением .RSA
, именно он содержит подпись разработчика. Его необходимо разархивировать. Как правило, файл называется CERT.RSA
или BNDLTOOL.RSA
(иногда могут встречаться другие названия).
Далее необходимо прочитать содержимое файла. Сделать это можно, например, при помощи утилиты keytool
, входящей в состав JRE
.
keytool -printcert -file BNDLTOOL.RSA
Результат выполнения команды будет выглядеть примерно следующим образом:
Owner: CN=Unknown, OU="Google, Inc", O="Google, Inc", L=Mountain View, ST=CA, C=US
Issuer: CN=Unknown, OU="Google, Inc", O="Google, Inc", L=Mountain View, ST=CA, C=US
Serial number: 4934987e
Valid from: Tue Dec 02 05:07:58 MSK 2008 until: Sat Apr 19 05:07:58 MSK 2036
Certificate fingerprints:
MD5: D0:46:FC:5D:1F:C3:CD:0E:57:C5:44:40:97:CD:54:49
SHA1: 24:BB:24:C0:5E:47:E0:AE:FA:68:A5:8A:76:61:79:D9:B6:13:A6:00
SHA256: 3D:7A:12:23:01:9A:A3:9D:9E:A0:E3:43:6A:B7:C0:89:6B:FB:4F:B6:79:F4:DE:5F:E7:C2:3F:32:6C:8F:99:4A
Signature algorithm name: MD5withRSA (weak)
Subject Public Key Algorithm: 1024-bit RSA key
Version: 1
Нам необходим хэш, полученный по алгоритму SHA-256. Для этого копируем значение:
3D:7A:12:23:01:9A:A3:9D:9E:A0:E3:43:6A:B7:C0:89:6B:FB:4F:B6:79:F4:DE:5F:E7:C2:3F:32:6C:8F:99:4A
Шаг 4. Добавление приложения в SDK в качестве доверенного
После того, как у на есть App ID приложения и значение хэша подписи разработчика, мы можем проинициализировать SDK следующим образом:
String appId = "com.google.android.apps.photos";
String hash = "3D:7A:12:23:01:9A:A3:9D:9E:A0:E3:43:6A:B7:C0:89:6B:FB:4F:B6:79:F4:DE:5F:E7:C2:3F:32:6C:8F:99:4A";
HashMap<String, String[]> trustedApps = new HashMap<>();
trustedApps.put(appId, new String[]{hash});
MyDss.init(
activity.getApplicationContext(),
MyDss.RootCertificateType.Development,
MyDss.DSS_LOG_DEBUG,
trustedApps, // Список "доверенных" приложений, убран из аргументов начиная с версии 2.1.616
new DSSInitCallback() {
@Override
public void error(DSSError error) {
// ...
}
@Override
public void success(MyDss myDssInstance) {
// ...
}
});
Альтернативный способ получения подписи разработчика приложения
Подпись разработчика приложения можно также получить программно, написав и установив небольшое приложение на Android-устройство.
Код получения подписей всех установленных приложений выглядит следующим образом:
private void getSHA256SignaturesForAllApps() throws NoSuchAlgorithmException {
PackageManager p = getApplicationContext().getPackageManager();
final List<PackageInfo> installedApps = p.getInstalledPackages(
PackageManager.GET_PROVIDERS |
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
? PackageManager.GET_SIGNING_CERTIFICATES
: PackageManager.GET_SIGNATURES));
for (PackageInfo i : installedApps) {
Log.w(TAG, "Package: "+i.packageName);
Log.i(TAG, " -- name: "+p.getApplicationLabel(i.applicationInfo).toString());
Signature[] appSignatures;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
appSignatures = i.signatures;
} else {
appSignatures = i.signingInfo.getSigningCertificateHistory();
}
for (Signature s: appSignatures) {
String sigHex = bytesToHex(s.toByteArray());
String sigSHA256Hex = bytesToHex(MessageDigest.getInstance("SHA-256").digest(s.toByteArray()));
Log.i(TAG, " -- signature: "+sigHex);
Log.i(TAG, " -- signature SHA256 digest: "+sigSHA256Hex);
}
}
}
private String bytesToHex(byte[] bytes) {
// Представление байтов в hex-формате
char[] hexArray = "0123456789abcdef".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
Особенности работы на Android 11+
При запуске СДК на устройстве под управлением Android 11 или новее, проверка наличия потенциально вредоносных приложений осуществляться не будет. Это связано с ограничениями ОС на доступ к списку установленных приложений. Чтобы обеспечить запуск такой проверки, необходимо в манифест приложения добавить разрешение:
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
Использование этого разрешения потребует заполнения дополнительной декларации при публикации приложения в Google Play. Подробнее об этом прочесть в соответствующем разделе документации.
Запуск Activity из СДК и задачи Android
По умолчанию, при вызове методов СДК, требующих показа пользовательского интерфейса, экземпляры Activity из СДК запускаются в составе новой задачи (используется вызов Context.startActivity()
с флагом Intent.FLAG_ACTIVITY_NEW_TASK
- это требование ОС). Однако, в некоторых случаях такое поведение может быть нежелательным и предпочтительнее запускать Activity СДК в составе той же самой задачи. Тогда необходимо использовать перегрузки методов СДК с первым параметром типа Activity
, подставляя в качестве аргумента экземпляр Activity приложения.
К таким методам относятся:
MyDss.init(...)
(в качестве экземпляра Context можно передать Activity)DSSUsersManager.createDSSUser(...)
DSSUsersManager.createDSSUserWithInitQR(...)
DSSUsersManager.createDSSUserWithApproval(...)
DSSUsersManager.acceptAccountChanges(...)
DSSUsersManager.checkApprovalStatus(...)
DSSUsersManager.submitPassword(...)
DSSUsersManager.changePassword(...)
DSSUsersManager.revoke(...)
DSSUsersManager.renew(...)
DSSDeviceManager.processAwaitingDevice(...)
DSSOperationsManager.confirmOperation(...)
DSSOperationsManager.signDocumentsOffline(...)
DSSOperationsManager.signDocuments(...)
DSSCertificatesManagerNonQual.signCertificateRequest(...)
DSSCertificatesManagerNonQual.getExternalCertificates(...)