本文基于 HarmonyOS 7(API 26)官方《DID数字身份》开发指导与官方新能力一览整理。文中接口名称、权限名与版本号均标注官方出处;示例代码以官方开发步骤为骨架改写,未做真机实测,不编造任何实测数据。数字身份为 HarmonyOS 7 新能力,需升级至 HarmonyOS 7 并以实际支持机型为准。

引子:出示证件照这件事,我们交出去了多少多余信息
V哥先讲一个所有人都干过的事:注册某个服务,被要求"上传身份证照片"。照片拍完传上去,你交出去的是什么?姓名、性别、民族、出生日期、住址、身份证号、签发机关、有效期------全部。而那个业务真正要验证的,可能只有一句话:"这个人是不是成年人。"
一张证件照,交出去一百个字段,用上一个。多出来的九十九个去哪了?存在对方服务器里,存多久、谁能看、会不会被爬,你一概不知。V哥不是吓唬人,这是过去几十年"复印件思维"的通病:验证身份的唯一办法,就是把证件本身交出去。
HarmonyOS 7(API 26)给这个问题上了一个系统级解法------分布式数字身份 DID (Decentralized Identifier,去中心化身份)。官方新能力一览的原文是这么说的(HarmonyOS 7 新能力一览):
系统级的数字身份 DID 框架,通过 TEE 存储颁发用户的数字身份,使用时经本人同意后按需出示,证明身份且最小化证件隐私暴露。
V哥把这句话拆成三个词:TEE 颁发、本人同意、最小化出示 。这也是本文标题的由来,更是这一期想讲透的主线:数字身份的要害不是"把证件数字化",而是最小化暴露------凭证存在 TEE 里,用的时候经本人同意、按需出示,应用自始至终碰不到原始证件数据。
一、先想明白:DID 到底换了什么
传统方式和 DID 方式的差别,V哥画一张链路图就清楚了:

对应到官方能力,HarmonyOS 7 在 Online Authentication Kit(在线认证服务) 里新增了数字身份特性(从 API 版本 26.0.0 开始,官方开发指导),提供四块能力:
| 能力 | 官方描述关键词 | 对开发者的意义 |
|---|---|---|
| DID 密钥创建及使用 | 创建及使用与用户 DID 关联的密钥 | 密钥在 TEE 里生成,应用拿不到私钥 |
| DID 导入、查询及删除 | 导入 DID 标识、DID 文档等信息到设备 | 身份标识由用户设备持有,不在应用手里 |
| VC 导入、查询及删除 | 可验证凭证(Verifiable Credentials)导入设备 TEE 环境安全存储 | 凭证存 TEE,应用只能拿到概要信息 |
| VP 出示 | 获取用户同意后,在 TEE 中将需披露的属性组装成 VP(Verifiable Presentation)返回 | 出示这一步过本人同意 + 部分披露 |
注意官方那句"在 TEE 中将 VC 中需要披露的属性组装成 VP"------这句话就是"复印件思维"的终结者。传统流程里应用拿到的是完整证件;DID 流程里应用拿到的是系统在 TEE 里按你声明的字段范围裁剪、签名后的出示声明,多一个字段都出不来。
V哥在这里给出本文的自创观点:DID 的真正革命不是加密,而是"验证方权限的降维"。过去验证方默认有权查看证件全本,DID 之后验证方只拥有"提问权"------你问"是否成年",系统答"是",至于生日是哪天,从头到尾不经过你。把"查看"降级成"问答",隐私问题才从根上解决,加密只是给这套问答上了把锁。
还有一个架构认知先立住:这套体系是端云协同 的,不是端侧单机游戏。移动端负责 TEE 密钥、凭证存储和出示;你的应用还得有一个符合 W3C DID 协议的服务器,负责公钥上链、获取 DID 文档、向发行方拿凭证。官方原话:"应用部署符合 DID 协议的服务器之后,结合移动端的数字身份能力,可实现跨平台互通互认的数字身份业务场景。"
二、准入门槛:三条硬约束,一条都不能少
写代码之前先对表,官方"约束与限制"给了三条(官方开发指导):
① 服务器门槛。 应用已部署符合 DID 标准协议的服务器。这是最大的工程量所在------端侧 API 反而不难,难在云侧要按 W3C DID 协议把 DID 文档、凭证颁发、VP 核验这套东西建起来。
② 设备门槛。 设备需支持生物特征(指纹/3D人脸),且达到 ATL4 级别的认证可信等级。官方给的查询方式:
typescript
import { BusinessError } from '@kit.BasicServicesKit';
import { userAuth } from '@kit.UserAuthenticationKit';
// 查询设备人脸识别是否达到 ATL4 认证可信等级(官方示例)
try {
userAuth.getAvailableStatus(userAuth.UserAuthType.FACE, userAuth.AuthTrustLevel.ATL4);
console.info('current auth trust level is supported');
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`current auth trust level is not supported. Code is ${err?.code}, message: ${err?.message}`);
}
V哥的提醒:这个查询要放进运行时降级逻辑,不能只当启动自检。ATL4 不达标就别拉起 DID 流程,退回传统验证方式,别让用户面对一个必挂的按钮。
③ 权限门槛。 需要申请受限权限 ohos.permission.ACCESS_FIDO2_ONLINEAUTH(官方开发准备章节)。受限权限不是声明了就有,要按申请受限权限流程提交申请,具体获批条件以官方审核为准。
另外官方还有一条隐私红线:数字身份服务会将凭证信息、匿名化的指纹 ID 和面容 ID 等个人信息返回至应用,应用将个人信息上云前,需要向用户明示并且取得同意。这句要写进你的隐私设计里,不是免责声明,是硬要求。
三、动手第一步:在 TEE 里生成 DID 密钥
官方把业务分成三段流程:启用数字身份 → 颁发数字凭证 → 出示数字凭证。V哥用"给员工发一张数字化工作证"当例子串起来。
启用数字身份的官方流程是:应用云侧下发密钥别名等参数 → 构造 GenerateKeyRequest 调用 generateKey 生成 DID 密钥 → 拿到公钥、证书链 → 公钥上报云侧完成上链等操作并获取 DID 文档 → 调用 importDid 导入设备。核心代码(以官方开发步骤为骨架):
typescript
import { did } from '@kit.OnlineAuthenticationKit';
import { buffer } from '@kit.ArkTS';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 第一步:在 TEE 里生成 DID 密钥
async function generateDidKey(context: common.UIAbilityContext): Promise<void> {
// keyAlias 由应用云侧下发,别在前端硬编码
const generateKeyRequest: did.GenerateKeyRequest = {
keyAlias: 'vgeWorkCardKey',
keyConfig: {
algorithm: did.KeyAlgo.SM2, // 国密 SM2
purposeList: [did.KeyPurpose.SIGN, did.KeyPurpose.VERIFY]
},
authenticatorConfig: {
authTypeList: [did.AuthType.UVM_FINGERPRINT], // 绑定生物认证
requireBioId: true
}
};
try {
const response: did.GenerateKeyResponse =
await did.generateKey(context, generateKeyRequest);
// V哥提醒:公钥和证书链要上报应用云侧,由云侧完成公钥上链、换取 DID 文档
// 私钥留在 TEE 里,应用侧从头到尾摸不到
console.info('did key generated, certChain:', response.certChain);
} catch (error) {
const err = error as BusinessError;
console.error(`generateKey failed. Code: ${err.code}, message: ${err.message}`);
}
}
// 第二步:把云侧换来的 DID 文档导入设备
async function importDidDoc(context: common.UIAbilityContext): Promise<void> {
const importDidRequest: did.ImportDidRequest = {
isUpdate: false,
did: 'did:example:123456', // 云侧生成并返回的 DID 标识
didKeyList: [{ keyAlias: 'vgeWorkCardKey', keyId: 'keyId123' }],
didDoc: JSON.stringify({
'@context': 'https://www.w3.org/ns/did/v1',
id: 'did:example:123456'
// ... DID 文档其余内容由云侧下发
})
};
try {
await did.importDid(context, importDidRequest);
console.info('did imported');
} catch (error) {
const err = error as BusinessError;
console.error(`importDid failed. Code: ${err.code}, message: ${err.message}`);
}
}
两个细节值得停一下。其一,authenticatorConfig 里绑定生物认证------这一步决定了后面出示凭证时"本人同意"不是一句空话,而是拿指纹/人脸说了算。其二,did.sign 接口可以在已导入的 DID 密钥上做数据签名(官方能力之一),用户授权、数据签署都走它,私钥同样不出 TEE。
四、颁发:把 VC 灌进 TEE
启用身份后,应用从发行方获取可验证凭证(比如工作证凭证),通过 importDigitalCredential 导入设备,DID 服务验证凭证格式并在 TEE 中安全存储:
typescript
async function importCredential(context: common.UIAbilityContext): Promise<void> {
const request: did.ImportDigitalCredentialRequest = {
did: 'did:example:123456',
credentialType: did.CredentialType.VC,
isUpdate: false,
// credentialData 为发行方下发的 VC 内容,需符合官方规定的 VC 格式
credentialData: buildVcFromIssuer(),
// 显示配置:决定用户在系统界面里看到什么
displayConfig: {
credentialDisplayName: '工作证',
issuerDisplayName: 'V哥科技公司',
propertyDisplayName: '姓名'
},
securityConfig: {
authConfig: { requireAuth: true } // 后续使用需生物认证
}
};
try {
const response = await did.importDigitalCredential(context, request);
// V哥提醒:应用侧只拿得到凭证概要(credentialSummary),
// VC 本体存在 TEE 里------这就是"应用不碰原始证件数据"的落点
console.info('credential imported, summary:', response.credentialSummary);
} catch (error) {
const err = error as BusinessError;
console.error(`importDigitalCredential failed. Code: ${err.code}, message: ${err.message}`);
}
}
这里有一个官方明说的格式硬约束:数字身份服务仅支持解析两种格式的 VC (类型均为选择性披露凭证,签名类型分别为 SM3WithSM2 与 SM2Signature2024,并涉及默克尔根计算方式的选择)。多传不可识别的字段不会报错,但不会被解析。V哥的建议:VC 组装放在云侧发行方服务里做,格式对表官方开发指导里的两个样例,端侧只做透传------端侧写 JSON 组装逻辑,出了格式问题你连报错都难定位。
五、出示:本人同意 + 最小化披露,整条链的题眼
前面都是铺垫,这一步才是 DID 的灵魂。当应用作为验证方需要请求用户凭证时,官方流程是:应用云侧下发请求参数 → 构造 GetDigitalCredentialRequest 调用 getDigitalCredential → 用户确认出示的凭证及披露的属性字段后,数字身份服务将 VP 出示到验证方应用:
typescript
async function presentCredential(context: common.UIAbilityContext): Promise<void> {
const request: did.GetDigitalCredentialRequest = {
credentialType: did.CredentialType.VP,
// 展示给用户看的验证方信息与用途------本人同意的前提是知道"给谁看、干什么用"
displayConfig: {
verifierDisplayName: '访客系统',
purpose: '访客身份核验'
},
holderConfigList: [{
holderDid: 'did:example:123456',
holderDidKeyId: 'keyId123'
}],
credentialFilterList: [{
credentialId: 'credential123',
issuerDid: 'did:example:issuer'
}]
};
try {
const response = await did.getDigitalCredential(context, request);
// 系统在 TEE 中完成:生物认证授权 -> 按披露范围裁剪属性 -> 组装并签名 VP
// V哥拿到的只有 VP,裁掉了哪些字段,链路上无人知晓
handlePresentation(response);
} catch (error) {
const err = error as BusinessError;
console.error(`getDigitalCredential failed. Code: ${err.code}, message: ${err.message}`);
}
}
官方对这一步的描述有三个关键词,V哥逐个标注分量:
- 获取用户同意:不是应用调个弹窗意思一下,是数字身份服务层面的确认流程,用户能看到"出示什么、给谁看、披露哪些字段"。
- 生物认证授权出示:拿指纹/人脸做授权,出示动作本身和"本人"强绑定。
- 凭证的部分披露:VP 里只含被披露的属性。VP 同样只支持官方规定的两种格式,验证方解析时要对表。
V哥再给一个接入思路上的判断:这套能力最适合的接入位,是你业务里"本来就要收证件"的环节------酒店入住核验、访客登记、入职背书、年龄敏感服务。凡是过去靠"上传证件照"过审的流程,都是 DID 的候选改造点。反过来,纯粹为了炫技把 DID 套在无关环节上,只会在受限权限申请和云侧协议部署上白费功夫。
六、接入自检清单
V哥把整条链路压成六项,接 DID 前逐项对表:
| # | 检查项 | 依据 |
|---|---|---|
| 1 | HarmonyOS 7(API 26)+ 实际支持 ATL4 的机型? | 约束与限制 |
| 2 | 受限权限 ohos.permission.ACCESS_FIDO2_ONLINEAUTH 已申请? |
开发准备 |
| 3 | 应用云侧已部署符合 W3C DID 协议的服务? | 约束与限制 |
| 4 | VC/VP 组装与解析对表官方两种格式? | 开发步骤 |
| 5 | 出示环节的验证方名称与用途 displayConfig 写清楚? |
出示流程 |
| 6 | 个人信息上云前已明示并取得用户同意? | 约束与限制 |
最后说句实话:DID 不是一个人能落地的能力,它是端侧 TEE、云侧协议、发行方、验证方四方协奏。也正因为门槛在这里,它筛掉了一批只想"传张照片"的旧流程------能按 DID 标准把身份验证重构掉的业务,才有资格说自己在做隐私合规。
参考与出处
本文涉及的机制、流程、接口与约束,均来自以下华为开发者联盟官方文档:
最后一句:复印件思维的时代,证明自己是谁的办法是把证件全本交出去;DID 时代,你只需要让 TEE 替你说一句"是"------多一个字段都算系统失职,这才是数字身份该有的样子。