【HarmonyOS 7新能力|037】数字盾工程封装:把接入逻辑放进可维护的分层结构

转账、授权、删除密钥或提交重要合同,都不能把安全寄托在一个普通"确认"按钮上。页面可能被覆盖,待签内容可能在展示后发生变化,重复请求也可能导致同一操作执行两次。数字盾类能力的工程目标,是把用户看到的内容、用户作出的输入、最终签名的上下文和服务端执行结果连接成一条可核验闭环。
说明:文中的
TrustedUiPort、SecureSignPort等为教学抽象,不代表 HarmonyOS SDK 的真实接口。可信环境、认证方式、密钥硬件、权限与支持设备以当前官方文档和目标 SDK 为准;任何单项能力都不能构成"绝对安全"承诺。
1. 先定义需要保护的确认语义
安全确认不是给任意字符串盖章。业务要先定义操作类型、主体、对象、金额或范围、有效期以及执行后的影响。只有这些字段稳定,用户看到的摘要和签名输入才能来自同一份数据。
ts
export interface ProtectedOperation {
operationId: string
action: 'authorize' | 'transfer' | 'delete' | 'submit'
subjectId: string
target: string
consequence: string
expiresAt: number
nonce: string
}
不要把页面文案直接作为签名载荷,也不要让页面自行选择要签哪些字段。领域模型是唯一事实来源,展示摘要与待签摘要都由同一个规范化器生成。
2. 四层结构隔离可信边界

业务页面负责发起意图和显示最终结果;安全编排层校验请求并驱动状态机;可信能力适配层封装可信 UI、输入和签名;审计与密钥边界保存最少的核验记录,密钥材料始终留在受保护能力内部。
ts
export interface TrustedUiPort {
confirm(model: TrustedConfirmModel): Promise<TrustedInputResult>
}
export interface SecureSignPort {
sign(input: SignInput): Promise<SignResult>
}
页面无法直接取得私钥句柄或调用底层签名,适配器也不决定业务是否执行。边界清晰后,平台接口升级集中在适配层,风险策略集中在编排层。
3. 请求校验必须早于可信界面
可信界面只能保证其自身展示过程,不会替业务修复一份过期或字段缺失的请求。进入流程时先检查操作 ID、随机数、时间窗、动作白名单、目标格式以及当前会话是否有权发起。
ts
export function validateOperation(op: ProtectedOperation, now: number): CheckResult {
if (!op.operationId || !op.nonce) return fail('MISSING_IDENTITY')
if (op.expiresAt <= now) return fail('OPERATION_EXPIRED')
if (!allowedActions.has(op.action)) return fail('ACTION_NOT_ALLOWED')
if (!isTargetValid(op.target)) return fail('INVALID_TARGET')
return pass()
}
无效请求直接关闭,不进入认证与签名。这样既减少不必要的敏感交互,也避免攻击者利用可信流程试探系统状态。
4. 上下文摘要必须确定且可复现
同一业务对象在不同设备、语言或字段顺序下应得到相同摘要。使用明确字段顺序、字符编码、版本号和长度边界进行规范化,再由经过验证的密码学能力计算摘要。
ts
export interface CanonicalEnvelope {
schema: 'protected-operation/v1'
operationId: string
action: string
subjectId: string
target: string
expiresAt: number
nonce: string
}
function canonicalize(op: ProtectedOperation): string {
return stableSerialize(toEnvelope(op))
}
不要依赖普通对象默认序列化顺序,也不要自创摘要算法。摘要版本随结构记录,服务端按相同规范重建并核验。
5. 可信界面展示真正被签的内容

可信界面应展示操作类型、关键对象、不可逆后果和请求来源,而不是笼统的"是否继续"。展示模型从规范化信封生成,禁止页面临时覆盖关键字段。
ts
export interface TrustedConfirmModel {
title: string
primaryFacts: readonly { label: string; value: string }[]
consequence: string
digestPreview: string
timeoutAt: number
}
摘要预览不是让用户人工校验哈希,而是让诊断和审计能确认展示与签名的绑定。普通说明文字可以本地化,关键事实必须保持语义一致。
6. 可信输入只表达明确同意
认证成功不等于用户同意。可信输入需要发生在用户已看到完整关键内容之后,并明确区分确认、取消、超时与认证失败。页面不能把取消自动转换成重试。
ts
export type TrustedInputResult =
| { status: 'confirmed'; proof: string; confirmedAt: number }
| { status: 'cancelled' }
| { status: 'timeout' }
| { status: 'failed'; code: string }
快速连点应只产生一个有效结果。交互期间禁用普通页面的重复入口,流程结束后再恢复,避免并发创建多份待签请求。
7. 签名输入绑定操作与确认结果
签名输入至少包含规范化摘要、操作 ID、随机数、验证方、确认时间和有效期。可信输入返回的证明若平台支持,也应按官方方式绑定,而不是只签业务正文。
ts
export interface SignInput {
operationDigest: string
operationId: string
verifierId: string
nonce: string
confirmedAt: number
expiresAt: number
}
私钥生成、选择、认证和签名由可信能力负责。应用不导出密钥,不把签名前明文或认证秘密写入日志与普通持久化。
8. 状态机保证流程只能向前
编排器使用 validating、presenting、confirming、signing、verifying、completed、cancelled、failed 状态。每个状态只接受允许的事件,完成态不能再次进入签名。
ts
export type ShieldFlowState =
| { kind: 'validating' }
| { kind: 'confirming'; operationId: string }
| { kind: 'signing'; digest: string }
| { kind: 'completed'; receiptId: string }
| { kind: 'cancelled' }
| { kind: 'failed'; code: string; retryable: boolean }
页面旋转、进入后台或组件重建只重新订阅状态,不重新发起操作。涉及可信交互时的后台策略按官方能力与产品安全要求处理,不能猜测继续执行。
9. 服务端核验后才执行业务
客户端得到签名不代表操作已经完成。服务端必须重建规范化摘要,验证签名、密钥或证书状态、随机数、时间窗、主体权限和操作 ID 的未消费状态,全部通过后才在同一事务语义中执行业务并消费随机数。
ts
export interface VerificationReceipt {
operationId: string
accepted: boolean
reasonCode: string
receiptId?: string
verifiedAt: number
}
客户端只根据服务端回执展示成功。网络超时属于"结果未知",应凭 operationId 查询状态,不能直接再次提交。
10. 防重放与幂等是一套设计
随机数防止旧签名换场景复用,操作 ID 防止同一业务重复执行,短有效期缩小攻击窗口。服务端为操作建立 pending/consumed/expired/rejected 记录,并以原子方式完成核验与消费。
ts
async function submitOnce(payload: SignedOperation): Promise<VerificationReceipt> {
const known = await receiptRepository.find(payload.operationId)
if (known?.terminal) return known.receipt
return verifier.verifyAndConsume(payload)
}
重试必须携带相同操作 ID;创建新 ID 等价于新业务请求,需要重新展示和确认,不能静默替换。
11. 失败关闭与安全审计
取消、超时、界面失效、签名失败、上下文不一致或核验失败都应终止当前流程。允许重试时重新校验请求,必要时重新展示,不复用已过期确认。
ts
export interface ShieldAuditRecord {
operationId: string
action: string
state: string
reasonCode: string
digestVersion: string
occurredAt: number
}
审计不保存私钥、认证数据、完整敏感正文或可重放载荷。记录用于定位流程在哪一层失败,而不是复制全部请求。用户主动取消应作为正常结果,不应标记为攻击。
12. 测试清单与总结
测试至少覆盖字段篡改、过期请求、重复随机数、重复操作 ID、可信界面取消、输入超时、签名失败、服务端拒绝、网络结果未知和回执查询。还要验证页面展示摘要与实际签名摘要一致,普通页面不能覆盖可信区域,日志中没有密钥与认证材料。
数字盾工程化的核心,是让"看见、同意、签名、执行"指向同一操作。用规范化信封固定语义,用可信界面和可信输入收集明确同意,用签名绑定上下文,用服务端核验和幂等消费完成闭环。任何一环失败就安全关闭,任何一次成功都有可验证回执,才是可维护的高风险操作保护方案。