200 lines
13 KiB
HTML
200 lines
13 KiB
HTML
<!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>
|