AML/passport-nfc-app/docs/开发计划.html

200 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!doctype html>
<html lang="zh-Hans">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>手机扫护照混合 App — 开发计划</title>
<style>
:root {
--bg: #f6f7f9; --surface: #ffffff; --surface-2: #f0f2f5; --border: #dde1e6;
--text: #1d2329; --text-2: #5b6570; --accent: #2f6fde;
--ok: #1f8a4c; --ok-bg: #e5f5ec; --bad: #c4372b; --bad-bg: #fbe9e7; --warn: #a86400; --warn-bg: #fdf1dc;
--code-bg: #eef1f5;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #14171b; --surface: #1c2026; --surface-2: #232830; --border: #323943;
--text: #e4e8ec; --text-2: #9aa4ae; --accent: #6a9cf5;
--ok: #5fd08f; --ok-bg: #173325; --bad: #f08a80; --bad-bg: #3a1d1a; --warn: #f0b75c; --warn-bg: #3a2b12;
--code-bg: #262c35;
}
}
:root[data-theme="dark"] {
--bg: #14171b; --surface: #1c2026; --surface-2: #232830; --border: #323943;
--text: #e4e8ec; --text-2: #9aa4ae; --accent: #6a9cf5;
--ok: #5fd08f; --ok-bg: #173325; --bad: #f08a80; --bad-bg: #3a1d1a; --warn: #f0b75c; --warn-bg: #3a2b12;
--code-bg: #262c35;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--text);
font: 15px/1.65 -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif; }
main { max-width: 980px; margin: 0 auto; padding: 32px 16px 64px; }
h1 { font-size: 26px; margin: 0 0 4px; }
h2 { font-size: 19px; margin: 40px 0 12px; padding-bottom: 6px; border-bottom: 1px solid var(--border); }
h3 { font-size: 16px; margin: 24px 0 8px; }
p, li { color: var(--text); }
.sub { color: var(--text-2); margin: 0 0 24px; }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 16px 20px; margin: 12px 0; }
.callout { border-left: 4px solid var(--accent); }
.callout.warn { border-left-color: var(--warn); }
.callout.bad { border-left-color: var(--bad); }
code { background: var(--code-bg); padding: 1px 5px; border-radius: 4px; font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
pre { background: var(--code-bg); padding: 12px 14px; border-radius: 8px; overflow-x: auto; font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.table-wrap { overflow-x: auto; margin: 8px 0; }
table { border-collapse: collapse; width: 100%; background: var(--surface); font-size: 14px; }
th, td { border: 1px solid var(--border); padding: 7px 10px; text-align: left; vertical-align: top; }
th { background: var(--surface-2); font-weight: 600; white-space: nowrap; }
.y { color: var(--ok); background: var(--ok-bg); font-weight: 600; text-align: center; white-space: nowrap; }
.n { color: var(--bad); background: var(--bad-bg); font-weight: 600; text-align: center; white-space: nowrap; }
.m { color: var(--warn); background: var(--warn-bg); font-weight: 600; text-align: center; white-space: nowrap; }
.steps { margin: 0; padding-left: 20px; }
.steps li { margin: 4px 0; }
.tag { display: inline-block; font-size: 12px; padding: 1px 8px; border-radius: 10px; background: var(--surface-2); color: var(--text-2); margin-right: 4px; }
.phase { display: inline-block; font-weight: 700; color: var(--accent); margin-right: 6px; }
footer { margin-top: 48px; color: var(--text-2); font-size: 13px; }
</style>
</head>
<body>
<main>
<h1>手机扫护照混合 App — 开发计划</h1>
<p class="sub">工程代号 <code>passport-nfc-app</code>。目标:用手机 NFC 读取带芯片的电子护照(eMRTD),读出 MRZ、基本信息、芯片头像并做真伪验证。本文档为立项与分阶段实施计划。最后更新 2026-11-10。</p>
<div class="card callout">
<strong>一句话:</strong>用 <strong>Capacitor + Angular</strong> 做壳,护照芯片读取由<strong>自定义原生插件</strong>完成(iOS 用 NFCPassportReader,Android 用 jMRTD)。双端、全深度(MRZ + DG1 + DG2 头像 + PA 验真),先做独立 Demo,暂不接后端。
</div>
<h2>1. 已确认的决策</h2>
<div class="table-wrap">
<table>
<thead><tr><th>决策项</th><th>结论</th><th>理由</th></tr></thead>
<tbody>
<tr><td>技术栈</td><td><strong>Capacitor + Angular</strong></td><td>贴合现有 AML_Frontend / justsolutionsWeb 的 Angular 技术栈,H5 可复用,NFC 走原生插件</td></tr>
<tr><td>目标平台</td><td><strong>iOS + Android</strong></td><td>两端都要覆盖;实施上 Android 优先</td></tr>
<tr><td>读取深度</td><td><strong>MRZ + DG1 + DG2 头像 + PA 被动认证</strong></td><td>KYC 防伪必须验签(PA)</td></tr>
<tr><td>后端集成</td><td><strong>先做独立 Demo</strong></td><td>先验证可行性,后续再接 <code>ScanCertificateV2</code></td></tr>
</tbody>
</table>
</div>
<h2>2. 技术约束与现实(必须先知道)</h2>
<div class="card callout bad">
<ul>
<li><strong>网页读不了护照芯片。</strong>Web NFC(<code>NDEFReader</code>)只支持 NDEF、仅安卓 Chrome,不暴露 APDU;护照需要 ISO-DEP / APDU + BAC/PACE,只能走原生。所以本项目重心在原生插件,不在 H5。</li>
<li><strong>iOS NFC 门槛高。</strong>需要 <strong>付费 Apple 开发者账号</strong>才能开 NFC entitlement(<code>com.apple.developer.nfc.readersession.iso7816</code>,AID 需含 eMRTD <code>A0000002471001</code>);NFC <strong>不能在模拟器跑</strong>,必须 iPhone 7+ / iOS 13+ 真机。</li>
<li><strong>Android 需真机。</strong>需带 NFC 的真机,模拟器无 NFC。</li>
<li><strong>测试需要实体护照。</strong>每个阶段验证都要一本带芯片的护照 + 对应 MRZ。</li>
<li><strong>必须先有 MRZ。</strong>MRZ 的「护照号 + 出生日期 + 有效期」是开芯片 BAC/PACE 的钥匙,缺它读不了芯片。</li>
</ul>
</div>
<h2>3. 架构总览</h2>
<pre>┌─────────────────────────────────────────────┐
│ Angular (WebView) —— 界面层 │
│ 输入/OCR MRZ → 调插件 → 展示字段/头像/验真结果 │
└───────────────┬─────────────────────────────┘
│ Capacitor Bridge (JS ↔ Native)
┌───────────────┴─────────────────────────────┐
│ 自定义插件 PassportNfc.readPassport(mrzKey) │
│ ├─ iOS : Swift + NFCPassportReader │
│ └─ Android: Kotlin + jMRTD + SpongyCastle │
│ BAC/PACE → 读 DG1/DG2 → PA 验签 │
└──────────────────────────────────────────────┘</pre>
<h2>4. 关键原生库选型</h2>
<p>均为业界事实标准,避免自己实现密码学与协议栈。</p>
<div class="table-wrap">
<table>
<thead><tr><th>平台</th><th>库</th><th>语言 / 许可</th><th>能力</th></tr></thead>
<tbody>
<tr><td>iOS</td><td><code>NFCPassportReader</code>(AndyQ)</td><td>Swift / MIT</td><td>BAC + PACE、DG1/DG2、PA(可带 CSCA masterlist)、AA</td></tr>
<tr><td>Android</td><td><code>jMRTD</code> + <code>SCUBA</code> + <code>SpongyCastle</code></td><td>Java/Kotlin / LGPL</td><td>BAC + PACE、全数据组、PA、AA</td></tr>
<tr><td>MRZ OCR(P4)</td><td>ML Kit Text Recognition 或 Tesseract</td><td>—</td><td>摄像头识别 MRZ 两行,替代手输</td></tr>
</tbody>
</table>
</div>
<h2>5. App 核心流程</h2>
<ol class="steps">
<li>输入或摄像头扫描 MRZ → 得到 <code>护照号 + 出生日期 + 有效期</code></li>
<li>点「贴卡读取」→ 调 <code>PassportNfc.readPassport({ mrzKey })</code></li>
<li>原生层:13.56MHz 供电 → BAC/PACE 开安全通道 → 读 DG1(文本)、DG2(头像)、EF.SOD</li>
<li>PA 被动认证:SOD → DSC → CSCA 证书链校验</li>
<li>回传 JSON + 头像 base64 + 验真结论 → Angular 展示</li>
</ol>
<h2>6. 建议工程结构</h2>
<pre>passport-nfc-app/
├── docs/ 本计划等文档
├── src/ Angular 界面(MRZ 输入页、读取页、结果页)
├── capacitor.config.ts
├── android/ cap add android 后生成
├── ios/ cap add ios 后生成
└── plugins/passport-nfc/ 自定义 Capacitor 插件
├── src/ TS 接口 definitions / web 桩
├── android/ Kotlin + jMRTD 实现
└── ios/ Swift + NFCPassportReader 实现</pre>
<h2>7. 分阶段路线图</h2>
<div class="table-wrap">
<table>
<thead><tr><th>阶段</th><th>内容</th><th>产出 / 验收</th><th>工具链前置</th></tr></thead>
<tbody>
<tr><td><span class="phase">P0</span></td><td>搭 Capacitor + Angular 骨架 + 插件 TS 接口与 web 桩</td><td>Web 壳能跑;插件接口定义完成</td><td>Node(已就绪)</td></tr>
<tr><td><span class="phase">P1</span></td><td><strong>Android 优先</strong>:Kotlin 插件接 jMRTD,手输 MRZ,读 DG1/DG2</td><td>安卓真机扫出芯片文本 + 头像</td><td>JDK + Android Studio + 安卓真机</td></tr>
<tr><td><span class="phase">P2</span></td><td>PA 被动认证 + 头像展示 + 错误/超时处理 + UI 打磨</td><td>安卓完整 Demo,含验真结论</td><td>同上 + CSCA masterlist</td></tr>
<tr><td><span class="phase">P3</span></td><td>iOS:Swift 插件接 NFCPassportReader,配 entitlement</td><td>iPhone 真机双端打通</td><td>Xcode + CocoaPods + iPhone + 付费账号</td></tr>
<tr><td><span class="phase">P4</span></td><td>MRZ 摄像头 OCR,替代手输;体验完善</td><td>扫 MRZ 自动填充</td><td>ML Kit / Tesseract</td></tr>
<tr><td><span class="phase">P5</span></td><td>(可选)接 AML 后端 <code>ScanCertificateV2</code>,与柜台扫描仪统一口径</td><td>读取结果入 KYC 流程</td><td>后端联调</td></tr>
</tbody>
</table>
</div>
<h2>8. 本机工具链现状</h2>
<div class="table-wrap">
<table>
<thead><tr><th>工具</th><th>状态</th><th>用途</th><th>动作</th></tr></thead>
<tbody>
<tr><td>Node 24 / npm 11</td><td class="y">已就绪</td><td>Capacitor + Angular</td><td>—</td></tr>
<tr><td>JDK</td><td class="n">未装</td><td>Android 构建</td><td>P1 前装(Android Studio 自带)</td></tr>
<tr><td>Android SDK / Studio</td><td class="n">未装</td><td>Android 构建 / 真机部署</td><td>P1 前装</td></tr>
<tr><td>Xcode</td><td class="n">未装</td><td>iOS 构建</td><td>P3 前装</td></tr>
<tr><td>CocoaPods</td><td class="n">未装</td><td>iOS 依赖</td><td>P3 前装</td></tr>
<tr><td>付费 Apple 开发者账号</td><td class="m">待确认</td><td>iOS NFC entitlement</td><td>P3 前准备</td></tr>
</tbody>
</table>
</div>
<h2>9. 风险与合规</h2>
<div class="card callout warn">
<ul>
<li><strong>隐私</strong>:护照芯片含姓名、证件号、头像等敏感个人信息。Demo 阶段<strong>不落盘、不上传</strong>,仅内存展示;接后端时按现有 KYC 的数据口径与脱敏要求处理。</li>
<li><strong>PA 验签依赖 CSCA masterlist</strong>:完整被动认证需各签发国 CSCA 证书(来源 ICAO PKD)。Demo 可先做 SOD 哈希一致性 + DSC 签名校验,masterlist 到位后再补全证书链。</li>
<li><strong>iOS 政策风险</strong>:NFC entitlement 需付费账号并审批;若账号/设备不具备,P3 会被阻塞,但不影响 P0–P2 的 Android 路线。</li>
<li><strong>库许可</strong>:jMRTD/SCUBA 为 LGPL,注意引用方式与分发合规。</li>
<li><strong>兼容性</strong>:不同国家/版本护照数据组与安全机制(BAC vs PACE)不一,需多本护照回归测试。</li>
</ul>
</div>
<h2>10. 验收标准(Demo Done)</h2>
<ul class="steps">
<li>安卓真机:输入 MRZ → 贴卡 → 读出 DG1 文本字段 + DG2 头像,PA 给出通过/失败结论</li>
<li>iOS 真机:同等能力打通</li>
<li>读取失败(MRZ 错、芯片读不到、验签失败)有明确提示,不崩溃</li>
<li>全程不持久化敏感数据</li>
</ul>
<h2>11. 下一步</h2>
<div class="card callout">
<strong>P0 立即可做(无需原生工具链):</strong>初始化 <code>passport-nfc-app</code> 的 Capacitor + Angular 骨架,定义 <code>PassportNfc</code> 插件接口与 web 桩,搭好 MRZ 输入 / 结果展示两个页面。<br>
之后进入 P1 需先安装 JDK + Android Studio 并准备一台带 NFC 的安卓真机 + 一本实体护照。
</div>
<footer>
相关文档:同目录将补充《插件接口说明》《构建与真机调试》;背景见 AML_Backend <code>Docs/KYC/电子护照芯片读取原理.html</code>。标准依据 ICAO Doc 9303、ISO/IEC 14443 / 7816-4。
</footer>
</main>
</body>
</html>