HarmonyOS7 TEE 级安全怎么用?分布式数字身份与数字盾实战
前言
接着上篇的安全话题,今天聊两个更硬核的东西:分布式数字身份(DID)和数字盾。这俩配合起来,能解决一个很现实的问题——怎么在鸿蒙生态里证明"我是我",同时不把自己的隐私全抖出去。
DID 到底是干嘛的
传统的身份验证,你得把身份证号、手机号、人脸数据全交给 App,App 再传给服务器验证。中间任何一个环节泄露,你就完了。
DID(Decentralized Identifier,分布式数字身份)换了个思路:系统帮你在 TEE 里生成一个唯一的数字身份标识,这个标识跟你的真实身份绑定,但应用只能拿到一个"身份证明",拿不到你的原始信息。
打个比方:你去酒店入住,DID 相当于你跟前台说"系统可以证明我的身份",前台向系统验证一下就完事,不需要把你的身份证复印件留在他那里。
数字盾的三板斧
数字盾是 HarmonyOS 7 在安全操作层面的能力,它提供三个核心保障:

可信数字签名。用户的签名操作在 TEE 里完成,签出来的数字签名具有法律效力级别的防篡改能力。
可信 UI 确认。关键操作(比如转账确认、合同签署)的界面由系统安全层渲染,应用无法伪造或劫持这个界面。
可信输入。密码、PIN 码这些敏感输入走安全输入通道,键盘和输入框都在 TEE 保护下,截屏录屏都拿不到内容。
给智能助手接入 DID
场景:用户在智能助手里签署一份智能家居设备的共享授权书,需要验证身份并进行数字签名。
注册 DID 身份
首次使用时,为用户创建一个 DID 身份。这个过程系统会自动处理密钥生成和 TEE 存储:
import { did } from '@kit.DistributedIdentityKit';
import { common } from '@kit.AbilityKit';
async function registerUserDID(context: common.UIAbilityContext): Promise<string> {
// 检查设备是否支持 DID
const isSupported = await did.isSupported();
if (!isSupported) {
throw new Error('当前设备不支持分布式数字身份');
}
// 创建 DID 身份声明
const claim: did.IdentityClaim = {
// 声明类型:自然人身份
type: did.ClaimType.NATURAL_PERSON,
// 绑定的认证方式(支持多种)
authMethods: [
did.AuthMethod.FACE, // 人脸
did.AuthMethod.FINGERPRINT, // 指纹
],
// 最小化隐私暴露:只声明必要属性
disclosedAttributes: [
did.Attribute.AGE_VERIFIED, // 只证明"已成年",不暴露具体年龄
did.Attribute.IDENTITY_VALID, // 只证明"身份有效",不暴露证件号
],
};
try {
const didResult = await did.register(context, claim);
console.info(`DID 注册成功: ${didResult.did}`);
// 把 DID 标识存到 Preferences 里,后续使用
const prefs = await preferences.getPreferences(context, 'user_identity');
await prefs.put('did', didResult.did);
await prefs.flush();
return didResult.did;
} catch (err) {
console.error('DID 注册失败:', JSON.stringify(err));
throw err;
}
}


注意 disclosedAttributes 这个字段——这就是"最小化隐私暴露"的核心。你只声明"我已成年",验证方只能得到 true/false,看不到你的出生日期。
发起身份验证
签署授权书之前,先验证用户身份:
import { did } from '@kit.DistributedIdentityKit';
async function verifyUserIdentity(
context: common.UIAbilityContext,
userDid: string
): Promise<did.VerifyResult> {
// 构造验证请求
const verifyRequest: did.VerifyRequest = {
did: userDid,
// 要求人脸验证
challenge: did.Challenge.BIOMETRIC,
// 验证用途声明(会展示给用户看)
purpose: '签署智能家居设备共享授权书',
// 超时时间
timeout: 30000,
};
try {
// 系统会弹出可信 UI 让用户进行生物认证
// 这个 UI 是系统层渲染的,应用无法干预
const result = await did.verify(context, verifyRequest);
if (result.verified) {
console.info('身份验证通过');
// result.token 是一个一次性的验证令牌
return result;
} else {
console.warn('身份验证未通过');
throw new Error('身份验证失败');
}
} catch (err) {
console.error('验证过程出错:', JSON.stringify(err));
throw err;
}
}
系统弹出的验证 UI 是可信 UI,由安全层直接渲染。你的应用代码拿不到用户的人脸数据,也看不到验证过程中的任何中间状态。
数字盾签名
验证通过后,用数字盾对授权书进行签名:
import { digitalShield } from '@kit.DigitalShieldKit';
interface AuthDocument {
deviceId: string;
sharedUsers: string[];
permissions: string[];
timestamp: number;
}
async function signAuthDocument(
doc: AuthDocument,
verifyToken: string
): Promise<digitalShield.SignatureResult> {
// 准备签名内容
const content = JSON.stringify(doc);
const contentHash = await digitalShield.hash(content, digitalShield.HashAlgorithm.SM3);
// 配置签名参数
const signConfig: digitalShield.SignConfig = {
algorithm: digitalShield.SignAlgorithm.SM2, // 国密 SM2
hashAlgorithm: digitalShield.HashAlgorithm.SM3,
// 关联身份验证令牌(证明签名者已通过身份验证)
identityToken: verifyToken,
// 签名时间戳
signTime: Date.now(),
};
// 构造可信 UI 确认内容
const confirmUI: digitalShield.ConfirmUIConfig = {
title: '签署设备共享授权书',
body: `将以下设备共享给 ${doc.sharedUsers.length} 位用户:\n` +
`设备: ${doc.deviceId}\n` +
`权限: ${doc.permissions.join(', ')}`,
confirmText: '确认签署',
cancelText: '取消',
// 安全级别:TEE
securityLevel: digitalShield.SecurityLevel.TEE,
};
try {
// 系统弹出可信 UI 让用户确认
// 确认后在 TEE 内完成签名
const result = await digitalShield.sign(contentHash, signConfig, confirmUI);
console.info(`签名完成,签名值: ${result.signature.substring(0, 20)}...`);
console.info(`签名证书哈希: ${result.certHash}`);
return result;
} catch (err) {
if (err.code === digitalShield.ErrorCode.USER_CANCELLED) {
console.info('用户取消了签名');
}
throw err;
}
}
可信输入框
授权书里可能需要用户输入 PIN 码做二次确认。用数字盾的可信输入组件:
import { digitalShield } from '@kit.DigitalShieldKit';
@Component
struct SecurePinInput {
@State pinLength: number = 0;
private onComplete: (encryptedPin: string) => void;
build() {
Column({ space: 16 }) {
Text('请输入安全 PIN 码')
.fontSize(18)
.fontWeight(FontWeight.Bold)
// 可信输入组件:渲染和输入都在安全层完成
// 应用层只能拿到加密后的结果
digitalShield.SecureInput({
type: digitalShield.InputType.PIN,
length: 6,
placeholder: '请输入 6 位 PIN',
// 防截屏标记
antiCapture: true,
onComplete: async (result: digitalShield.SecureInputResult) => {
// result.encryptedValue 是 TEE 加密后的 PIN
// 应用层拿不到原始 PIN 值
this.onComplete(result.encryptedValue);
},
onInput: (length: number) => {
this.pinLength = length;
}
})
.width('80%')
.height(48)
Text(`已输入 ${this.pinLength}/6 位`)
.fontSize(14)
.fontColor('#999')
}
.width('100%')
.padding(24)
.backgroundColor($r('app.color.bg_secondary'))
.borderRadius(16)
}
}
SecureInput 组件跟普通的 TextInput 用法差不多,但内部完全不同——键盘是安全键盘,输入内容直接进 TEE,你连输入了多少个字符都只能通过回调知道,拿不到原始值。
跨设备 DID 互认
DID 的一个强大之处在于跨设备互认。用户在手机上注册了 DID,走到平板上,平板能自动识别并信任这个身份。这靠的是鸿蒙的分布式身份信任链——所有设备的 TEE 共享一个根信任证书。
实际操作是这样的:
import { did } from '@kit.DistributedIdentityKit';
// 在平板上查找用户在其他设备上注册的 DID
async function findExistingDID(context: common.UIAbilityContext): Promise<string | null> {
const existingDIDs = await did.discoverTrustedDIDs(context);
if (existingDIDs.length > 0) {
// 找到已有的 DID,直接使用
const primaryDID = existingDIDs[0];
console.info(`发现已有 DID: ${primaryDID.did}, 注册设备: ${primaryDID.registeredDevice}`);
return primaryDID.did;
}
// 没有找到,需要新注册
return null;
}
这意味着用户在手机上签过的授权书,在平板上也能验证真伪,不需要重新走一遍身份验证。我们智能助手的设备共享授权就利用了这个能力——用户在手机上授权一次,平板和车机都能自动识别。
完整流程串起来
把上面几个步骤串成完整签名流程:
async function completeAuthSigning(
context: common.UIAbilityContext,
doc: AuthDocument
): Promise<boolean> {
try {
// 1. 获取用户 DID
const prefs = await preferences.getPreferences(context, 'user_identity');
const userDid = await prefs.get('did') as string;
if (!userDid) {
// 首次使用,先注册 DID
await registerUserDID(context);
return false; // 注册完让用户重新触发签名
}
// 2. 身份验证
const verifyResult = await verifyUserIdentity(context, userDid);
// 3. 数字签名
const signResult = await signAuthDocument(doc, verifyResult.token);
// 4. 保存签名结果
await saveSignature(doc.deviceId, signResult);
return true;
} catch (err) {
console.error('签名流程失败:', JSON.stringify(err));
return false;
}
}
几个实用建议
DID 注册一次就行。别每次操作都注册,注册一次后 DID 标识永久有效(除非用户主动重置)。
可信 UI 的文案要认真写。purpose 和 confirmUI.body 会直接展示给用户,写清楚"这个签名是干嘛的"。模糊的描述会降低用户信任度,也容易被安全审核打回。
TEE 操作有性能开销。一次签名大概 500ms-1s,别在列表滑动或者高频操作里嵌入 TEE 调用。
模拟器调试受限。跟星盾引擎一样,DID 和数字盾都依赖 TEE 硬件。调试时可以用 did.isSupported() 做判断,不支持时走 mock 路径。
下篇换个轻松点的话题,聊聊 HarmonyOS 7 的网络优化——QUIC 持久连接和预建链怎么让你的应用网速起飞。
更多推荐


所有评论(0)