macOS 桌面客户端 —— 开发者指南¶
eID 平台的 macOS(SwiftUI)桌面客户端 —— 该桌面应用通过 e-ID Mongolia 手机应用
完成二维码 / 登记号推送登录,并可为 PDF 加盖合格电子签名。源码位置:
desktop/macos-app/(XcodeGen 工程 eIDMongolia)。
First-party 客户端。 与 iOS 应用遵循完全相同的原则,本客户端并非 RP —— 客户端不持有 RP secret、RP UUID 或注册信息。所有调用都经由自有 Web 后端的 公开
/api/*路由(与浏览器所用路径完全一致)。Go RP-API(/v3/*)的RP_API_SECRET仅由 Web 服务端持有(web/src/lib/rpclient.ts)。因此这不是 RP 集成—— RP 集成由 Web 服务端自身完成,桌面端只是使用其公开前端。
1. 功能¶
- 登录 —— 通过二维码或登记号 / civil-ID 号发送推送,由手机应用使用 PIN1 确认。
Web 服务端会对签名进行密码学校验,从证书 subject 中提取姓名、civil-ID 号与
documentNumber并返回 —— 桌面端自身不解析证书。 - PDF 签名 —— 选择 PDF,在本地计算其 SHA-256 摘要,并在手机应用中使用 PIN2 签名。
加盖签章后的 PDF(PAdES/PKCS#7 + 验证页)保存到
~/Downloads。 - 身份句柄 —— 不存在 Bearer 会话;登录获得的
documentNumber作为身份句柄, 保存在钥匙串中(恢复时需通过 Touch ID 校验)。
2. 架构¶
客户端调用 Web 应用的公开 /api/* 路由(web/src/app/api/*)。
所使用的路由(依据源码 Core/Network/Endpoints.swift、Core/Network/APIClient.swift):
| 路由 | 方法 | 用途 |
|---|---|---|
/api/start |
POST | 启动二维码会话 → {sessionId, qr, deviceLinkBase, vc, pollToken} |
/api/login-notify |
POST | 按登记号 / civil-ID 号推送 → {sessionId, vc, pollToken}(限流 60 秒 3 次) |
/api/status |
GET | ?sessionId=&pollToken= 长轮询 —— 服务端保持约 1 秒,客户端重试 |
/api/sign-pdf-start |
POST | {etsi, digestB64, fileName, callbackUrl} → PIN2 推送 → {sessionId, vc, pollToken}(每个 etsi 限流 60 秒 3 次) |
/api/sign-pdf-download |
POST | multipart file + sessionId + pollToken → 加盖签章后的 PDF 字节 |
/api/health |
GET | 检查服务端健康状态 |
注意。
pollToken由start/login-notify/sign-pdf-start的响应返回, 且对/api/status与/api/sign-pdf-download为必需。sessionId会暴露在二维码中, 因此仅凭它无法读取个人信息 ——pollToken只签发给会话发起方 (Endpoints.swift,第 18-23 行)。
客户端中不存在 secret、RP UUID 与 RP 名称(Core/Network/AppConfig.swift)。
服务端基础 URL 的取值优先级(首个非空者生效):
| 来源 | 说明 |
|---|---|
UserDefaults["API_BASE_URL_OVERRIDE"] |
通过设置界面配置 |
env API_BASE_URL |
环境变量 |
| 默认值 | DEBUG:http://localhost:3000,Release:https://eidmongolia.mn |
3. 前置条件¶
- macOS 14+(deployment target 14.0)
- Xcode 16+,Swift 5.10
brew install xcodegen—— 从project.yml生成.xcodeproj- SPM 依赖:Sparkle 2(自动更新)、
../gerege-token-kit(本地路径包)
4. 构建与运行¶
cd desktop/macos-app
xcodegen generate
xcodebuild -project eIDMongolia.xcodeproj -scheme eIDMongolia \
-configuration Debug -destination 'platform=macOS,arch=arm64' build
open eIDMongolia.xcodeproj # ⌘R
重要。 请使用
-scheme eIDMongolia,而不是-target—— 解析本地GeregeTokenKitSPM 包需要 scheme。
本地测试¶
DEBUG 构建默认指向 web(:3000)。请同时启动 Go API 与 web:
cd ../../server && SMARTID_RP_API_SECRET= go run ./cmd/smartid # Go API :8080
cd ../../web && npm run dev # web :3000
若需指向其他地址,请使用 设置 → 服务器(或环境变量 API_BASE_URL)。
手机应用也必须指向同一台 Go 服务器。
5. 主要流程¶
登录(Features/Login/LoginView.swift)¶
二维码(initQR):
1. POST /api/start → {sessionId, qr, vc, pollToken}。
2. 使用 CoreImage 将 qr 值(= sessionId)渲染为二维码;在界面上显示验证码 vc。
3. 用手机扫描二维码 → 使用 PIN1 确认。
登记号推送(initiateLogin):
1. 用户输入登记号 / civil-ID 号(register,转为大写)。
2. POST /api/login-notify {register} → {sessionId, vc, pollToken};推送发送至手机。
共同的收尾(APIClient.waitForAuth):
- 以约 400 毫秒的间隔重复调用 GET /api/status?sessionId=&pollToken= 直至 COMPLETE
(服务端每次保持 1 秒)。
- 当返回 COMPLETE + OK 时,Web 服务端从证书 subject 中提取 name / idNumber
(serialNumber → civil-ID 号)。
- 结果以 StoredIdentity 形式保存到钥匙串
(documentNumber、fullName、civilID、certificateLevel)。
PDF 签名(Features/Sign/SignView.swift)¶
与网页演示页面(web/src/app/demo/page.tsx)的流程完全一致:
- 选择 PDF(最大 25 MB)。源 PDF 的 SHA-256 摘要在客户端本地计算
(
CryptoKit.SHA256)—— 与随后被加盖签章的字节完全相同。 POST /api/sign-pdf-start {etsi, digestB64, fileName, callbackUrl:""}→{sessionId, vc, pollToken}。etsi= 登录时获得的 civil-ID 号 (civilID,回退到nationalID,转为大写)。PIN2 推送发送至手机。callbackUrl为空 —— 由于手机是另一台设备,不存在 Web2App 返回。- 轮询
GET /api/status?sessionId=&pollToken=(与认证同路径)→ COMPLETE/OK。 POST /api/sign-pdf-download(multipartfile+sessionId+pollToken) → 加盖签章后的 PDF 字节 →~/Downloads/<名称>_signed.pdf(文件名冲突时追加数字后缀)。
SEC-3:在启动签名流程前会执行
SecurityGuard.enforce()—— 防篡改 / 反调试检查(仅在 Release 中启用)。
6. USB token kit¶
desktop/gerege-token-kit/ 是面向 FEITIAN USB 令牌的无依赖本地 SPM 包
(本地 PKCS#11/APDU,不连接服务器)。macOS 应用会自动将 ../gerege-token-kit
作为路径包解析,并在 Core/Token/(TokenManager、TokenProvisioner)与
Tokens 功能中使用。详见:USB token kit。
7. 源码(链接)¶
- macOS 应用:
https://github.com/gerege-systems/eid-platform-mn/tree/main/desktop/macos-app - 网络层:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/macos-app/Core/Network/Endpoints.swift - HTTP 客户端:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/macos-app/Core/Network/APIClient.swift - 登录:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/macos-app/Features/Login/LoginView.swift - 签名:
https://github.com/gerege-systems/eid-platform-mn/blob/main/desktop/macos-app/Features/Sign/SignView.swift - 组件细节:
desktop/macos-app/CLAUDE.md,安全加固:desktop/macos-app/SECURITY-HARDENING.md