ЖТЯИ.00118-01 97 02-01 КриптоКлюч SDK. Руководство Разработчика
КриптоКлюч SDK для встраивания в мобильное приложение представляет собой набор программных компонентов для использования в мобильных приложениях, который позволяет производить удаленное выполнение операций подписи и управление сертификатами, а также подтверждать операции Пользователя в КриптоПро Ключ, инициированные другими способами.
Термины и определения
Тип | Описание |
---|---|
User | Объект, содержащий всю необходимую информацию для выполнения действий от имени пользователя. Содержит, в том числе, информацию для доступа к экземпляру СЭП (url, корневой сертификат и пр.), "вектор аутентификации" для подтверждения действий и пр. С точки зрения СЭП данный объект является устройством, подключенным к учетной записи пользователя СЭП |
Device | Объект, содержащий информацию об устройствах, подключенных к той же учетной записи, что и объект User. Данный объект не содержит "вектор аутентификации" и не может использоваться для подтверждения операций или выполнения других действий |
Operation | Операция СЭП, для которой требуется подтверждение/отклонение. Операция может содержать несколько документов. При подтверждении/отклонении операции все содержащиеся в ней документы будут подписаны/отклонены. Операция создается на сервере СЭП |
Operation.Document | Документ, входящий в операцию, либо обрабатываемый самостоятельно |
Document Description | "Сниппет" документа, сформированный сервером СЭП на основе шаблона для сниппета и содержания документа |
Document Preview | Визуализированный в человеко-читаемую форму документ, сформированный сервером СЭП на основе шаблона для визуализации и содержания документа |
Document RawPDF | "Сырое" содержание документа (например, текстового файла), преобразованное в формат PDF |
Certificate | Сертификат, привязанный к ключу подписи в учетной записи на СЭП |
Установка
Для использования CKeySDK
необходимо добавить в проект:
- Фреймворк
- Ресурсы
- Корневые сертификаты
Добавление фреймворка
Swift Package Manager
Добавьте пакет CKeySDK
из репозитория:
https://.../ckey.git
CocoaPods
Добавьте в файл Podfile
зависимости:
pod 'CKeySDK', :git => '...'
Ручная установка
Добавьте в проект CKeySDK.xcframework
Carthage
Т.к. Carthage не поддерживает распространение бинарных фреймворков, данный метод мы не используем.
Ресурсы
Ресурсы лежат в репозитории в папке Resources
. Их необходимо прикрепить к приложению. Папку locale
нужно добавить с флагом Create folder references
.
При интеграции с помощью SPM/CocoaPods фреймворк можно найти в Project Navigator
.
- SPM:
Package Dependencies -> CKeySDK -> Referenced Binaries -> CKeySDK.xcframework
- CocoaPods:
Pods -> Pods -> CKeySDK -> Frameworks -> CKeySDK.xcframework
Корневые сертификаты
Для взаимодействия с серверами в ресурсы приложения в папку root-certs
нужно поместить сертификаты:
AppName.app/root-certs/
cert_1.crt
cert_2.cer
...
cert_N.certificate
Дополнительные действия
После сборки таргета необходимо запустить скрипт
Также можно переместить Инициализировать библиотеку нужно перед вызовом внутренних функций SDK. Можно выставить уровень логирования и режим работы. Пример для При Для работы c УКЭП можно использовать: Для работы c УНЭП можно использовать любые объекты и функции. Есть классы, которые включают в названии Перед тем, как начать подписывать документы и операции, нужно создать пользователя. Есть 3 способа это сделать: Пользователь создан и сохранен в хранилище, его статус — Как только статус пользователя станет При верификации может потребоваться QR-код. Это зависит от настроек политики сервера. Теперь статус пользователя При создании пользователя данным способом потребуется отсканировать QR-код, сгенерированый сервером. После создания статус пользователя — Данный способ применяется, когда уже есть устройство с подтвержденным пользователем и нужно привязать новое устройство. Потребуется передать идентификатор данного пользователя на новое устройство. Получить идентификатор можно у экземпляра пользователя: На новом устройстве вызываем метод: Мы создали нового пользователя, его статус — На основном устройстве вызываем метод Далее, на новом устройстве проверяем статус: Теперь новое устройство подтверждено, статус пользователя — В сценариях УНЭП интерфейс SDK не используется, применяются дополнительные методы для работы с данными и используется класс Для начала регистрации мы вызываем метод После регистрации будет создан После успешного сохранения у пользователя сменится статус на После смены статуса на Так же, для присоединения может потребоваться QR-код: если Персональные данные клиента для присоединения можно проверить в Для начала регистрации с QR кодом его нужно получить и распарсить. После этого его можно передать в метод QR-код может требовать активации. Код активации предоставляет сервер. Длину кода активации можно узнать, вызвав После регистрации будет создан Для привязки к другому устройству требуется знать После регистрации будет создан После сохранения статус сменится на Если для присоединения требуется отобразить персональные данные нового пользователя, то их можно найти в После этого на новом устройстве нужно проверить и обновить статус пользователя: Управление пользователями на текущем устройстве осуществляется с помощью классов Управление устройствами осуществляется с помощью Пользователей, установленных на устройстве, можно получить в Связанные устройства (в том числе и ожидающие подтверждения) можно получить, вызвав Добавление пользователя на новое устройство нужно подтвердить на старом с помощью метода С остальными методами можно ознакомиться в описаниях классов Для параметров подписания нужно получить параметры сервера подписания и идентификатор сертификата: С остальными доступными методами можно ознакомиться в описании API: Создание и восстановление резервных копий может быть полезно при смене устройства или если пользователь забыл пароль к своему профилю. Для создания резервной копии и восстановления понадобится придумать пароль для восстановления — Резервная копия Резервная копия Ключи, созданные с флагом Для создания резервной копии сертификат ключей подписи нужно передать в метод После архивации ключей их можно восстановить как на том же самом устройстве, так и на другом устройстве, где есть объект Для удаления архивной копии на сервере доступен метод Для обновление профиля устройства и симметричных ключей можно воспользоваться методом Для работы с NFC требуется отдельная сборка Добавьте пакет Добавьте в файл Добавьте в проект CKey_NFC.xcframework. Необходимо добавить в проект ресурсы аналогично инструкции. Для работы с картами и токенами Рутокен нужно дополнительно настроить проект по документации Рутокен. Убедитесь, что в проект добавлены все необходимые библиотеки и указаны все идентификаторы карт. Для работы с NFC токенами доступно 2 сценария: Для импорта сертификата сначала нужно получить с карты все доступные сертификаты, а потом нужный загрузить на сервер. При перечислении сертификатов будет возвращен массив После перечисления необходимо выбрать нужный сертификат и выгрузить его в СЭП. Для этого нужно передать его в метод После выгрузки сертификата NFC-токен можно использовать для подписании документов. Создание ключей происходит при вызове метода После создания ключей и подписания запроса на сертификат, нужно дождаться выпуска сертификата. Текущие сертификаты проверяем методом Как только сертификат будет выпущен, его можно установить на устройство методом После установки сертификата NFC-токен можно использовать для подписания документов. У фреймворка есть возможность подключить внешний логгер. Для этого логгер должен наследовать протокол После определения логгера, его нужно передать в метод После этого сообщения из SDK будут передаваться в установленный логгер. В SDK нет встроенных методов для сохранения логов файл. Но можно подключить стороннюю библиотеку, например CocoaLumberjack: Чтобы получить логи, используем API CocoaLumberjack: За внешний вид интерфейса отвечают файлы экранов В ассетах Для настройки оформления через код используются классы с префиксом Если сбросить класс компонентов на стандартный ( Данные ошибки возвращает SDK. При возникновении ошибки The request has timed out Превышено ожидание ответа от сервера. The server name could not be resolved. Не удается подключиться к конечной точке. Возможные причины: WinINet failed to perform content decoding on the response. For more information, see the Content Encoding topic. Возникла проблема с сертификатами. Возможные причины: Методы SDK в которых есть интерфейс, могут возвращать ошибку Данные ошибки возвращает сервер. При обработке статуса пользователя можно наткнуться на следующие ошибки: Т.е. например при вызове ConfigureApplication
, который лежит в репозитории в папке Build Phases -> New Run Script Phase
. Примеры для различных способов установки:# SPM
sh ${BUILD_DIR%Build/*}/SourcePackages/checkouts/ckeysdk/Tools/ConfigureApplication
# CocoaPods
tbw
ConfigureApplication
из репозитория в любое удобное место и указать нужный путь. Инициализация
AppDelegate
:import UIKit
import CKeySDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Установка уровня логирования и настройка конфигурации
#if DEBUG
CKeySDK.setLogLevel([.debug, .keys])
let configuration = CKey.Configuration(debugMode: true)
#else
let configuration = CKey.Configuration()
#endif
do {
// Инициализация библиотеки
try CKey.initialize(configuration: configuration)
} catch {
...
}
...
}
}
debugMode: true
проверка сертификатов на отзыв отключена, при запуске приложения отобразится экран, что сдк находится в режиме разработчика. Объекты и функции
NonQual
, в них есть расширенный набор методов:
Привязка устройства к учетной записи
Сценарии УКЭП
Онлайн-привязка устройства
let createdUser = try await UsersManager.createUser(
serviceURL: serverURL, // Адрес сервера СЭП
name: name, // Имя для сохранения учетной записи
pushNotificationsData: data, // Данные для отправки пуш-уведомлений
deviceName: deviceName, // Отображаемое дружественное имя устройства
externalId: externalId, // Внешний идентификатор
alias: alias, // Человекочитаемый идентификатор устройства
requirePassword: requirePwd // Требуется ли установка пароля
)
.installed
. Теперь нужно дождаться, когда его статус на сервере СЭП сменится на .notVerified
. Чтобы проверить текущий статус:let updatedUser = try await UsersManager.updateStatus(
user: user
)
.notVerified
, мы сможем его верифицировать:let verifiedUser = try await UsersManager.acceptAccountChanges(
user: user
)
.active
, с ним можно работать дальше.Привязка устройства с использованием QR-кода
let createdUser = try await UsersManager.createUserWithInitQR(
name: name, // Имя для сохранения учетной записи
pushNotificationsData: data, // Данные для отправки пуш-уведомлений
deviceName: deviceName, // Отображаемое дружественное имя устройства
externalId: externalId, // Внешний идентификатор
alias: alias, // Человекочитаемый идентификатор устройства
requirePassword: true // Требуется ли установка пароля
)
.active
, с ним можно работать дальше.Привязать к другому устройству
// Идентификатор подтвержденного пользователя, к которому привязываем новое устройство
let uid = user.UserID
let createdUser = try await UsersManager.createUserWithApproval(
serviceURL: serverURL, // Адрес сервера СЭП
uid: uid, // Идентификатор пользователя, к которому привязываем устройство
name: name, // Имя для сохранения учетной записи
pushNotificationsData: data, // Данные для отправки пуш-уведомлений
deviceName: deviceName, // Отображаемое дружественное имя устройства
externalID: externalID, // Внешний идентификатор
alias: alias, // Человекочитаемый идентификатор устройства
requirePassword: true // Требуется ли установка пароля
)
.approveRequired
, требуется подтверждение с основного устройства. На экране отобразится QR-код, который нужно отсканировать основным устройством.processAwaitingDevice
:try await DevicesManager.processAwaitingDevice(
user: user // Пользователь к которому привязываем устройство
)
try await UsersManager.checkApprovalStatus(
user: newUser
)
.active
и с ним можно работать на новом устройстве.Сценарии УНЭП
UsersManagerNonQual
.Зарегистрировать онлайн
createUser
:let createdUser = try await UsersManagerNonQual.createUser(
serviceURL: serverURL, // Адрес сервера СЭП
pushNotificationsData: data, // Данные для отправки пуш-уведомлений
deviceName: deviceName, // Отображаемое дружественное имя устройства
externalId: externalId, // Внешний идентификатор
alias: alias, // Человекочитаемый идентификатор устройства
)
User
в статусе created
. Для дальнейшей работы его нужно сохранить в хранилище.
let storedUser = try await UsersManagerNonQual.store(
user: user, // Новый пользователь
name: name, // Имя для хранения
password: password // Пароль для хранения
)
.installed
. Далее, необходимо дождаться от сервера смены статуса на .notVerified
. Для проверки статуса нужно вызвать UsersManager.updateStatus
:// Обновляем сведения о пользователе
let updatedUser = UsersManager.updateStatus(
user: user
)
if updatedUser.state == .notVerified {
...
}
.notVerified
необходимо вызвать метод UsersManagerNonQual.acceptAccountChanges
. Данный метод требует, чтобы перед выполнением user.isReadyToSign == true
.user.verification == .qrRequired
. Его необходимо отсканировать и распарсить заранее, а содержимое передать в параметре verificationQRValue
.User.profile
.if !user.isReadyToSign {
// Предъявляем пароль
try UsersManagerNonQual.submitPassword(user: user, password: password)
}
if user.verification == .qrRequired {
// Парсим QR-код
let qrValue = ...
// Проверяем
let verificationQRCode: QRCodeVerification = try CKey.analyzeQR(qrValue)
}
// Присоединяем к УЗ
let acceptedUser = try await UsersManagerNonQual.acceptAccountChanges(
user: user, // Пользователь
verificationQRCode: verificationQRCode // QR-код
)
Привязка устройства с использованием QR-кода
UsersManagerNonQual.createUserWithInitQR
.PolicyManager.getPolicy
, в переменной Policy.activationCodeLength
.
// Парсим QR-код
let registrationQRCode: QRCodeKinit = try CKey.analyzeQR(qrValue)
// Проверяем активацию
if !qrCode.isActivated {
let activatedQRCode = try CKey.activate(qrCodeKinit: qrCode, code: activationCode)
}
// Регистрируем
let createdUser = try await UsersManagerNonQual.createUserWithInitQR(
qrCode: qrCode, // QR-код
pushNotificationsData: pushNotificationsData, // Данные для отправки пуш-уведомлений
deviceName: deviceName, // Отображаемое дружественное имя устройства
externalId: externalId, // Внешний идентификатор
alias: alias, // Человекочитаемый идентификатор устройства
)
User
в статусе created
. Для дальнейшей работы его нужно сохранить в хранилище.let storedUser = try await UsersManagerNonQual.store(
user: user, // Новый пользователь
name: name, // Имя для хранения
password: password // Пароль для хранения
)
Привязать к другому устройству
uid
и serviceURL
уже существующего на другом устройствке пользователя. На новом устройстве нужно вызвать метод UsersManagerNonQual.createUserWithApproval
с этими данными:let createdUser = try await UsersManagerNonQual.createUserWithApproval(
serviceURL: serviceURL, // Адрес для взаимодействия существующего пользователя
uid: userId, // UserID существующего пользователя
pushNotificationsData: pushNotificationsData, // Данные для отправки пуш-уведомлений
deviceName: deviceName, // Отображаемое дружественное имя устройства
externalId: externalId, // Внешний идентификатор
alias: alias, // Человекочитаемый идентификатор устройства
)
User
в статусе created
. Для дальнейшей работы его нужно сохранить в хранилище.let storedUser = try await UsersManagerNonQual.store(
user: user, // Новый пользователь
name: name, // Имя для хранения
password: password // Пароль для хранения
)
.approveRequired
— присоединение данного пользователя нужно подтвердить или отклонить на основном устройстве. Для этого нужно вызвать DevicesManagerNonQual.approve
или DevicesManagerNonQual.reject
.unapprovedUser.qrCode
. В сценарии УКЭП на новом устройстве отображается этот QR-код, старое устройство его сканирует и отображает содержимое на экране.// Получаем список устройств
let devices = try await DevicesManager.listDevices(
user: user // Основной активный пользователь
)
// Находим неподтвержденное устройство
let notApprovedDevice = devices.first(where { $0.state == .approveRequired })
if !user.isReadyToSign {
// Предъявляем пароль
try UsersManagerNonQual.submitPassword(user: user, password: password)
}
// Подтверждаем присоединение
try await DevicesManagerNonQual.approve(
device: notApprovedDevice, // Устройство, ожидающее подтверждения
user: user // Основной активный пользователь
)
// ... или отклоняем
try await DevicesManagerNonQual.reject(
device: notApprovedDevice, // Устройство, ожидающее подтверждения
user: user // Основной активный пользователь
)
do {
let approvedUser = try await UsersManagerNonQual.checkApprovalStatus(
user: userToCheck // Новый пользователь
)
} catch SDKError.approveRequired {
... // По-прежнему ожидаем подтверждение на основном устройстве
} catch {
...
}
Устройства и пользователи
UsersManager
и UsersManagerNonQual
.DevicesManager
и DevicesManagerNonQual
.UsersManager.users
.DevicesManager.listDevices
:let devices = try await DevicesManager.listDevices(user: user)
Прикрепление пользователя к устройству
DevicesManager.processAwaitingDevice
. Пример использования:
// Проходим регистрацию на новом устройстве
let approvedUser = try await UsersManager.createUserWithApproval(
serviceURL: serverURL, // Адрес сервера СЭП
uid: mainDeviceUserId, // Идентификатор пользователя с основного устройства
name: name, // Имя для сохранения учетной записи
pushNotificationsData: data, // Данные для отправки пуш-уведомлений
deviceName: deviceName, // Отображаемое дружественное имя устройства
externalId: externalId, // Внешний идентификатор
alias: alias, // Человекочитаемый идентификатор устройства
requirePassword: requirePwd, // Требуется ли установка пароля
statusCheckingInterval: interval // Интервал проверки присоединения устройства
)
// На основном устройстве потребуется подтвердить присоединение нового
try await DevicesManager.processAwaitingDevice(
user: user // Пользователь, к которому привязывается новое устройство
)
Операции и документы
Получение списка операций
let operations = try await OperationsManager.getOperationsList(
user: user, // Пользователь, для которого проверяются операции
operationType: nil, // Фильтр операций по типу
operationID: nil // Фильтр по идентификатору
)
Подтверждение операции
let approveRequest = try await OperationsManager.confirmOperation(
operation: operation, // Операция для подтверждения
user: user, // Пользователь, подтверждающий операцию
signMode: .online // Способ подтверждения. В данном случае — online
)
Загрузка документов
let uploadedDocumentId = try await OperationsManager.uploadDocument(
documentContent: documentData, // Бинарные данные документа
title: documentTitle, // Заголовок документа
snippetTemplate: documentSnippet, // HTML-сниппет документа
previewTemplate: documentPreview, // HTML-превью документа
user: user // Пользователь-владелец документа
)
Подпись документов
// Формируем параметры подписания
let templateId = signServerParams.processingTemplates.first.id
let certificateId = certificate.CertificateId
let signParams = SignParams(
signTemplateId: templateId, // Идентификатор шаблона подписи
certId: certificateId, // Идентификатор сертификата, которым будем подписывать
pinCode: "" // Пин-код от сертификата. Если пин-код не установлен, то укажите пустую строку
)
// Подписываем и получаем результаты подписания
let signingResults = try await OperationsManager.signDocuments(
documentsIDs: confirmedDocuments, // Документы для подписания
user: user, // Пользователь, подписывающий документы
signParams: signParams // Параметры подписания
)
Дополнительная информация
Параметры подписания
// Получаем параметры сервера подписания
let signServerParams = try await PolicyManager.getSignServerParams(
user: user // Пользователь, запрашивающий параметры сервера подписания
)
...
// Получаем сертификат
let certificates = try await CertificatesManager.listCertificates(
user: user // Пользователь, запрашивающий сертификаты
)
...
// Выбираем сертификат, например, первый в списке. Запросы на сертификат игнорируем
let certificate: Certificate = certificates.first(where: {
$0.type == .certificate
})
Резервное копирование
recoveryPassword
.Резервная копия профиля пользователя
// Создание резервной копии
let userBackupData: Data = try UsersManagerNonQual.createBackup(
user: user,
recoveryPassword: recoveryPassword
)
// Восстановление
let restoredUser: User = try UsersManagerNonQual.restoreFromBackup(
backupData: userBackupData,
recoveryPassword: recoveryPassword
)
UsersManagerNonQual.store(
user: restoredUser,
name: username,
password: password
) { storingResult in
switch result {
case let .success(storedUser): // Сохраненный пользователь
case let .failure(error): ...
}
}
userBackupData
после создания находится в памяти. Необходимо самостоятельно сохранить ее на устройство любым возможным способом.Резервая копия ключей подписи на устройстве
// Создание резервной копии
let keyBackupData: Data = try KeysManagerNonQual.createBackup(
keyInfo: keyInfo,
recoveryPassword: recoveryPassword
)
// Восстановление
let restoredKeyInfo: KeyInfo = try KeysManagerNonQual.restoreFromBackup(
backupData: keyBackupData,
recoveryPassword: recoveryPassword
)
keyBackupData
после создания находится в памяти. Необходимо самостоятельно сохранить ее на устройство любым возможным способом.Архивирование ключей подписи на сервере СЭП
isExportable
могут быть архивированы на сервере СЭП.
// При подписи сертификата
try await CertificatesManagerNonQual.sign(
certificateRequest: request,
user: user,
isExportable: true
)
// При создании ключей
let keyInfo = try KeysManagerNonQual.createKeyPair(
for: user,
pin: pin,
isExportable: true
)
CertificatesManagerNonQual.exportPfx(...)
. Метод требует ПИН-код от контейнера с ключевой информацией. Если передать nil
, то будет использован пин-код по умолчанию. User
, привязанный к той же учетной записи.
// Экспорт
// Получаем список сертификатов
let certificates = try await CertificatesManager.listCertificates(
user: user)
// // Выбираем сертификат для экспорта
let certificateToExport = certificatesList.first {
$0.type == .certificate &&
!$0.isArchived &&
CertificatesManagerNonQual.checkIfAccessibleOnThisDevice(certificate: $0, for: user)
}
// Выгружаем архив
try await CertificatesManagerNonQual.exportPfx(
user: user,
certificate: certificateToExport, // Сертификат, ключи которого архивируем
pin: pin, // Пин-код от контейнера ключей
pfxPin: nil // Дополнительный пин-код, на котором будет зашифрован архив (опционально)
)
...
// Импорт
// Получаем список сертификатов
let certificates = try await CertificatesManager.listCertificates(
user: user)
// Находим доступный для импорта сертификат
let archivedCertificate = certificatesList.first {
$0.type == .certificate &&
$0.isArchived
}
// Импортируем. После импорта на устройство будут загружены ключи
try await CertificatesManagerNonQual.importPfx(
user: user,
certificate: archivedCertificate,
pin: newPin, // Новый пин-код для контейнера ключей
pfxPin: nil // Пин-код для расшифрования архива
)
CertificatesManagerNonQual.removePfx(user:certificate:)
. Обновление профиля устройства
UsersManager.renew(user:callback:)
. После использования метода старый User
будет заменен новым.let userToRenew: User = ... // Пользователь, которого хотим обновить
let renewedUser = try await UsersManagerNonQual.renew(user: userToRenew)
Работа с NFC картами и токенами Рутокен
CKeySDK_NFC
Установка сборки с NFC
Swift Package Manager
CKey_NFC
из репозитория:https://.../ckey-nfc.git
CocoaPods
Podfile
зависимости:pod 'CKey_NFC', :git => 'https://.../ckey-nfc.git'
Ручная установка
Ресурсы для NFC
Настройка проекта
Импорт сертификата с карты
[Result<Certificate, Error>]
. Считывание данных сертификата — отдельная операция. Поэтому возвращаются результаты всех считываний по-отдельности.CertificateManager.setCertificate(user:certificate:)
.// Перечисление доступных сертификатов на карте
let certificatesResults = try await CertificatesManager.listExternalCertificates()
let certificateToImport = ... // Выбираем сертификат для импорта
// Загружаем сертификат на сервер
try await CertificatesManager.setCertificate(
user: user,
certificate: certificateToImport
)
Создание ключей на карте
CertificatesManagerNonQual.signCertificateRequest
. Место для создания ключей выбирается параметром keysSource
. По-умолчанию выбрано KeysSourceIdentifier.localGeneric
— ключи создаются на устройстве. Для NFC-токенов необходимо выбрать KeysSourceIdentifier.rutokenNFC
.CertificatesManager.listCertificates(user:callback:)
.CertificatesManager.install
.
// Подписываем запрос на сертификат
try await CertificatesManagerNonQual.sign(
certificateRequest: certificateRequest,
user: user,
keysSource: .rutokenNFC // Используем NFC-токен Рутокен
)
// Получаем список сертификатов
let certificates = CertificatesManager.listCertificates(...)
// Проверяем, выпущен ли сертификат
let certificateToInstall: Certificate = certificates.first {
// Сертификат активный
$0.state == .active &&
// Ключи хранятся на клиенте
$0.isClient &&
// Ключи хранятся на текущем устройстве
CertificatesManagerNonQual.checkIfAccessibleOnThisDevice(certificate: $0, for: user) &&
// Сертификт не установлен
!CertificatesManagerNonQual.checkIfInstalled(certificate: $0, for: user)
}
// Устанавливаем выпущеный сертификат
try await CertificatesManagerNonQual.install(
certificate: certificateToInstall,
user: user
)
Логирование
LoggerProtocol
.public protocol LoggerProtocol {
func debug(_ message: String, category: LoggingCategory)
func error(_ message: String, category: LoggingCategory)
func sensitive(_ message: String, category: LoggingCategory)
}
/// Пример логгера
struct MyLogger: LoggerProtocol {
func debug(_ message: String, category: LoggingCategory) {
...
}
func error(_ message: String, category: LoggingCategory) {
...
}
func sensitive(_ message: String, category: LoggingCategory) {
...
}
}
CKey.setLogger(...)
:let myLogger = MyLogger()
// CKey.setLogLevels([.debug, .keys]) <- Теперь можно убрать из кода приложения
CKey.setLogger(myLogger, options: [.debug, .sensitive])
Сохранение логов в файл
import CocoaLumberjack
import CocoaLumberjackSwift
...
// Настраиваем CocoaLumberjack
let fileLogger = DDFileLogger()
fileLogger.logFileManager.maximumNumberOfLogFiles = 2 // Храним только 2 файла
fileLogger.maximumFileSize = 256 * 1024 // не более 256kb каждый
fileLogger.rollingFrequency = 60 * 60 * 24 // с сообщениями за последние 24 часа
DDLog.add(fileLogger)
...
// Описываем методы логгера
final class MyLogger: LoggerProtocol {
func debug(_ message: String, category: LoggingCategory) {
DDLogInfo("[SDK][\(category)] \(message)")
}
func error(_ message: String, category: LoggingCategory) {
DDLogError("[SDK][\(category)] \(message)")
}
func sensitive(_ message: String, category: LoggingCategory) {
#if DEBUG
DDLogInfo("[SDK][Sensitive][\(category)] \(message)")
#endif
}
}
...
// Назначаем логгер
CKey.setLogger(MyLogger(), options: [.debug, .sensitive])
fileLogger
.logFileManager
.sortedLogFileInfos
.compactMap { (info: DDInfo) -> Data in
FileManager.default.contents(atPath: info.filePath) // Читаем логи из файла
}
.forEach { (logData: Data) in
... // Обрабатываем логи каждого файла
}
Оформление интерфейса
*.xib
и ассеты CKeySDK_Assets.xcassets
. Префикс ассетов может отличаться в зависимости от версии сдк — CKeySDK
или CKeySDK_NFC
.Xib файлы
Xib
Описание
CKeySDK_MainNavigationController.xib
Контроллер навигации всех экранов
CKeySDK_MainNavigationController.xib
Контроллер навигации всех экранов
CKeySDK_CameraNavigationController.xib
Контроллер навигации экрана с камерой
CKeySDK_OverlayViewController.xib
Оверлей с индикатором активности. Закрывает экран приложения при взаимодействии с фреймворком.
CKeySDK_CameraViewController.xib
Экран с камерой
CKeySDK_ApprovingQRViewController.xib
Экран с QR-кодом для присоединения другого устройства
CKeySDK_PasswordViewController.xib
Экран с вводом и созданием ПИН-кода
CKeySDK_BiometrySuggestionViewController.xib
Экран с предложением использовать биометрию
CKeySDK_DocumentsListViewController.xib
Экран со списком документов
CKeySDK_DocumentPreviewCell.xib
Ячейка с превью документа
CKeySDK_PDFViewController.xib
Просмотр PDF документа
CKeySDK_AcceptAccountActionsViewController.xib
Действия при подтверждении присоединения к УЗ
CKeySDK_ProcessDeviceActionsViewController.xib
Действия при подтверждении присоединения к другому устройству
CKeySDK_DocumentsActionsViewController.xib
Действия при подписании документов
CKeySDK_OperationActionsViewController.xib
Действия при работе с операцией с документами
CKeySDK_InfoViewController.xib
Общие экран с таблицей значений
CKeySDK_KeyValueCell.xib
Ячейка с заголовком и значением
RndmBioViewController.xib
Генератор случайных чисел CPROCSP
RndmBioViewControllerIPhone.xib
Генератор случайных чисел CPROCSP для iPhone
Ассеты
CKeySDK_Assets.xcassets
лежат цвета и изображения по-умолчанию.Совместимость с Appearance
Custom
. Они применяют к элементам в xib'ах стили, описаные в CKey.appearance.*
:
UIView
, UILabel
, ...), то стили Appearance
применяться не будут. Работа с распространенными ошибками
SDKError
SDKError.networkError
, необходимо смотреть на параметр code
:12002
12007
12175
root-certs/prod
с корневыми сертификатами сервера.cer
, а не .crt
. Либо они не в формате PEM.Работа с UI
SDKError.canceled
— она означает, что пользователь закрыл UI.ServerError
.userNotFound
: Пользователь был удален на сервере.deviceBlocked
: Устройство заблокировано на сервере.keyExpiredOrNotYetValid
: Срок действия ключей истек еще не наступилUsersManager.updateStatus
мы не получим User
со статусом .expired
, а метод вернет ошибку ServerError.keyExpiredOrNotYetValid