Перейти к содержанию

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
// app/build.gradle.kts
dependencies {
    implementation(project(":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 есть вспомогательная функция:

val auth = rememberBiometricAuthenticator()   // etalon/core/BiometricAuth.kt из eIDMongolia

Контракт BiometricAuthenticator (интерфейс SDK):

interface BiometricAuthenticator {
    suspend fun authenticate(cipher: Cipher, reason: String): Cipher
}

Отмена пользователем / сбой биометрии → исключение (fail-closed): разворачивание ключа и подписание прекращаются, тихого обхода нет (паритет с .userPresence Secure 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 assembleReleasebuild/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/complete
  • GET /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