eID Android SDK —— 集成指南¶
面向开发者的指南:如何在 Android 应用中加入 eID(Gerege Smart-ID)的注册(enroll)、
身份认证与电子签名功能。SDK 与 Go 后端(server/)通信,并在手机端执行分布式密钥生成与
2-of-2 门限 ECDSA。其密码学内核是独立的 Kotlin 实现,通过 golden 向量与 Go 服务端的
internal/crypto 保持逐字节兼容。
包名:
mn.eidmongolia.smartid· 客户端类:GeregeSmartIdClient· 模块::android-sdk(Android library,.aar)。
1. 概览¶
SDK 通过单一的 GeregeSmartIdClient 提供以下高层操作:
| 操作 | 函数 | 说明 |
|---|---|---|
| 注册 | enroll(...) |
KYC 授权 + 分布式密钥生成 → 2 份证书(签名 PIN2、身份认证 PIN1) |
| 批准会话 | approve(...) |
使用门限 ECDSA 对经推送 / 二维码到达的会话进行签名确认 |
| 校验 PIN | verifyPIN(...) / pinUnlocksSlot(...) |
用 PIN 解锁并校验已保存的身份(不访问服务器) |
| 修改 PIN | changePIN(...) |
用旧 PIN 解锁后以新 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 在模拟器上不可用。 请以
SMARTID_REQUIRE_ATTESTATION=false运行 Go 服务端(dev 默认值)—— 应用将在无认证的情况下继续运行。在模拟器 / 无 Play Services 的环境中,SDK 会优雅地捕获AttestationUnavailableException并跳过认证;在真机上, 认证错误(篡改 / 网络)按 fail-closed 处理(请求被取消)。
3. 安装¶
SDK 以 :android-sdk Gradle 模块形式引入(未发布为 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),需要在
network_security_config中允许明文流量 (局域网测试)。在公网主机上,SDK 会将 TLS 固定到 Let's Encrypt 根证书; 对 localhost/.local/私有 IP 会自动跳过固定。
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):解封与签名随即中止, 不存在静默绕过(与 iOS Secure Enclave 的
.userPresence保持一致)。
5.3 注册(enroll)¶
enroll 使用 KYC 授权(danAuthCode)+ PIN 执行分布式密钥生成,获取证书并保存在手机上。
若提供 authPin,则会创建两把独立的门限密钥以支持 双证书配置(签名 + 身份认证):
suspend fun enroll(
danAuthCode: String,
pin: CharArray, // PIN2 —— 签名(不可否认)
pushToken: String, // FCM 令牌
authPin: CharArray? = null, // PIN1 —— 身份认证(提供则为双证书)
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)与密钥绑定 (证书 ↔ 联合公钥 Q = qClient+qServer)—— 若服务端返回的是对应其他密钥的证书, 则在保存之前直接拒绝。
预热(可选)。 在打开注册页面时调用
client.prewarmEnroll(), 可提前在后台完成开销较大的 Paillier 密钥生成,从而缩短 enroll 的等待时间。
5.4 身份认证与签名(approve)¶
使用 PIN 批准经推送 / 二维码到达的 sessionId。若 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 / transcript 绑定)。若不匹配,则不产出签名。
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 之前,需通过 SDK 完成 KYC:
val (state, verifyUrl) = client.danInit(registrationNumber) // DAN 核验 URL
val verified = client.danStatus(state) // 轮询
val (passed, score) = client.danLiveness(state, selfieBytes) // 人脸比对
此外还有 gsignInit/gsignVerify(G-Sign)与 passportInit(外国护照)等变体 ——
详见源码。
6. 密钥存储与生物识别¶
SDK 使用 PBKDF2(PIN)+AES-GCM 加密身份数据(xClient + Paillier 私钥 + 证书),
保存在 EncryptedSharedPreferences 中,并额外用 Android Keystore 中
要求用户认证(生物识别)的密钥进行封装(以阻断离线 PIN 暴力破解,
与 iOS Secure Enclave 保持一致)。PIN 与 x_client 绝不会离开设备发送到服务器。
深入的技术细节 —— 密钥存储、RSA-OAEP 封装 / 解封、v1→v2 迁移、
setInvalidatedByBiometricEnrollment、StrongBox 回退 —— 请见单独文档: Android 生物识别密钥。
7. 构建 / 运行示例应用¶
前置条件:已安装 Android SDK 并配置 ANDROID_HOME;如需 FCM 推送,还需真实的 Firebase 项目文件
app/google-services.json(示例文件为 app/google-services.json.example)。
cd android/eIDMongolia
# 如需 FCM 推送(可选):替换为真实的 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
或在 Android Studio 中打开并运行 eIDMongolia。若要单独构建 SDK 库:
cd android/android-sdk && gradle assembleRelease →
build/outputs/aar/...-release.aar。
缺少
google-services.json不会导致构建失败 —— 应用仍可编译并运行 (FCM 推送被禁用;Play Integrity 与 enroll/approve 无需 google-services.json 即可工作)。
8. 应用所使用的 Go 后端接口¶
均对应 Go 服务端的 internal/httpapi(与 iOS 相同):
GET /v3/attestation/challenge—— 认证 noncePOST /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}—— 2-of-2 门限 ECDSA(3 轮)- KYC:
/v3/kyc/dan/*、/v3/kyc/gsign/*
服务端会在响应中返回 X-Next-Attestation-Nonce 头(SDK 的 nonce 缓存优化)。
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 - SDK README(Kotlin 架构):
https://github.com/gerege-systems/eid-platform-mn/blob/main/android/android-sdk/README.md - 生物识别密钥(深入技术细节):ANDROID_BIOMETRIC_KEYS.md