Агуулгыг алгасах

eID Android SDK — интеграцийн гарын авлага

Android аппликейшнд eID (Gerege Smart-ID) бүртгэл (enroll), нэвтрэлт (authentication) ба цахим гарын үсэг (signature)-ийг нэмэх developer гарын авлага. SDK нь Go backend (server/)-тэй харьцаж, распределённый keygen + 2-of-2 threshold ECDSA-г утасны талд гүйцэтгэнэ. Крипто цөм нь Kotlin-д бие даасан хэрэгжүүлэлт бөгөөд Go серверийн internal/crypto-той golden-vector-аар байт-нийцтэй.

Package: mn.eidmongolia.smartid · Client class: GeregeSmartIdClient · Module: :android-sdk (Android library, .aar).

1. Товч танилцуулга

SDK нь дараах өндөр түвшний үйлдлүүдийг нэг GeregeSmartIdClient-ээр хийнэ:

Үйлдэл Функц Тайлбар
Бүртгэл enroll(...) KYC consent + distributed keygen → 2 сертификат (signing PIN2, authentication PIN1)
Session зөвшөөрөх approve(...) Push/QR-аас ирсэн session-ийг threshold ECDSA-аар зурж баталгаажуулах
PIN шалгах verifyPIN(...) / pinUnlocksSlot(...) Хадгалсан identity-г PIN-ээр тайлж шалгах (сервер рүү хандахгүй)
PIN солих changePIN(...) Хуучин PIN-ээр тайлж, шинэ PIN-ээр дахин шифрлэх
KYC danInit/danStatus/danLiveness, gsignInit/gsignVerify, passportInit DAN / G-Sign / гадаад паспортын баталгаажуулалт
Dashboard pendingSign/pendingSession/recentActivity/sessionInfo Хүлээгдэж буй хүсэлт + сүүлийн үйл ажиллагаа

2 сертификат нэг иргэнд. Enroll нь keygen-ийг ХОЁР удаа ажиллуулна: Authentication (PIN1, нэвтрэлт) ба Signing (PIN2, хууль ёсны гарын үсэг). Хувийн түлхүүр хэзээ ч нэг дор бүрэн байдаггүй — нэг хувь утсанд, нэг хувь серверт.

2. Урьдчилсан нөхцөл

  • minSdk 24, targetSdk 35, JDK 17, Kotlin 1.9.24 (SDK build: compileSdk 34).
  • Play Integrity attestation — хүсэлт бүрд серверийн nonce дээр шинэ token авна. Апп-ийн applicationId ба Google Cloud project (cloudProjectNumber) нь серверт бүртгэлтэй байх ёстой.
  • Backend URL — default https://rp-api.eidmongolia.mn (config-оор солигдоно, доор үз).

(!) Эмулятор дээр Play Integrity ажиллахгүй. Go серверийг SMARTID_REQUIRE_ATTESTATION=false (dev default)-оор ажиллуул — апп attestation-гүйгээр үргэлжилнэ. Эмулятор / Play Services байхгүй орчинд SDK нь AttestationUnavailableException-ийг зөөлөн барьж attestation-гүй үргэлжилнэ; жинхэнэ төхөөрөмж дээрх attestation алдаа (tamper/network) нь fail-closed (хүсэлт цуцлагдана).

3. Суулгах

SDK нь :android-sdk Gradle module хэлбэрээр орно (Maven coordinate-аар нийтлэгдээгүй — repo дотор зэргэлдээ module эсвэл .aar-аар). Жишээ апп нь settings.gradle.kts-д зэргэлдээ module болгож оруулдаг:

// 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 өөрөө дараах хамааралтай (transitively орж ирнэ): com.google.android.play:integrity, androidx.security:security-crypto (EncryptedSharedPreferences), com.squareup.okhttp3:okhttp, kotlinx-coroutines-android + -play-services.

Биометр (заавал апп талд). Keystore auth-required түлхүүрийг зөвшөөрүүлэхэд апп нь BiometricPrompt (FragmentActivity) хэрэгтэй тул апп-даа биометр dependency нэмнэ:

// 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. Тохиргоо — backend URL

Апп нь backend суурь URL-ийг build үед BuildConfig.GEREGE_BACKEND_URL-д шигтгэнэ. Утгыг gerege.backendUrl gradle property-аас авч, хоосон бол default руу унана:

// 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) Command line property:
./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 project (Play Integrity)
}

Локал Go сервер (http) руу холбогдвол network_security_config-д cleartext зөвшөөрөл хэрэгтэй (LAN тест). Нийтийн host дээр SDK нь TLS-ийг Let's Encrypt root-д пинлэдэг; localhost/.local/ private-IP дээр пиннинг автоматаар алгасна.

5. Үндсэн ашиглалт

5.1 Client үүсгэх

val client = GeregeSmartIdClient(
    context = this,                            // Context (Activity/Application)
    baseUrl = AppConfig.backendUrl,            // BuildConfig.GEREGE_BACKEND_URL
    cloudProjectNumber = AppConfig.cloudProjectNumber,  // Play Integrity
    account = "default",                       // (сонголт) keystore slot нэр
)

5.2 Биометр authenticator (заавал)

approve / verifyPIN / changePIN зэрэг load-хийдэг үйлдлүүд нь Keystore-ийн user-authentication-required түлхүүрийг задлахад BiometricAuthenticator-ийг шаарддаг. SDK нь UI framework-аас хамааралгүй — апп нь BiometricPrompt-оор гүүр өгнө. Compose-д helper байдаг:

val auth = rememberBiometricAuthenticator()   // eIDMongolia-ийн etalon/core/BiometricAuth.kt

BiometricAuthenticator нь дараах гэрээтэй (SDK интерфейс):

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

Хэрэглэгч цуцлах / биометр амжилтгүй → throw (fail-closed): unwrap/signing зогсоно, чимээгүй bypass байхгүй (iOS Secure Enclave .userPresence-тэй parity).

5.3 Бүртгэл (enroll)

enroll нь KYC consent (danAuthCode) + PIN-ээр distributed keygen хийж, сертификат авч утсанд хадгална. authPin өгвөл 2-cert (signing + authentication) хоёр тусдаа threshold түлхүүр үүснэ:

suspend fun enroll(
    danAuthCode: String,
    pin: CharArray,                        // PIN2 — signing (non-repudiation)
    pushToken: String,                     // FCM token
    authPin: CharArray? = null,            // PIN1 — authentication (өгвөл 2-cert)
    reviewLatin: (suspend (LatinNameProposal) -> Pair<String, String>?)? = null,
    progress: ProgressHandler? = null,
): String   // → documentNumber
val documentNumber = client.enroll(
    danAuthCode = danCode,
    pin = "1234".toCharArray(),            // PIN2 (signing)
    pushToken = fcmToken,
    authPin = "5678".toCharArray(),        // PIN1 (authentication)
)

Enroll нь дотроо серверийн keygen proof (rogue-key хамгаалалт) ба key-binding (гэрчилгээ ↔ joint Q = qClient+qServer) шалгалтыг хийдэг — сервер өөр түлхүүрт cert буцаавал хадгалахаас өмнө татгалзана.

Prewarm (сонголт). Бүртгэлийн дэлгэц нээгдэхэд client.prewarmEnroll()-ийг дуудвал хүнд Paillier keygen-ийг background-д урьдчилан хийж, enroll-ийн хүлээлтийг багасгана.

5.4 Нэвтрэлт ба гарын үсэг (approve)

Push/QR-аас ирсэн sessionId-г 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,             // хэрэглэгчийн баталсан verification code (WYSIWYS)
    progress: ProgressHandler? = null,
): String   // → status ("OK" гэх мэт)
// Нэвтрэлт (login):
val status = client.approve(sessionId, "5678".toCharArray(), auth, authentication = true)

// Гарын үсэг (sign):
val status = client.approve(sessionId, "1234".toCharArray(), auth)

approve дотор utас threshold ECDSA-ийн 3 round (commit → prove → finish)-ийг гүйцэтгэж, эцсийн гарын үсгийг серверт илгээхээсээ өмнө session-ий мессежтэй (SIGN: digest; AUTH: ACSP_V2(rpChallenge, SPKI)) таарч буйг бие даан ECDSA-verify хийдэг (WYSIWYS / transcript binding). Таарахгүй бол гарын үсэг гарахгүй.

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 (богино)

Enroll-д хэрэгтэй danAuthCode-ийг авахын өмнө KYC-г SDK-аар гүйцэтгэнэ:

val (state, verifyUrl) = client.danInit(registrationNumber)   // DAN verify URL
val verified = client.danStatus(state)                        // polling
val (passed, score) = client.danLiveness(state, selfieBytes)  // face-match

Мөн gsignInit/gsignVerify (G-Sign), passportInit (гадаад паспорт) хувилбарууд байна — эх кодыг үз.

6. Түлхүүр хадгалалт ба биометр

SDK нь identity (xClient + Paillier private key + cert)-ийг PBKDF2(PIN)+AES-GCM-ээр шифрлэж EncryptedSharedPreferences-д хадгалаад, дээр нь Android Keystore-ийн user-authentication-required (биометр) түлхүүрээр давхар wrap хийдэг (offline PIN brute-force-ийг хаах, iOS Secure Enclave parity). PIN болон x_client сервер рүү хэзээ ч очдоггүй.

Түлхүүр хадгалалт, RSA-OAEP wrap/unwrap, v1→v2 migration, setInvalidatedByBiometricEnrollment, StrongBox fallback зэрэг гүн техник дэлгэрэнгүйг тусдаа баримтад бичсэн: Android biometric-gated keys.

7. Example app build / ажиллуулах

Урьдчилсан нөхцөл: ANDROID_HOME тохируулсан Android SDK; 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 — backend URL-ийг property-оор дамжуулна
./gradlew :app:assembleDebug -Pgerege.backendUrl=https://<go-backend>
# → app/build/outputs/apk/debug/app-debug.apk

Эсвэл Android Studio-д eIDMongolia-ийг нээж ажиллуулна. SDK library-г тусад нь угсрах: cd android/android-sdk && gradle assembleReleasebuild/outputs/aar/...-release.aar.

google-services.json байхгүй үед build тасрахгүй — апп компайл болж ажиллана (FCM push идэвхгүй; Play Integrity + enroll/approve нь google-services.json-гүйгээр ажиллана).

8. Апп ашигладаг Go backend endpoint-ууд

Бүгд Go серверийн internal/httpapi-д таарна (iOS-той ижил):

  • GET /v3/attestation/challenge — attestation 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} — 2-of-2 threshold ECDSA (3 round)
  • KYC: /v3/kyc/dan/*, /v3/kyc/gsign/*

Сервер хариунд X-Next-Attestation-Nonce header өгдөг (SDK nonce-cache optimization).

9. Холбоос (эх сурвалж)

  • Android SDK client: 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
  • Biometric authenticator interface: https://github.com/gerege-systems/eid-platform-mn/blob/main/android/android-sdk/src/main/kotlin/mn/eidmongolia/smartid/BiometricAuthenticator.kt
  • Example app entry point: https://github.com/gerege-systems/eid-platform-mn/blob/main/android/eIDMongolia/app/src/main/kotlin/mn/eidmongolia/example/MainActivity.kt
  • Android README (build/run): https://github.com/gerege-systems/eid-platform-mn/blob/main/android/README.md
  • SDK README (Kotlin архитектур): https://github.com/gerege-systems/eid-platform-mn/blob/main/android/android-sdk/README.md
  • Биометр түлхүүр (гүн техник): ANDROID_BIOMETRIC_KEYS.md