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


HarmonyOS 7 数字身份 DID 实战

引子:出示证件照这件事,我们交出去了多少多余信息

V哥先讲一个所有人都干过的事:注册某个服务,被要求"上传身份证照片"。照片拍完传上去,你交出去的是什么?姓名、性别、民族、出生日期、住址、身份证号、签发机关、有效期——全部。而那个业务真正要验证的,可能只有一句话:“这个人是不是成年人。”

一张证件照,交出去一百个字段,用上一个。多出来的九十九个去哪了?存在对方服务器里,存多久、谁能看、会不会被爬,你一概不知。V哥不是吓唬人,这是过去几十年"复印件思维"的通病:验证身份的唯一办法,就是把证件本身交出去。

HarmonyOS 7(API 26)给这个问题上了一个系统级解法——分布式数字身份 DID(Decentralized Identifier,去中心化身份)。官方新能力一览的原文是这么说的(HarmonyOS 7 新能力一览):

系统级的数字身份 DID 框架,通过 TEE 存储颁发用户的数字身份,使用时经本人同意后按需出示,证明身份且最小化证件隐私暴露。

V哥把这句话拆成三个词:TEE 颁发、本人同意、最小化出示。这也是本文标题的由来,更是这一期想讲透的主线:数字身份的要害不是"把证件数字化",而是最小化暴露——凭证存在 TEE 里,用的时候经本人同意、按需出示,应用自始至终碰不到原始证件数据。


一、先想明白:DID 到底换了什么

传统方式和 DID 方式的差别,V哥画一张链路图就清楚了:

DID 颁发出示链路

对应到官方能力,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 级别的认证可信等级。官方给的查询方式:

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 导入设备。核心代码(以官方开发步骤为骨架):

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 中安全存储

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(类型均为选择性披露凭证,签名类型分别为 SM3WithSM2SM2Signature2024,并涉及默克尔根计算方式的选择)。多传不可识别的字段不会报错,但不会被解析。V哥的建议:VC 组装放在云侧发行方服务里做,格式对表官方开发指导里的两个样例,端侧只做透传——端侧写 JSON 组装逻辑,出了格式问题你连报错都难定位。


五、出示:本人同意 + 最小化披露,整条链的题眼

前面都是铺垫,这一步才是 DID 的灵魂。当应用作为验证方需要请求用户凭证时,官方流程是:应用云侧下发请求参数 → 构造 GetDigitalCredentialRequest 调用 getDigitalCredential用户确认出示的凭证及披露的属性字段后,数字身份服务将 VP 出示到验证方应用:

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 前逐项对表:

#检查项依据
1HarmonyOS 7(API 26)+ 实际支持 ATL4 的机型?约束与限制
2受限权限 ohos.permission.ACCESS_FIDO2_ONLINEAUTH 已申请?开发准备
3应用云侧已部署符合 W3C DID 协议的服务?约束与限制
4VC/VP 组装与解析对表官方两种格式?开发步骤
5出示环节的验证方名称与用途 displayConfig 写清楚?出示流程
6个人信息上云前已明示并取得用户同意?约束与限制

最后说句实话:DID 不是一个人能落地的能力,它是端侧 TEE、云侧协议、发行方、验证方四方协奏。也正因为门槛在这里,它筛掉了一批只想"传张照片"的旧流程——能按 DID 标准把身份验证重构掉的业务,才有资格说自己在做隐私合规


参考与出处

本文涉及的机制、流程、接口与约束,均来自以下华为开发者联盟官方文档:


最后一句:复印件思维的时代,证明自己是谁的办法是把证件全本交出去;DID 时代,你只需要让 TEE 替你说一句"是"——多一个字段都算系统失职,这才是数字身份该有的样子。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐