eID Android SDK — руководство по интеграции¶
Руководство разработчика по добавлению в Android-приложение регистрации (enroll), аутентификации
и электронной подписи eID (Gerege Smart-ID). SDK общается с Go-бэкендом (server/) и выполняет
распределённую генерацию ключей и пороговую ECDSA 2-of-2 на стороне телефона. Его
криптографическое ядро — самостоятельная реализация на Kotlin, побайтово совместимая с
internal/crypto Go-сервера (проверено golden-векторами).
Пакет:
mn.eidmongolia.smartid· Класс клиента:GeregeSmartIdClient· Модуль::android-sdk(Android-библиотека,.aar).
1. Обзор¶
SDK предоставляет следующие высокоуровневые операции через единый GeregeSmartIdClient:
| Действие | Функция | Описание |
|---|---|---|
| Регистрация | enroll(...) |
Согласие KYC + распределённая генерация ключей → 2 сертификата (подписание PIN2, аутентификация PIN1) |
| Подтверждение сессии | approve(...) |
Подписать и подтвердить пришедшую по push/QR сессию пороговой ECDSA |
| Проверка PIN | verifyPIN(...) / pinUnlocksSlot(...) |
Разблокировать и проверить сохранённую идентичность PIN-кодом (без обращения к серверу) |
| Смена PIN | changePIN(...) |
Разблокировать старым PIN и перешифровать новым |
| KYC | danInit/danStatus/danLiveness, gsignInit/gsignVerify, passportInit |
Верификация DAN / G-Sign / иностранного паспорта |
| Панель | pendingSign/pendingSession/recentActivity/sessionInfo |
Ожидающие запросы + недавняя активность |
2 сертификата на гражданина. Enroll выполняет генерацию ключей ДВАЖДЫ: аутентификация (PIN1, вход) и подписание (PIN2, юридически значимая подпись). Закрытый ключ никогда не существует целиком в одном месте — одна половина на телефоне, другая на сервере.
2. Требования¶
- minSdk 24, targetSdk 35, JDK 17, Kotlin 1.9.24 (сборка SDK:
compileSdk 34). - Аттестация Play Integrity — для каждого запроса получается свежий токен на nonce сервера.
applicationIdприложения и проект Google Cloud (cloudProjectNumber) должны быть зарегистрированы на сервере. - URL бэкенда — по умолчанию
https://rp-api.eidmongolia.mn(настраивается, см. ниже).
(!) Play Integrity не работает на эмуляторе. Запускайте Go-сервер с
SMARTID_REQUIRE_ATTESTATION=false(значение по умолчанию для dev) — приложение продолжит работу без аттестации. На эмуляторе или в среде без Play Services SDK корректно перехватываетAttestationUnavailableExceptionи продолжает без аттестации; на реальном устройстве ошибка аттестации (вмешательство/сеть) обрабатывается по принципу fail-closed (запрос отменяется).
3. Установка¶
SDK подключается как Gradle-модуль :android-sdk (не публикуется в Maven — используется как
соседний модуль внутри репозитория либо как .aar). Пример приложения подключает его как соседний
модуль в settings.gradle.kts:
// eIDMongolia/settings.gradle.kts
include(":app")
include(":android-sdk")
project(":android-sdk").projectDir = file("../android-sdk") // android/android-sdk
Сам SDK зависит от следующего (подтягивается транзитивно): com.google.android.play:integrity,
androidx.security:security-crypto (EncryptedSharedPreferences), com.squareup.okhttp3:okhttp,
kotlinx-coroutines-android + -play-services.
Биометрия (обязательна на стороне приложения). Для авторизации ключа Keystore с требованием
аутентификации приложению нужен BiometricPrompt (FragmentActivity), поэтому добавьте
биометрические зависимости в своё приложение:
// app/build.gradle.kts
implementation("androidx.fragment:fragment-ktx:1.8.2")
implementation("androidx.biometric:biometric:1.1.0") // BiometricPrompt требует FragmentActivity
MainActivityприложения должна бытьFragmentActivity(илиAppCompatActivity) — этого требует BiometricPrompt.
4. Конфигурация — URL бэкенда¶
Приложение зашивает базовый URL бэкенда в BuildConfig.GEREGE_BACKEND_URL на этапе сборки.
Значение берётся из gradle-свойства gerege.backendUrl, а при пустом значении используется значение
по умолчанию:
// app/build.gradle.kts (defaultConfig)
val backendUrl = (project.findProperty("gerege.backendUrl") as String?)
?.takeIf { it.isNotBlank() } ?: "https://rp-api.eidmongolia.mn"
buildConfigField("String", "GEREGE_BACKEND_URL", "\"$backendUrl\"")
Два способа настройки:
# 1) Свойство в командной строке:
./gradlew :app:assembleDebug -Pgerege.backendUrl=https://<go-backend>
# 2) либо в eIDMongolia/gradle.properties:
# gerege.backendUrl=https://smartid-staging.example.mn
В приложении значение читается через AppConfig:
object AppConfig {
val backendUrl: String get() = BuildConfig.GEREGE_BACKEND_URL
const val cloudProjectNumber: Long = 399766783708L // проект Google Cloud (Play Integrity)
}
Для подключения к локальному Go-серверу (http) требуется разрешение cleartext в
network_security_config(тестирование в локальной сети). На публичном хосте SDK делает TLS-pinning к корню Let's Encrypt; для localhost/.local/приватных IP pinning пропускается автоматически.
5. Базовое использование¶
5.1 Создание клиента¶
val client = GeregeSmartIdClient(
context = this, // Context (Activity/Application)
baseUrl = AppConfig.backendUrl, // BuildConfig.GEREGE_BACKEND_URL
cloudProjectNumber = AppConfig.cloudProjectNumber, // Play Integrity
account = "default", // (необязательно) имя слота в keystore
)
5.2 Биометрический аутентификатор (обязателен)¶
Операции загрузки, такие как approve / verifyPIN / changePIN, требуют
BiometricAuthenticator, чтобы разблокировать ключ Keystore с требованием аутентификации
пользователя. SDK не зависит от UI-фреймворка — приложение связывает его через BiometricPrompt.
Для Compose есть вспомогательная функция:
Контракт BiometricAuthenticator (интерфейс SDK):
interface BiometricAuthenticator {
suspend fun authenticate(cipher: Cipher, reason: String): Cipher
}
Отмена пользователем / сбой биометрии → исключение (fail-closed): разворачивание ключа и подписание прекращаются, тихого обхода нет (паритет с
.userPresenceSecure Enclave на iOS).
5.3 Регистрация (enroll)¶
enroll выполняет распределённую генерацию ключей с согласием KYC (danAuthCode) и PIN, получает
сертификат и сохраняет его на телефоне. Если передан authPin, создаются два отдельных пороговых
ключа для конфигурации 2-cert (подписание + аутентификация):
suspend fun enroll(
danAuthCode: String,
pin: CharArray, // PIN2 — подписание (неотказуемость)
pushToken: String, // токен FCM
authPin: CharArray? = null, // PIN1 — аутентификация (если передан — 2-cert)
reviewLatin: (suspend (LatinNameProposal) -> Pair<String, String>?)? = null,
progress: ProgressHandler? = null,
): String // → documentNumber
val documentNumber = client.enroll(
danAuthCode = danCode,
pin = "1234".toCharArray(), // PIN2 (подписание)
pushToken = fcmToken,
authPin = "5678".toCharArray(), // PIN1 (аутентификация)
)
Внутри enroll выполняет проверку доказательства генерации ключа сервером (защита от rogue-key) и key-binding (сертификат ↔ совместный Q = qClient+qServer) — если сервер вернул сертификат для другого ключа, регистрация отклоняется до сохранения.
Прогрев (необязательно). Вызовите
client.prewarmEnroll()при открытии экрана регистрации, чтобы заранее выполнить тяжёлую генерацию ключей Paillier в фоне и сократить ожидание при enroll.
5.4 Аутентификация и подпись (approve)¶
Подтвердите sessionId, пришедший по push/QR, с помощью PIN. Если authentication = true,
используется ключ auth (PIN1, вход); иначе — ключ sign (PIN2, подпись):
suspend fun approve(
sessionId: String,
pin: CharArray,
auth: BiometricAuthenticator,
authentication: Boolean = false, // true → PIN1/auth; false → PIN2/sign
confirmVc: String? = null, // подтверждённый пользователем код (WYSIWYS)
progress: ProgressHandler? = null,
): String // → статус ("OK" и т. п.)
// Аутентификация (вход):
val status = client.approve(sessionId, "5678".toCharArray(), auth, authentication = true)
// Подпись (sign):
val status = client.approve(sessionId, "1234".toCharArray(), auth)
Внутри approve телефон выполняет 3 раунда пороговой ECDSA (commit → prove → finish) и перед
отправкой финальной подписи на сервер самостоятельно проверяет по ECDSA её соответствие сообщению
сессии (SIGN: дайджест; AUTH: ACSP_V2(rpChallenge, SPKI)) (WYSIWYS / привязка транскрипта).
Если соответствия нет, подпись не формируется.
5.5 Проверка / смена PIN¶
suspend fun verifyPIN(slot: PINSlot, pin: CharArray, auth: BiometricAuthenticator): Boolean
suspend fun changePIN(slot: PINSlot, oldPin: CharArray, newPin: CharArray, auth: BiometricAuthenticator)
// slot: GeregeSmartIdClient.PINSlot.AUTH (PIN1) | .SIGN (PIN2)
Проверка локального состояния (без сервера): hasRegistration(): Boolean, deleteRegistration().
5.6 KYC (кратко)¶
Перед получением danAuthCode, необходимого для enroll, выполните KYC через SDK:
val (state, verifyUrl) = client.danInit(registrationNumber) // URL верификации DAN
val verified = client.danStatus(state) // опрос
val (passed, score) = client.danLiveness(state, selfieBytes) // сопоставление лиц
Также доступны варианты gsignInit/gsignVerify (G-Sign) и passportInit (иностранный паспорт) —
см. исходный код.
6. Хранение ключей и биометрия¶
SDK шифрует идентичность (xClient + закрытый ключ Paillier + сертификат) через
PBKDF2(PIN)+AES-GCM, сохраняет её в EncryptedSharedPreferences и дополнительно оборачивает
ключом Android Keystore с требованием аутентификации пользователя (биометрия) — чтобы блокировать
офлайн-перебор PIN (паритет с Secure Enclave на iOS). PIN и x_client никогда не покидают
устройство и не отправляются на сервер.
Глубокие технические детали — хранение ключей, RSA-OAEP wrap/unwrap, миграция v1→v2,
setInvalidatedByBiometricEnrollment, fallback на StrongBox — описаны в отдельном документе: Биометрические ключи Android.
7. Сборка и запуск примера приложения¶
Требования: установленный Android SDK с настроенным ANDROID_HOME; для FCM-push — реальный
файл проекта Firebase app/google-services.json (пример: app/google-services.json.example).
cd android/eIDMongolia
# Для FCM-push (необязательно): замените на реальный файл Firebase
cp app/google-services.json.example app/google-services.json
# Debug APK — URL бэкенда передаётся свойством
./gradlew :app:assembleDebug -Pgerege.backendUrl=https://<go-backend>
# → app/build/outputs/apk/debug/app-debug.apk
Либо откройте и запустите eIDMongolia в Android Studio. Чтобы собрать библиотеку SDK отдельно:
cd android/android-sdk && gradle assembleRelease →
build/outputs/aar/...-release.aar.
Сборка не ломается при отсутствии
google-services.json— приложение компилируется и работает (FCM-push отключён; Play Integrity и enroll/approve работают без google-services.json).
8. Эндпойнты Go-бэкенда, используемые приложением¶
Все соответствуют internal/httpapi Go-сервера (как и на iOS):
GET /v3/attestation/challenge— nonce для аттестацииPOST /v3/enrollment/init·/v3/enrollment/completeGET /v3/mobile/session/{id}·/v3/mobile/pending/{doc}·/v3/mobile/activity/{doc}POST /v3/mobile/session/{id}/sign/{commit,prove,finish}— пороговая ECDSA 2-of-2 (3 раунда)- KYC:
/v3/kyc/dan/*,/v3/kyc/gsign/*
В ответе сервер возвращает заголовок X-Next-Attestation-Nonce (оптимизация кеша nonce в SDK).
9. Ссылки (исходники)¶
- Клиент Android SDK:
https://github.com/gerege-systems/eid-platform-mn/blob/main/android/android-sdk/src/main/kotlin/mn/eidmongolia/smartid/GeregeSmartIdClient.kt - Хранилище Keystore:
https://github.com/gerege-systems/eid-platform-mn/blob/main/android/android-sdk/src/main/kotlin/mn/eidmongolia/smartid/AndroidSecureKeyStore.kt - Интерфейс биометрического аутентификатора:
https://github.com/gerege-systems/eid-platform-mn/blob/main/android/android-sdk/src/main/kotlin/mn/eidmongolia/smartid/BiometricAuthenticator.kt - Точка входа примера приложения:
https://github.com/gerege-systems/eid-platform-mn/blob/main/android/eIDMongolia/app/src/main/kotlin/mn/eidmongolia/example/MainActivity.kt - Android README (сборка/запуск):
https://github.com/gerege-systems/eid-platform-mn/blob/main/android/README.md - README SDK (архитектура на Kotlin):
https://github.com/gerege-systems/eid-platform-mn/blob/main/android/android-sdk/README.md - Биометрические ключи (глубокая техническая часть): ANDROID_BIOMETRIC_KEYS.md