My Title

WeIdentity文档

weidentity-logo.png

什么是 WeIdentity?

WeIdentity是一套分布式多中心的技术解决方案,可承载实体对象(人或者物)的现实身份与链上身份的可信映射、以及实现实体对象之间安全的访问授权与数据交换。WeIdentity由微众银行自主研发并完全开源,秉承公众联盟链整合资源、交换价值、服务公众的理念,致力于成为链接多个垂直行业领域的分布式商业基础设施,促进泛行业、跨机构、跨地域间的身份认证和数据合作。

1. 主要模块介绍

WeIdentity目前主要包含两大模块:WeIdentity DID以及WeIdentity Credential。

分布式身份标识 (WeIdentity DID)

传统方式中,用户的注册和身份管理完全依赖于单一中心的注册机构;随着分布式账本技术(例如区块链)的出现,分布式多中心的身份注册、标识和管理成为可能。 WeIdentity DID模块在FISCO-BCOS区块链底层平台上实现了一套符合W3C DID规范的分布式多中心的身份标识协议,使实体(人或物)的现实身份实现了链上的身份标识;同时,WeIdentity DID给与Entity(人或者物)直接拥有和控制自己身份ID的能力。

WeIdentity DID秉承以下设计理念:

目标 说明
多中心 分布式多中心的ID注册机制,摆脱对传统模式下单一中心ID注册的依赖
开源开放 技术方案完全开源,面向政府、企业、开发者服务
隐私保护 实体的现实身份和可验证数字凭证的内容进行链下存储。支持实体将信息最小化或者选择性披露给其他机构,同时防止任何第三方反向推测出实体在现实世界或其他场景语义中的身份
可移植性 基于WeIdentity规范,数据可移植至遵循同样规范的其他平台,兼容业务主流区块链底层平台
互操作性 提供标准化接口,支持跨链、跨平台互操作
可扩展性 保证操作性,可移植性或简单性的情况下,数据模型可以通过多种不同方式进行扩展

可验证数字凭证 (WeIdentity Credential)

现实世界中存在着各种各样用于描述实体身份、实体间关系的数据,如身份证、行驶证、存款证明、处方、毕业证、房产证、信用报告等。WeIdentity Credential提供了一整套基于W3C VC规范的解决方案,旨在对这一类数据进行标准化、电子化,生成可验证、可交换的「凭证」(Credential),支持对凭证的属性进行选择性披露,及生成链上存证(Evidence)。

WeIdentity支持认证机构自行注册标准化凭证模板,共同丰富公众联盟链的生态。

本页化繁为简地聊了聊WeIdentity,您可以在此快速了解WeIdentity的参考场景、体验Demo、快速部署并体验WeIdentity的核心功能。 如果您是开发人员,还可以进一步了解WeIdentity的参考实现,以及深入了解SDK的使用方式。

2. WeIdentity 参考场景

roles-relation.png

在WeIdentity生态中,存在着上图所示的几类角色,不同角色的权责和角色之间的关系如下表所示:

角色 说明
User (Entity) 用户(实体)。会在链上注册属于自己的WeIdentity DID,从Issuer处申请Credential,并授权转发或直接出示给Verifier来使用之。
Issuer Credential的发行者。会验证实体对WeIdentity DID的所有权,其次发行实体相关的Credential。
Verifier Credential的使用者。会验证实体对WeIdentity DID的所有权,其次在链上验证Credential的真实性,以便处理相关业务。
User Agent / Credential Repository 用户(实体)在此生成WeIdentity DID。为了便于使用,实体也可将自己的私钥、持有的Credential托管于此。

在实际业务里,WeIdentity可以被广泛运用在「实体身份标识」及「可信数据交换」场景中。首先,通过User Agent为不同的实体生成独立唯一的DID;其次,Issuer验证实体身份及DID所有权,为实体发行各种各样的电子化Credential。当实体需要办理业务的时候,可以直接将Credential出示给Verifier,也可以通过在链上进行主动授权 + 授权存证上链的方式,由之前授权的凭证存储机构转发给Verifier。

以上流程,保证了数据以实体用户为中心,同时实体身份、确权、授权等操作在链上完成,可追溯,可验证,不可篡改。

3. Demo体验

下面提供了几个不同场景的WeIdentity Demo:

使用场景 访问入口 设计说明
学历信息电子化 开始体验(目前暂时下线,暂停体验) 基于WeID,将用户身份同电子身份ID对应的学历信息电子化,Hash上链,保证身份和学历信息高效验证,不可篡改

4. 当前状态

WeIdentity目前支持基于FISCO-BCOS的区块链,并提供Java SDK及RestService方式供部署。具体的规范文档、安装部署和使用指引如下表所示:

集成方法 文档入口 当前状态
Java SDK

5. Getting Started

接下来,您可以在此页,从零开始安装,部署和使用WeIdentity。

联系我们

邮箱:weidentity@webank.com

如何贡献


weidentity-logo.png
什么是 WeIdentity?

WeIdentity是一套分布式多中心的技术解决方案,可承载实体对象(人或者物)的现实身份与链上身份的可信映射、以及实现实体对象之间安全的访问授权与数据交换。WeIdentity由微众银行自主研发并完全开源,秉承公众联盟链整合资源、交换价值、服务公众的理念,致力于成为链接多个垂直行业领域的分布式商业基础设施,促进泛行业、跨机构、跨地域间的身份认证和数据合作。

1. 主要模块介绍

WeIdentity目前主要包含两大模块:WeIdentity DID以及WeIdentity Credential。

分布式身份标识 (WeIdentity DID)

传统方式中,用户的注册和身份管理完全依赖于单一中心的注册机构;随着分布式账本技术(例如区块链)的出现,分布式多中心的身份注册、标识和管理成为可能。 WeIdentity DID模块在FISCO-BCOS区块链底层平台上实现了一套符合W3C DID规范的分布式多中心的身份标识协议,使实体(人或物)的现实身份实现了链上的身份标识;同时,WeIdentity DID给与Entity(人或者物)直接拥有和控制自己身份ID的能力。

WeIdentity DID秉承以下设计理念:

目标 说明
多中心 分布式多中心的ID注册机制,摆脱对传统模式下单一中心ID注册的依赖
开源开放 技术方案完全开源,面向政府、企业、开发者服务
隐私保护 实体的现实身份和可验证数字凭证的内容进行链下存储。支持实体将信息最小化或者选择性披露给其他机构,同时防止任何第三方反向推测出实体在现实世界或其他场景语义中的身份
可移植性 基于WeIdentity规范,数据可移植至遵循同样规范的其他平台,兼容业务主流区块链底层平台
互操作性 提供标准化接口,支持跨链、跨平台互操作
可扩展性 保证操作性,可移植性或简单性的情况下,数据模型可以通过多种不同方式进行扩展

可验证数字凭证 (WeIdentity Credential)

现实世界中存在着各种各样用于描述实体身份、实体间关系的数据,如身份证、行驶证、存款证明、处方、毕业证、房产证、信用报告等。WeIdentity Credential提供了一整套基于W3C VC规范的解决方案,旨在对这一类数据进行标准化、电子化,生成可验证、可交换的「凭证」(Credential),支持对凭证的属性进行选择性披露,及生成链上存证(Evidence)。

WeIdentity支持认证机构自行注册标准化凭证模板,共同丰富公众联盟链的生态。

本页化繁为简地聊了聊WeIdentity,您可以在此快速了解WeIdentity的参考场景、体验Demo、快速部署并体验WeIdentity的核心功能。 如果您是开发人员,还可以进一步了解WeIdentity的参考实现,以及深入了解SDK的使用方式。

2. WeIdentity 参考场景
roles-relation.png

在WeIdentity生态中,存在着上图所示的几类角色,不同角色的权责和角色之间的关系如下表所示:

角色 说明
User (Entity) 用户(实体)。会在链上注册属于自己的WeIdentity DID,从Issuer处申请Credential,并授权转发或直接出示给Verifier来使用之。
Issuer Credential的发行者。会验证实体对WeIdentity DID的所有权,其次发行实体相关的Credential。
Verifier Credential的使用者。会验证实体对WeIdentity DID的所有权,其次在链上验证Credential的真实性,以便处理相关业务。
User Agent / Credential Repository 用户(实体)在此生成WeIdentity DID。为了便于使用,实体也可将自己的私钥、持有的Credential托管于此。

在实际业务里,WeIdentity可以被广泛运用在「实体身份标识」及「可信数据交换」场景中。首先,通过User Agent为不同的实体生成独立唯一的DID;其次,Issuer验证实体身份及DID所有权,为实体发行各种各样的电子化Credential。当实体需要办理业务的时候,可以直接将Credential出示给Verifier,也可以通过在链上进行主动授权 + 授权存证上链的方式,由之前授权的凭证存储机构转发给Verifier。

以上流程,保证了数据以实体用户为中心,同时实体身份、确权、授权等操作在链上完成,可追溯,可验证,不可篡改。

3. Demo体验

下面提供了几个不同场景的WeIdentity Demo:

使用场景 访问入口 设计说明
学历信息电子化 开始体验(目前暂时下线,暂停体验) 基于WeID,将用户身份同电子身份ID对应的学历信息电子化,Hash上链,保证身份和学历信息高效验证,不可篡改
4. 当前状态

WeIdentity目前支持基于FISCO-BCOS的区块链,并提供Java SDK及RestService方式供部署。具体的规范文档、安装部署和使用指引如下表所示:

集成方法 文档入口 当前状态
Java SDK

5. Getting Started

接下来,您可以在此页,从零开始安装,部署和使用WeIdentity。

联系我们

邮箱:weidentity@webank.com

如何贡献
WeIdentity 入门
准备工作
配置 说明
操作系统 CentOS (7.2.* 64位)或Ubuntu(16.04 64位)。
FISCO-BCOS区块链环境 您需要有一套可以运行的FISCO-BCOS区块链环境,如果没有,可以参考「FISCO-BCOS 2.0节点安装方法」来搭建一套区块链环境。
JDK 要求Oracle JDK1.8+,如果使用 WeIdentity 过程中发现 JDK 版本导致的任何问题,推荐使用jdk8u141、jdk8u212、jdk8u231。JDK 跟 WeID 直接的兼容性,可见兼容性文档
网络连通 检查部署 WeIdentity JAVA SDK 的服务器是否能 telnet 通 FISCO BCOS 节点的 channel 端口(channel端口是什么,详见),若telnet不通,需要检查网络连通性和安全策略。
第1步:安装 WeIdentity 部署工具

您可以参照“安装 WeIdentity 部署工具”,安装 WeIdentity 部署工具,这是一个网页工具,可以通过工具可以完成WeIdentity的部署,同时也提供了一些其他功能。

第2步:使用 WeIdentity 部署工具完成部署

参照“配置教程”,完成配置和部署。

第3步:运行 Sample 代码,体验接口(可选)

注解

如果不想体验 Sample,可以直接跳过这一步

您可以参考“开发样例使用”,体验 WeIdentity 的各种接口。

第4步:在自己的 Java Service 中集成 WeIdentity Java SDK

参考:“集成 WeIdentity Java SDK”

注解

如果您是要的是其他语言而非 Java,可以参照RestService文档,通过 WeIdentity Rest Service 来调用相关的接口。

其他:WeIdentity Java SDK 接口文档

参考“Java SDK文档”,深入了解 WeIdentity 的接口调用。

其他: WeIdentity 部署工具其他功能介绍

参考“WeIdentity 部署工具使用简介”

其他: WeIdentity 命令行工具使用

我们提供了一些快捷工具,可以帮您快速体验 weid-java-sdk,请参考“WeIdentity JAVA SDK 便捷工具使用”

术语
术语 对应中文 说明
JSON-LD   详见:JSON-LD官网JSON-LD Wiki
Authorization 授权 详见:Authorization vs Authentication
Authentication 身份验证 详见:Authorization vs Authentication
Public Key 公钥 详见:Public Key Wiki定义
Private Key 私钥 详见(内容描述中包含Private Key):Public Key Wiki定义
Signature 数字签名 详见:Digital Signature Wiki定义
DID 分布式身份标识 W3C的DID规范定义的去中心化ID。详见:W3C DID规范
WeIdentity DID   分布式身份标识,简称WeID,WeIdentity的分布式多中心的ID注册机制下生成的实体的ID。符合W3C DID规范
WeIdentity Document   描述如何使用DID,至少包含了3个字段:披露的公钥列表;Authentication描述如何Authenticate;Service Endpoint
Claim 声明 对实体的一个声明或者主张,用于装载凭证(Credential)业务数据的字段,例如电子驾照的各项信息就是存在一个Claim结构中
WeIdentity Credential 可验证数字凭证 简称“凭证”,遵循W3C Verifiable Credential规范的电子凭证,可用来抽象现实世界凭证类的对象,一个Credential可以包含一个或者多个Claim。例如电子驾照,电子学历等
Verifiable Credential 同上 同上
Credential 同上 同上
Notification 通知 WeIdentity体系实现的通知机制
CPT 凭证的声明类型 Claim Protocol Type,不同的Issuer按业务场景需要,各自定义不同类型数据结构的Claim,各种各样的Claim用不同的CPT来定义
chain-id 链 ID 用于路由到不同的链网络(如果需要跟其他链打通,需要找 WeIdentity 开源项目的 owner 微众银行注册路由信息),例如同时使用 WeIdentity 的可能有多条区块链,可以使用这个字段作为标识信息,路由到特定区块链
Service Endpoint 服务端点 Entity暴露的服务的地址,例如暴露自己的Credential托管的服务地址,暴露自己的文件存储服务地址
publish 发布 一个Issuer(包括Authority Issuer)发行一种新的CPT,这个动作称为publish
issue 发行 一个Issuer(包括Authority Issuer)按照某种CPT定义的格式,发行Credential,这个动作叫issue
payload 有效载荷 Notification机制里,用以描述业务相关信息的字段
Trusted Data 可信数据 由权威机构发行的与实体关联的数据,通过摘要算法与签名算法,在保证数据隐私前提下,实现数据不可篡改与可验证
Issuer 凭证发行者 任意拥有WeIdentity DID的Entity都可以作为Issuer来发行Credential
Authority Issuer 权威凭证发行者 对凭证或者用户的申明(Claim)进行认证的权威机构或者可信机构,例如给居民下发驾照的交通事务局,为学生授予大学毕业证书的大学,发行房产证的房屋管理部门,都属于权威凭证发行者。在你自己构建的 WeID 世界中,总有一些机构因为其权威性,可以为某一些信息背书,从而让大家都相信其发行的凭证(Credential),这类机构都属于权威凭证发行者
Entity 实体 WeIdentity Document或Credential描述的实体,即拥有WeIdentity DID的人或者物
Verifier 凭证验证者 使用凭证的第三方,会验证这个凭证是否经过权威机构认证,例如劳工局办理业务时需要用户提供驾照,这时劳工局就是Verifier
Data Repository 数据托管机构 数据的托管机构,例如用户把Credential托管在某个用户身份管理APP里面
User Agent 用户代理 用户私钥托管机构,例如某个用户身份管理APP。依据业务场景需求,可以是云端保存机制的,也可以是客服端本地保存机制的
Publisher CPT发布者 发布一个新的CPT的角色,被称为Publisher
Committee Member 委员会机构成员 管理Authority Issuer的委员会机构的成员
Specific Issuer 特定类型的发行者 指定了特定类型的发行者,如学校、政府机构等,与Authority Issuer相互不矛盾
Verifiable Presentation   遵循W3C Verifiable Presentation规范的凭证打包集。一个Presentation可以包括一个或多个Credential。
Policy   机构指定的Verifiable Presentation验证策略,例如:至少包括哪些Credential;每个Credential的Claim中属性项对应要求等
使用场景
WeIdentity使用场景及解决方案概述

「人是社会关系的总和」。通过WeIdentity对实体进行DID、凭证(Credential)的生成及绑定,可以更加准确完善地描述实体身份、实体间关系,并有效提高实体间信息流转的真实性和效率。

一般而言,使用WeIdentity的业务流程是:

  1. 为参与业务的相关机构搭建区块链。
  2. 对人及相关机构进行KYC(可选),生成WeIdentity DID并上链。
  3. 对业务中需要的各类证明生成可验证数字凭证(Credential),通过区块链技术的私钥管理、及不可篡改的数字化证明特性,进行可信登记、授权流转及真实性验证。
案例:新入职员工背景调查
  • 背景

合作方是一家中小企业,在招聘员工时需要对员工的学历信息、之前雇主信息进行真实性验证。存在的问题是:对员工而言,需要去每个机构花费大量时间精力获取最新版的材料。对企业而言,材料的获取和流转的过程中可能遭到篡改,而且缺乏验证材料真实性的手段。

  • 参与方
    • 员工
    • 学校
    • 前雇主公司
    • 现雇主公司
  • WeIdentity解决方案及基本流程
  1. 员工、学校、公司分别进行WeIdentity DID注册及KYC认证。
  2. 员工向学校学校申请学历证明凭证(Credential)、学位证明凭证(Credential)。
  3. 员工向前雇主公司申请工作证明凭证(Credential)、离职证明凭证(Credential)。
  4. 员工将这些凭证(Credential)挂到自己的个人主页上,或者直接提交给现雇主公司。
  5. 现雇主公司通过凭证验证(Verify)接口对上述凭证(Credential)进行验证。
  6. 验证通过,现雇主公司发放入职offer。
案例:医疗检查结果与处方可信流转
  • 背景

合作方包括了若干家医院和药店,在患者诊疗或购买处方类药品时,医院和药店可能需要参考其他医院所开具的处方、检查结果,保险机构则需要根据这些数据进行理赔。存在的问题是:对于患者而言,需要妥善分类并保管每张诊疗结果的单据,否则补办单据将会是一个非常复杂耗时的过程。对医院和药店而言,不同医院的诊疗结果和处方是非标准化的,而且诊疗结果和处方在流转过程中可能遭到篡改,缺乏验证其真实性的手段。

  • 参与方
    • 患者
    • 医院
    • 药店
    • 保险机构
  • WeIdentity解决方案及基本流程
  1. 患者、医院、药店分别进行WeIdentity DID注册及KYC认证。
  2. 患者就诊过程中,医院对患者生成各项医疗检查凭证(Credential),并开具处方凭证(Credential)。
  3. 患者将这些凭证保存在本地数据库里(如手机等移动存储媒介)。
  4. 患者在其他医院进行复诊时,可直接出示以上凭证(Credential),不需出示纸质检查结果及处方。
  5. 其他医院可通过凭证验证(Verify)接口进行验证,并在此基础上提供医疗服务。
  6. 患者在药店开药时,亦可出示以上凭证(Credential)并由药店通过凭证验证(Verify)接口进行验证。
  7. 验证通过,药店开药。
  8. 保险机构亦可通过凭证验证(Verify)接口进行验证,验证通过,为患者进行保险理赔。

处方凭证Claim标准格式范例:

属性 说明 属性 说明
name 患者姓名 gender 患者性别
department 科室 drugs 药品列表及用量
syndrome 症状描述 diagnosis 诊断
doctor 开方医师 DID hospital 医院 DID
案例:选择性披露
  • 背景

合作方为某娱乐机构。此机构需要年满18岁方可入场消费。某个实体人想进娱乐机构消费,但他不想暴露自己的真实姓名等隐私信息。

  • 参与方
    • 实体人
    • 娱乐机构
  • WeIdentity解决方案及基本流程
  1. 实体人及娱乐机构进行WeIdentity DID注册及KYC认证。
  2. 实体人向娱乐机构进行选择性披露,只披露KYC认证(Credential)的DID、有效期、出生日期等信息,隐藏其他信息。
  3. 娱乐机构通过凭证验证(Verify)接口对选择性披露的数据进行验证。
  4. 验证通过,允许进入。
案例:政务办理
  • 背景

居民政务数据存在于不同部门,跨部门的政务办理往往需要先至部门A开具证明,再至部门B进行办理。对居民而言,流程繁琐且文件不易管理与保存;对政府部门而言,希望提升用户体验并确保用户隐私数据不泄露。通过WeIdentity解决方案,可以为居民生成可信的电子证件,居民授权后由机构进行验证,从而简化业务流程,降低隐私数据泄露风险。

  • 参与方
    • 居民
    • 身份证明机构
    • 证件签发机构
    • 证件验证机构
  • WeIdentity解决方案及基本流程
  1. 由身份证明机构为居民进行WeIdentity DID注册及KYC认证。
  2. 居民向证件签发机构申请证明文件,证明签发机构按照规范生成电子凭证(Credential)并关联到居民的WeIdentity DID
  3. 居民授权证明验证机构对凭证(Credential)进行验证;同时生成一条居民授权记录,存储在区块链上
  4. 证明验证机构通过凭证验证(Verify)接口进行验证
  5. 验证通过,为居民进行业务办理
案例:慈善公益
  • 背景

目前不同公益平台间数据互通存在成本,当捐助者自身有求助、募款或其他需要自证的需求时,难以形成完整的用户公益履历。通过WeIdentity解决方案,可帮助用户快速建立自身完整的公益履历与画像,促进公益行业的发展

  • 参与方
    • 用户
    • 身份证明机构
    • 公益机构
    • 公益信息验证机构
  • WeIdentity解决方案及基本流程
  1. 在身份证明机构、公益机构、公益信息验证机构间搭建区块链网络,机构作为节点接入并注册WeIdentity DID
  2. 由身份证明机构为用户进行KYC并生成WeIdentity DID
  3. 用户向公益机构申请,由公益机构按照规范生成公益信息凭证并关联到用户的WeIdentity DID
  4. 用户可选择匿名捐赠,不出示KYC凭证
  5. 用户可授权另一家公益机构对其公益凭证进行验证,验证通过可提高用户在该机构的公信力,从而为用户提供更精准的服务
  6. 用户可授权企业、院校或政府单位对其公益凭证进行验证,验证通过对用户的履历进行加分
案例:数据共享
  • 背景

当前,不同机构间存在着大量用户数据流通的需求。然而,由于各个机构之间通常难以组建有效的信任合作机制,因此,各机构间难以将各自保管的用户数据安全可信地授权共享给其他机构。通过WeIdentity解决方案,可帮助机构间进行可信数据授权及共享,使得各机构可基于全面的数据为用户提供更高质量的服务。

  • 参与方
    • 用户
    • 数据持有机构
    • 数据使用机构
    • 身份证明机构
  • WeIdentity解决方案及基本流程
  1. 在身份证明机构、数据持有机构、数据使用机构间搭建区块链网络,机构作为节点接入并注册WeIdentity DID
  2. 由身份证明机构为用户进行KYC并生成WeIdentity DID
  3. 用户授权数据使用机构使用自己的数据
  4. 由身份证明机构为用户生成授权凭证(Credential),标明授权对象、数据属主、有效期、授权内容等属性,并使用用户私钥进行签名;身份证明机构可选择将授权凭证生成摘要并写入区块链,达到增信目的
  5. 数据使用机构出示授权凭证给数据持有机构
  6. 数据持有机构通过凭证验证(Verify)接口,验证授权凭证
  7. 验证通过,数据持有机构将数据发送给数据使用机构
WeIdentity 规范
版本 说明 作者
V0.1.0 完成整体协议框架 张俊麒
V0.2.0 完善流程,ER等 张俊麒
V0.3.0 添加选择性披露,Notification机制 张俊麒
V0.3.1 补充WeIdentity Document,增加CPT字段定义 陈浩
V0.3.2 描述修改 张俊麒
V0.3.3 补充图解及撤销部分实现 张俊麒,胡朝新
1. 设计目标
目标 说明
多中心 分布式多中心的ID注册机制,摆脱对传统模式下单一中心ID注册的依赖
开源开放 技术方案完全开源,面向政府、企业、开发者服务
隐私保护 实体的现实身份和可验证数字凭证的内容进行链下存储。支持实体将信息最小化或者选择性披露给其他机构,同时防止任何第三方反向推测出实体在现实世界或其他场景语义中的身份
可移植性 基于WeIdentity规范,数据可移植至遵循同样规范的其他平台,兼容业务主流区块链底层平台
互操作性 提供标准化接口,支持跨链、跨平台互操作
可扩展性 保证操作性,可移植性或简单性的情况下,数据模型可以通过多种不同方式进行扩展

2. 相关术语

详见:术语表

3. WeIdentity DID
WeIdentity DID与WeIdentity Credential的关系 weidentity-er.png

从图中可见,WeIdentity DID与WeIdentity Credential的关系并非单纯的一对多:从设计目标上看,WeIdentity DID用来描述实体(人或物),WeIdentity Credential用来描述实体的身份、属性和实体间关系。因此,一个WeIdentity DID可以持有多个WeIdentity Credential;而一个WeIdentity Credential则会包含至少一个所描述的WeIdentity DID,可能会有多个。最后,每个WeIdentity DID都有一个WeIdentity Document,用来存储此DID的认证方式(如公钥、私钥套件)等信息,与WeIdentity Credential无关。

总体流程 overall-flow.png

一般来说,WeIdentity解决方案的基本流程如下:

  1. 用户根据业务需求,选择是否需要进行KYC认证。
  2. 用户生成WeIdentity DID。
  3. 用户向相关业务方申请Credential。
  4. 相关业务方扮演Issuer的角色,发行Credential交给用户。
  5. 用户成为了Credential的Holder。
  6. 用户出示Credential,以完成业务需求。
  7. 相关业务方扮演Verifier的角色,验证Credential有效性。
如何生成WeIdentity DID

WeIdentity DID = did:weid:chain-id:bs-specific-string

weidentity-did-format1.png
字段 说明
did 遵循DID规范,使用固定字符“did”
weid WeIdentity DID规范的method name字段,固定为“weid”
chain-id 链 ID,用于路由到不同的链网络(如果需要跟其他链打通,需要找 WeIdentity 开源项目的 owner 微众银行注册路由信息),例如同时使用 WeIdentity 的可能有多条区块链,可以使用这个字段作为标识信息,路由到特定区块链
bs-specific-string 基于底层区块链平台生成,代表Entity在链上的地址,保证全网唯一

备注:bsSpecificString根据区块链底层平台和业务具体情况来确定生成规则,例如可以是随机字符串,或者区块链上的地址。

示例(这个例子中,chain-id是``101``: "did:weid:101:0x0086eb1f712ebc6f1c276e12ec21"

WeIdentity Document格式
字段 说明
@context 用于描述WeIdentity Document结构等信息
id WeIdentity DID,表示当前Document描述的Entity,用于自描述
created Document的创建时间
updated Document的更新时间
publicKey 公钥数组列表,格式如下
publicKey.id 公钥的ID
publicKey.type 用于指定signature suite
publicKey.owner 指定控制对应私钥的Entity,遵从WeIdentity规范,如果为空,则表明owner是Document的id字段,如果是Credential类Entity,则owner一般是某用户
authentication 用于Entity证明其与当前Document的关联性
authentication.type 用于指定signature suite
authentication.publicKey 用来验证签名的公钥,引用publicKey数组里定义的公钥
service service描述数组,用于描述跟当前DID相关的服务,格式如下
service.id service endpoint的ID
service.type service endpoint的协议
service.serviceEndpoint serviceEndpoint列表,可以是URI或者一个JSON-LD对象
service.其他 待定
recovery WeIdentity DID私钥丢失后,可由本字段指定的WeIdentity进行公私钥重置。是否需要抽象一层合约层来实现待定
  • WeIdentity DID的Authorization机制及Recovery机制由补充规范实现,不在本文中定义。

示例:

{
  "@context": "https://weidentity.webank.com/did/v1",
  "id": "did:weid:1:123456789abcdefghi",
  "created": "2017-09-24T17:00:00Z",
  "updated": "2018-09-24T02:41:00Z",
  "publicKey": [{
    "id": "did:example:123456789abcdefghi#keys-1",
    "type": "RsaVerificationKey2018",
    "owner": "did:example:123456789abcdefghi",
    "publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
  }, {
    "id": "did:example:123456789abcdefghi#keys-2",
    "type": "Secp256k1VerificationKey2018",
    "owner": "did:example:123456789abcdefghi",
    "publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71"
  }],
  "authentication": [{
    // this key can be used to authenticate as DID ...9938
    "type": "RsaSignatureAuthentication2018",
    "publicKey": "did:weid:1:123456789abcdefghi#keys-1"
    }
  ],
  "service": [{
    "type": "drivingCardService",
    "serviceEndpoint": "https://weidentity.webank.com/endpoint/8377464"
    }, {
    "type": "padiCertificateService",
    "serviceEndpoint": "https://weidentity.webank.com/endpoint/8377465"
  }],
  "recovery": ["did:weid:1:2323e3e3dweweewew2","did:weid:1:2323e3e3dweweewew3"],
}
WeIdentity DID支持操作
创建

创建一个WeIdentity DID(以及对应的WeIdentity Document)

读取/验证

通过WeIdentity DID读取到WeIdentity Document,并验证

更新

更新WeIdentity Document的相关属性

4. WeIdentity Credential
角色和关系 roles-relation.png

如图所示在WeIdentity生态中,存在着以下角色:

角色 说明
User (Entity) 用户(实体)。会注册属于自己的WeIdentity DID,申请Credential,并通过出示给相关业务方来使用之。
Issuer Credential的发行者。会首先验证实体对WeIdentity DID的所有权,其次发行Credential交给实体。
Verifier Credential的使用者。会首先验证实体对WeIdentity DID的所有权,其次验证Credential的有效性。
User Agent / Credential Repository 用户(实体)在此生成WeIdentity DID。为了便于使用,实体也可将自己的私钥、持有的Credential托管于此。
场景 scenario.png

上图展示了五个WeIdentity生态下Credential在不同角色间流转的场景:

  1. 身份证明机构作为Issuer向用户发行「实名认证Credential」,政府机构作为Verifier在办理公共事务时对其进行验证。
  2. 学校作为Issuer向用户发行「学历证明Credential」,公司作为Verifier在对候选人进行背景调查时对其进行验证。
  3. 出入境机构作为Issuer向用户发行「签证Credential」,海关作为Verifier在出入境时对其进行验证。
  4. 公司作为Issuer向用户发行「收入证明Credential」,银行作为Verifier在发放贷款时对其进行验证。
  5. 商户作为Issuer向用户发行「优惠券Credential」,商户自己作为Verifier在对优惠券核销时对其进行验证。

更多使用场景,可见: 使用场景文档

Credential结构
属性 说明
@context 用于描述Credential的字段信息等
id 本Credential的ID,按UUID生成
issuer Issuer的DID,遵从WeIdentity规范
issued issue日期
claim Claim的具体细节,数据结构由CPT定义,详见CPT介绍
claim.primeNumberIdx 素数在素数表中的index
claim.type Claim Protocol Type的ID,申请按序递增,例如中国内地驾照设置为CPT100
revocation 撤销相关实现,待补充
proof Issuer的签名列表,是一个数组,可由holder和issuer分别打上签名
proof.type 签名类型
proof.created 签名的创建时间
proof.creator 签名机构的WeIdentity DID
proof.salt 盐值,选择性披露时用于保证不被类似彩虹表的方式反向破解 hash 算法
proof.signatureValue 签名的具体value,对整个Credential结构中除去signature字段的其他字段做签名
区块链上的Credential结构
属性 说明
id 同上
type 同上
issued 同上
claimHash Claim结构内容的hash
revocation 同上
signature 同上
{
  "claim": {
     "age": 1,
     "gender": "F",
     "id": "did:weid:101:0xd6f4d1215c52ee7e7975ac946a0e094040aa5eeb",
     "name": "1"
  },
  "context": "https://github.com/WeBankBlockchain/WeIdentity/blob/master/context/v1",
  "cptId": 2000006,
  "expirationDate": "2020-09-24T14:34:44Z",
  "id": "f894d33d-88c3-4122-afb6-87e21d2ae656",
  "issuanceDate": "2020-09-23T14:34:44Z",
  "issuer": "did:weid:101:0xd6f4d1215c52ee7e7975ac946a0e094040aa5eeb",
  "proof": {
     "created": "2020-09-23T14:34:44Z",
     "creator": "did:weid:101:0xd6f4d1215c52ee7e7975ac946a0e094040aa5eeb#keys-0",
     "salt": {
        "age": "IObiF",
        "gender": "GdzQs",
        "id": "dDIIt",
        "name": "PecuG"
     },
     "signatureValue": "/T21Vb5aw2dyB5kgh5LQmMHCQcx7MLJvFLf0g+EPkJVwoFH7+tPwokNBBHjQWn3BWLORDHJcbsUo+6OrRgTp8wA=",
     "type": "Secp256k1"
   },
  "type": [
     "VerifiableCredential",
     "original"
  ],
  "$from": "toJson"
 }
Claim Protocol Type(CPT)注册机制

不同的Issuer按业务场景需要,各自定义不同类型数据结构的Claim,所有的Claim结构都需要到CPT合约注册,以保证全网唯一。所有的CPT定义文件(JSON-LD格式)可以从CPT合约下载。

  • CPT格式
属性 说明
@context 用于描述CPT等信息
id CPT的编号,要保证唯一
cptType Credential 的类型,目前有三种:lite1,original,zkp
title CPT的名字,例如“FISCO BCOS 高级工程师证书”,或者“驾照”
description CPT的描述信息
properties Claim 结构的说明
created 创建时间
updated 更新时间
version CPT的版本号
publisher CPT的发布者的WeIdentity DID
proof CPT的发布者的签名
  • CPT例子
{
   "$context" : "http://json-schema.org/draft-04/schema#",
   "cptType" : "original",
   "description" : "学历证书说明",
   "title" : "学历证书",
   "type" : "object",
   "properties" : {
     "id" : {
       "description" : "学生证id",
       "type" : "string"
     },
     "name" : {
       "description" : "名字",
       "type" : "string"
     }
   }
 }
cpt-er.png

其中CPT为模板类,定义了Claim包含的数据字段及各字段属性要求。Claim为CPT的实例。Issuer将Claim进行签名,即可生成Credential。

Claim示例参考:

Claim实例

Credential操作
创建Credential

任何实体都可以issue一个Credential。

验证Credential

通过这个接口,一个Entity可以对一个Credential进行验证。

存储/提取Credential

Credential的holder可以转移这个Credential,或者提取完整的Credential私下存储。

撤销 Credential

Credential的Issuer可以撤销这个Credential。

选择性披露
如何为Credential生成签名

Issuer生成Credential签名的过程:

  1. Claim中的每个字段计算生成一个对应的hash值。
  2. 将Claim中的每个字段的hash值以某种形式拼接起来形成一个字符串Claim_Hash,然后跟Credential原有的其他字段组成一个新的用于计算hash的Credential结构。
  3. 对这个包含Claim_Hash的Credential结构计算hash,得到Credential Hash。
  4. 使用Private Key对这个Credential Hash进行签名,得到签名的值Signature。
sign-credential.png
如何验证选择性披露的Credential

用户选择需要披露的字段集合(可以是一个或者几个字段,这个例子中是Field_1),需要披露的字段提供原文,其他字段提供hash值。将包含这个Claim结构的Credential披露给Verifier。下面是Verifier验证的过程:

  1. Verifier从Credential提取用户披露的Claim字段。
  2. Verifier对用户披露的字段分别计算hash(这个例子中是Field_1,计算出Field_1_hash),然后得到一个包含所有字段hash值的Claim结构。
  3. 对这个Claim结构中的每个字段的hash值以某种形式拼接起来形成一个字符串Claim_Hash,然后跟Credential原有的其他字段组成一个新的用于计算hash的Credential结构。
  4. 对这个包含Claim_Hash的Credential结构计算hash,得到Credential Hash。
  5. 使用Credential的Signature和Issuer的public key进行decrypt,得到一个签名的计算值。
  6. 比较Credential的Signature与签名的计算值,看是否相等,确认这个Credential的合法性。
verify-credential.png
Credential撤销
撤销如何工作

Credential撤销机制利用了下面两点:

  1. 任意大于1的整数 a,如果 a 不是素数,则 a 可以表示为一系列素数的乘积,且这个表示是唯一的(不考虑顺序)。参见:算数基本定理
  2. 目前没有一个有效率的算法,对两个足够大的素数乘积得到的半素数(semiprime)进行整数分解。参见:整数分解

WeIdentity 会公开一个大素数的文件,每个素数会有一个 index,供所有 Issuer 使用。Issuer 发行一个 Credential 的时候,就随机从这个大素数文件中选择一个素数,这个素数的 index 会作为这个 Credential 的属性之一。并把以往所有发行的未撤销的 Credential 的素数相乘,得到一个大数 Accumulator(每个 Issuer 会维护自己的 Accumulator),并将这个 Accumulator 公开供所有接入方查询。

Issuer 如何撤销一个 Credential

用这个 Credential 对应的大素数去除 Issuer 自己的 Accumulator,将结果更新为新的 Accumulator。

Verifier 如何验证一个 Credential 有效(未被撤销)

用这个 Credential 对应的大素数去除 Credential 的 Issuer 公开的 Accumulator,如果能整除,则表明是 Credential 是有效的(未被撤销)。

before-revocation.png after-revocation.png
5. Authority Issuer注册

链上每个WeIdentity DID持有者(包括人,机构,或者物)都可以issue一个Claim,并打上自己的签名,但只有联盟链里面的权威机构才可以在公共合约注册成为Authority Issuer。每个注册为Authority Issuer的新成员需要联盟链内其他机构投票通过。

字段 说明
version version描述
id Authority Issuer的WeIdentity DID,符合WeIdentity规范的ID
name Authority Issuer机构的名字
created 创建日期
updated 更新日期
publicKey Authority Issuer公开的公钥列表
validCrenRef Authority Issuer公布的自己的当前所有未撤销的credential的primeNumber字段的累加乘积
6. Notification

通用字段如下,根据不同的type,notification的payload结构不同。

字段 说明
type 通知的类型:register, update
weid notification描述的实体的WeIdentity DID
payload Notification机制里,用以描述业务相关信息的字段
WeIdentity DID注册事件的通知

当创建WeIdentity DID时,注册了该notification的机构会收到这个通知,表明有新的WeIdentity DID被注册。

字段
type register
  • payload结构:

暂定为NULL。

WeIdentity DID属性修改的通知

当WeIdentity DID的属性有更新时,注册了该notification的机构会收到这个通知,例如public key改变或者其他meta data值改变。

字段
type document_mod,表示WeIdentity Document有修改
  • payload结构(此payload是一个数组,以支持多个属性变更的通知。):
字段 说明
operation add, update 或者 remove
field 变动的字段的key
original 原值
new 新值
Notification携带业务数据

用于传输小段数据,例如机构间传递Credential的明文可以通过这类通知,payload由机构双方自行约定结构。

字段
type transportation
Notification订阅

支持对感兴趣的通知进行注册。依赖于实现。

WeIdentity Sample 开发样例

weid-sample 是基于 WeIdentity 开发的 Java 应用样例程序,提供了一整套的流程演示,可以帮您快速理解 WeIdentity 的运行机制,您也可以参考该样例程序,开发您的 Java 应用。

环境准备

您可以参考 WeIdentity 安装部署文档准备工作进行准备。

快速体验

我们提供以下方式体验 weid-sample:

体验使用WeIdentity Sample
前提条件

运行 WeIdentity-Sample 需要提前使用 WeIdentity 部署工具完成部署,请参考WeIdentity JAVA SDK安装部署完成部署,并参照Java应用集成章节完成 weid-sample 的配置。

1. 配置与部署
1.1 下载 WeIdentity-Sample 源码:
git clone https://github.com/WeBankBlockchain/WeIdentity-Sample

注解

如果您的服务器在中国内地,下载速度可能会比较慢,可以直接使用在国内的源:git clone https://gitee.com/WeBank/WeIdentity-Sample

1.2 配置基本信息

若您在体验WeIdentity Sample之前已经完成WeIdentity Build Tool的部署和配置,weid-sample会自动从Build Tool中加载基本配置信息,无需您再次进行配置。

若您想单独体验WeIdentity Sample, 您可以参考部署weid-java-sdk与配置基本信息进行配置。

  • 编译 WeIdentity-Sample

如果您是第一次运行 WeIdentity-Sample,您需要先进行编译:

chmod +x build.sh
./build.sh
2. Swagger服务方式体验接口

使用 spring-boot 方式,weid-sample 程序将作为一个后台进程运行,您可以使用swagger可视化地体验交互流程。

2.1 启用服务
chmod +x build.sh start.sh stop.sh
./start.sh

若启动成功,则会打印以下信息:

[main] INFO  AnnotationMBeanExporter() - Registering beans for JMX exposure on startup
[main] INFO  Http11NioProtocol() - Initializing ProtocolHandler ["https-jsse-nio-6101"]
[main] INFO  Http11NioProtocol() - Starting ProtocolHandler ["https-jsse-nio-6100"]
[main] INFO  NioSelectorPool() - Using a shared selector for servlet write/read
[main] INFO  Http11NioProtocol() - Initializing ProtocolHandler ["http-nio-6101"]
[main] INFO  NioSelectorPool() - Using a shared selector for servlet write/read
[main] INFO  Http11NioProtocol() - Starting ProtocolHandler ["http-nio-6101"]
[main] INFO  TomcatEmbeddedServletContainer() - Tomcat started on port(s): 6100 (https) 6101 (http)
[main] INFO  SampleApp() - Started SampleApp in 3.588 seconds (JVM running for 4.294)
2.2 流程演示

以下将为您演示 假设您的服务部署在本地,地址是 127.0.0.1,服务端口是 6101。您可以在 resources/ 里修改端口信息。 您可以使用浏览器打开http://127.0.0.1:6101/swagger-ui.html,通过可视化的方式体验WeIdentity的核心功能。

  • 创建 WeID

单击``/step1/issuer/createWeId``,创建WeID,并返回结果。

若调用成功,则会显示以下信息:

_images/weid-sample-springboot-1.png

表明创建的 WeID 是 did:weid:1:0xbb96163789a4e16790f3d213319bd4cf2b517582。

  • 注册 Cpt

单击``/step2/registCpt``,参数里的 publisher 传入step1刚刚注册的WeID

运行成功,则会打印以下信息:

_images/weid-sample-springboot-2.png

表明注册 CPT 成功,CPT ID 为 2000000。

  • 创建 Credential

单击``/step3/createCredential``,修改参数``claimData``为具体值,参数issuer为step1的WeID,参数cptId为step2返回的Cpt ID

运行成功,则会打印以下信息:

_images/weid-sample-springboot-3.png

表明创建 Credential 成功,Credential 的具体信息为图中的 credential 字段对应的内容。

  • 验证 Credential

单击``/step1/verifyCredential``,修改参数为上步所得到的``credential``。

若运行成功,则会打印以下信息:

_images/weid-sample-springboot-4.png

表明 Credential 验证成功。

至此,您已经体验了 weid-sample 实现的各个角色的运行流程,实现的入口类在weid-sample工程的 com.webank.weid.demo.server.SampleApp,您可以参考进行您的 Java 应用开发。

3. 命令行方式使用

命令行方式比较完整的模拟了各个 WeIdentity 角色的工作流程,可以帮您快速体验 WeIdentity 也业务流程和运行机制。 各个角色的基本流程如下:

  • Issuer
创建 WeID
注册成为 Authority Issuer
注册 CPT
创建 Credential
  • User Agent
创建 WeID
创建 Presentation
打包 Presentation 成 QRcode 或者 Json 串,发送给 Verifier
  • Verifier
获取 User Agent 的 Presentation
验证 Presentation
3.1 基本流程的演示
  • Issuer 操作流程演示
chmod +x command.sh
./command.sh issuer

若运行成功,则会打印包括创建 WeID、注册成为 Authority Issuer、注册 CPT 和创建 Credential 等运行流程。

以下为截取的部分流程日志:

--------- start issuer ----------
issuer() init...

begin to createWeId...

createWeId result:

result:(com.webank.weid.protocol.response.CreateWeIdDataResult)
weId: did:weid:1:0x7a276b294ecf0eb7b917765f308f024af2c99a38
userWeIdPublicKey:(com.webank.weid.protocol.base.WeIdPublicKey)
    publicKey: 1443108387689714733821851716463554592846955595194902087319775398382966796515741745
    951182105547115313067791999154982272567881519406873966935891855085705784
userWeIdPrivateKey:(com.webank.weid.protocol.base.WeIdPrivateKey)
    privateKey: 46686865859949148045125507514815998920467147178097685958028816903332430030079
errorCode: 0
errorMessage: success
transactionInfo:(com.webank.weid.protocol.response.TransactionInfo)
blockNumber: 2098
transactionHash: 0x20fc5c2730e4636248b121d31ffdbf7fa12e95185068fc1dea060d1afa9d554e
transactionIndex: 0

begin to setPublicKey...

setPublicKey result:

result: true
errorCode: 0
errorMessage: success
transactionInfo:(com.webank.weid.protocol.response.TransactionInfo)
blockNumber: 2099
transactionHash: 0x498d2bfd2d8ffa297af699c788e80de1bd51c255a7365307624637ae5a42f3a1
transactionIndex: 0
  • User Agent 操作流程演示
./command.sh user_agent

运行成功,则会打印包括创建 WeID、创建 Presentation 以及打包 Presentation 成 QRcode 或者 Json 串的流程。 以下为截取的部分日志:

--------- start User Agent ----------
userAgent() init...

begin to create weId for useragent...

createWeId result:

result:(com.webank.weid.protocol.response.CreateWeIdDataResult)
weId: did:weid:1:0x38198689923961e8ecd6d57d88d027b1a6d1daf2
userWeIdPublicKey:(com.webank.weid.protocol.base.WeIdPublicKey)
    publicKey: 12409513077193959265896252693672990701614851618753940603742819290794422690048786166
    777486244492302423653282585338774488347536362368216536452956852123869456
userWeIdPrivateKey:(com.webank.weid.protocol.base.WeIdPrivateKey)
    privateKey: 11700070604387246310492373601720779844791990854359896181912833510050901695117
errorCode: 0
errorMessage: success
transactionInfo:(com.webank.weid.protocol.response.TransactionInfo)
blockNumber: 2107
transactionHash: 0x2474141b82c367d8d5770a7f4d124aeaf985e7fa3e3e2f7f98eeed3d38d862f5
transactionIndex: 0
  • Verifier 操作流程演示
./command.sh verifier

运行成功,则会打印 Verifier 反序列化 Presentation 以及验证 Presentation 的过程。 以下为截取的部分日志,详细流程可以参考代码实现:

--------- start verifier ----------
verifier() init...

------------------------------

begin create weid for verifier...

createWeId result:

result:(com.webank.weid.protocol.response.CreateWeIdDataResult)
    weId: did:weid:1:0xc43f2c19d118069334465203caec2f172b309c58
    userWeIdPublicKey:(com.webank.weid.protocol.base.WeIdPublicKey)
        publicKey: 1802001392887294114478621319460626832326728735808626637646481738691052543569123247811055025421632020659858167535619017862031831947976217438376528638044178
    userWeIdPrivateKey:(com.webank.weid.protocol.base.WeIdPrivateKey)
        privateKey: 18729487184487047589926382583327624427891635082897243001876050275017499781990
errorCode: 0
errorMessage: success
transactionInfo:(com.webank.weid.protocol.response.TransactionInfo)
    blockNumber: 63
    transactionHash: 0xe76321d5778ed627f2dd051eb327e7dc5190180013691ef73b21b5c264fffad8
    transactionIndex: 0

------------------------------

begin get the presentation json...

至此,您已经体验了 WeIdentity-Sample 实现的各个角色的运行流程,实现的入口类在 WeIdentity-Sample 工程的 com.webank.weid.demo.command.DemoCommand,您可以参考进行您的 Java 应用开发。

部署 weid-java-sdk 与配置基本信息
配置 Committee Member 私钥

注解

此项配置并非必要。由于注册 Authority Issuer 需要委员会机构成员( Committee Member )权限,若您不是发布智能合约的机构,您无需关注此配置项。 若您是智能合约发布的机构,您可以参考以下进行配置:

将您在部署WeIdentity智能合约阶段生成的私钥文件拷贝至 weid-sample/keys/priv/ 目录中,此私钥后续将用于注册 Authority Issuer,weid-sample 会自动加载。

配置weidentity.properties
cd weid-sample
vim resources/weidentity.properties

关键配置如下:

blockchain.orgid :机构名称。样例以 organizationA 为例,请修改为 organizationA。
nodes :区块链节点信息。你可以修改为您区块链网络中的任一节点即可。

配置样例:

blockchain.orgid=organizationA
nodes=10.10.10.10:20200
配置节点证书和密钥文件
cd resources/

FISCO BCOS 2.0请参考2.0 web3sdk客户端配置,将证书文件 ca.crtnode.crtnode.key 复制出来,拷贝至当前目录下。

FAQ

技术问题列表:


  • “把数据主权回归用户”体现在哪些方面?

数据主权意味着用户对数据的控制,主要体现在下面几个方面:

  1. 用户控制自己的数据存储和交换过程。
  2. 用户身份及与其相关的凭证、数据都是可移植的。
  3. 用户可通过授权方式决定谁有权限访问自己的数据。
  4. 针对某个具体的结构化数据,用户可选择性披露其中的某些字段,而数据验证方能判断整个结构化数据的有效性与真实性。

  • 如果要用接入KYC,需要接入什么机构?现在已经接入了该机构吗?

WeIdentity项目中提及的KYC过程分为两步:

  1. 将现实生活中的人与现实生活中的号码关联起来,比如人和身份证号、人和银行卡号、人和社交网站账号等,这个过程一般由权威机构或企业完成,比如公安局、商业银行、运营方等;
  2. 将现实生活中的号码与WeIdentity DID关联起来,比如社交网站账号和WeIdentity DID,这个过程在第1步完成后,由对应的机构来完成;

WeIdentity并不会改变现实世界中KYC的流程,而是与之结合,具体是否需要接入、以及接入什么机构,由业务场景决定,WeIdentity并不会为业务场景分派或指定特定的机构。


  • WeIdentity能提供哪些方面的凭证?这些凭证分别依托什么样的权威机构?举例说明?

WeIdentity项目设计了一套完整的凭证定义机制,目标是为了将现实世界中所有的凭证生成对应的WeIdentity凭证。现实世界中的凭证分为两大类:

  1. 权威型凭证:身份证、驾照、护照、学位证、银行交易流水证明、电子处方等,这些凭证的信息均可电子化、结构化表示出来,并且加上权威机构的数字签名即可生成WeIdentity凭证;
  2. 自定义凭证:授权凭证、请柬、借据等等,这些凭证与权威型凭证的区别仅仅在于数字签名是由个人签发的,而非权威机构签发;

因此,WeIdentity是依托于现实世界的凭证来生成的,具体能提供哪些凭证、这些凭证依托于什么权威机构是依托于现实世界的规则。


  • WeIdentity能否用于“存证/供应链/贸易/文化/游戏”等场景的应用,怎么用?

WeIdentity能用于一切涉及到身份认证、数据交换的场景,具体用法需深入结合对应场景的需求。


  • 如果详细的数据放到链下,怎么获取详细数据?

涉及隐私的数据都是在链外私有存储,获取详细数据有两种模式:

  1. 用户通过User Agent提供的服务下载详细数据,线上传输或线下二维码模式提交给数据使用方;
  2. 用户通过User Agent授权数据访问,数据使用方与数据提供方建立数据传输通道进行数据传输。

  • 新用户/新机构怎么登记到链上?

新用户不直接与链交互,而是通过User Agent代理接入上链; 新机构登记上链需考虑业务场景以及对应角色,有以下建议:

  1. 业务发起方机构需直接部署节点组链,并为其他没有部署节点的机构提供开放平台的接入方式;
  2. 其它业务参与方机构可在获得业务发起方允许的情况下,参与到联盟链中并部署区块链节点,或通过业务发起方提供的开放平台方式接入联盟链。

  • 如何保证权威机构的“权威性”,例如:能够长期地、稳定地提供准确的证明和相关数据?如果不能提供,怎么治理?

WeIdentity项目中提及的权威机构的定义一般都源自现实生活,其“权威性”来自于现实生活中的人对其的肯定与信任、政府部门对其职能的划分与管理,从而使其能长期、稳定的提供相关服务。当其职能发生变化时,WeIdentity应用场景的参与方也需要在达成某种业务共识的前提下作出相应的变更。


  • 有哪些机构可以参与共识,机构参与的判定标准是什么?

由具体业务场景和机构所扮演的角色共同决定,根据场景需要,数据提供方、数据使用方、用户代理都可以参与共识。


  • 生成的WeIdentity DID如何进行数据迁移?
  1. WeIdentity SDK提供数据导出接口,可以以json格式导出用户(人或者物)的WeIdentity Document完整数据,供User Agent数据迁移时使用;
  2. WeIdentity SDK也提供数据导入接口,从其他业务平台导出的数据,可以在支持WeIdentity的业务平台导入,即按照提供的WeIdentity Document和公私钥,重新在链上创建WeIdentity Document。当用户需要将自己的数据从一个业务平台导出到另外一个业务平台,或者运行WeIdentity的联盟链需要做整体数据迁移的时候,可以使用上面提到的数据导入导出接口。例如某用户想将业务平台A的数据迁移到业务平台B,则业务平台A可以跟业务平台B基于自己的业务需要,基于业务规则,业务平台A和业务平台B通过接口对接,进行相应的数据迁移。

  • 生成的Credential如何进行数据迁移?

WeIdentity项目中提及的Credential是遵循W3C Verifiable Credential规范实现的,根据不同的业务场景,用户选择不同的机构进行私有存储。WeIdentity项目会实现统一的“Credential导出”与“Credential导入”接口,在得到用户授权的情况下,机构可调用相应的接口实现Credential的导出与导入,从而实现数据迁移。


  • 是否支持批量存储?

目前没有批量接口。后续我们将会提供相关批量接口。


  • WeIdentity DID和标准化DID协议的异同是什么?

WeIdentity DID基于W3C的DID规范,实现了一套分布式多中心的身份标识协议(需依托分布式账本作为WeIdentity的底层运行平台),使实体(人或物)的可以在分布式账本上标识和鉴权其身份;目前W3C DID规范在发展,WeIdentity DID也在发展。


  • CPT到底是什么,能否举一个详细的例子?如何生成我自定义的CPT?

CPT(Claim Protocol Type,也即凭证声明类型)可以理解为各类凭证(Credential)的模板定义结构,例如驾照这类Credential的数据格式跟学位证这类Credential的数据格式肯定是不一样的,所以各类Credential需要定义自己的CPT类型。当机构想在WeIdentity生态发行一种凭证(例如某公司想基于WeIdentity发行员工使用的门禁卡),则可以定义这种门禁卡的数据格式,即定义一种CPT,然后注册到WeIdentity。


  • 获取详细数据时如何进行鉴权,以确认确实是已经授权过的请求?

在可信数据交换的场景下,当某机构A需要使用用户X在机构B处存储的数据M。机构A可以获取用户的授权Credential(可以通过 CPT101 来实现,也可以自行定义符合自己业务需求的 CPT )。

可信数据交换规范定义了数据获取接口和数据授权提供接口的规范。 机构B需要实现数据授权提供接口,机构A通过数据获取接口向机构B请求这个数据,请求中携带用户授权数据使用的Credential,机构B验证Credential正确后返回数据。


  • 如果一个发证机构的私钥丢失,要怎么处理?个人的私钥丢失呢?

机构和个人的WeIdentity DID都通过私钥来控制,如果私钥丢失,需要通过Recovery机制来支持重置WeIdentity的公钥。Recovery机制要求WeIdentity DID持有人设置相应的恢复人。未来会支持恢复人的多种模式,例如某个列表中任意一个恢复人可以恢复,或者需要收集列表中至少N个签名才可以恢复。


  • 已经发出的凭证(Credential)如何撤销和重新生成?

WeIdentity Credential支持撤销操作,由发行这个Credential的机构或者人来执行。 Credential的重新生成则为重新发行一个Credential ID不相同的全新Credential。同时Credential支持更新和过期时间延期等操作。


  • Credential(凭证)有没有伪造的可能,如何防伪造?

目前 Credential(凭证) 使用 ECDSA 签名,未来会支持 RSA。链上证明被伪造的可能性,即特定长度密匙的 ECDSA 或 RSA 私钥被攻破的可能性。在私钥不泄露,且密匙未被攻破的情况下,目前不存在被伪造的可能性。

注:


  • 在 FISCO-BCOS 上部署 WeIdentity,部署区块链节点的机器需要准备多大的硬盘?

详见 WeIdentity 存储容量预估


  • 如何给生成的PDF文档安装字体?

字体下载

您可从此:下载NotoSansCJKtc-Regular.ttf字体

CentOS

  1. 新建中文字体目录并拷贝字体到该目录

    sudo mkdir -p /usr/share/fonts/chinese
    sudo cp ./NotoSansCJKtc-Regular.ttf  /usr/share/fonts/chinese
    
  2. 安装相关依赖

    sudo yum -y install fontconfig ttmkfdir mkfontscale
    
  3. 安装字体

    sudo mkfontscale&&
    sudo mkfontdir&&
    sudo fc-cache -fv
    
  4. 查看字体是否成功安装

    fc-list
    

Ubuntu

  1. 新建中文字体目录并拷贝字体到该目录

    sudo mkdir -p /usr/share/fonts/chinese
    sudo cp ./NotoSansCJKtc-Regular.ttf  /usr/share/fonts/chinese
    
  2. 安装相关依赖

    sudo apt install xfonts-utils -y
    
  3. 安装字体

    sudo mkfontscale
    sudo mkfontdir
    sudo fc-cache -fv
    
  4. 查看字体是否成功安装

    fc-list
    

Window

  1. 双击字体文件进行安装即可。(window10可能存在安装字体后程序仍然报字体错误情况,此时右键点击字体文件选择为所有用户安装)

  • 项目启动报错问题集及解决方法

问题一

报错如下:

Exception in thread "main" java.lang.IncompatibleClassChangeError: class com.github.fge.jackson.JsonNumEquals has interface com.google.common.base.Equivalence as super class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.github.fge.jsonschema.core.keyword.syntax.checkers.common.EnumSyntaxChecker.<clinit>(EnumSyntaxChecker.java:46)
at com.github.fge.jsonschema.core.keyword.syntax.dictionaries.CommonSyntaxCheckerDictionary.<clinit>(CommonSyntaxCheckerDictionary.java:152)
at com.github.fge.jsonschema.core.keyword.syntax.dictionaries.DraftV3SyntaxCheckerDictionary.<clinit>(DraftV3SyntaxCheckerDictionary.java:55)
at com.github.fge.jsonschema.library.DraftV3Library.<clinit>(DraftV3Library.java:32)
at com.github.fge.jsonschema.cfg.ValidationConfigurationBuilder.<clinit>(ValidationConfigurationBuilder.java:63)
at com.github.fge.jsonschema.cfg.ValidationConfiguration.newBuilder(ValidationConfiguration.java:92)
at com.github.fge.jsonschema.cfg.ValidationConfiguration.byDefault(ValidationConfiguration.java:102)
at com.github.fge.jsonschema.main.JsonSchemaFactoryBuilder.<init>(JsonSchemaFactoryBuilder.java:68)
at com.github.fge.jsonschema.main.JsonSchemaFactory.newBuilder(JsonSchemaFactory.java:123)
at com.github.fge.jsonschema.main.JsonSchemaFactory.byDefault(JsonSchemaFactory.java:113)
at com.webank.weid.util.DataToolUtils.isValidJsonSchema(DataToolUtils.java:451)
at com.webank.weid.util.DataToolUtils.isCptJsonSchemaValid(DataToolUtils.java:465)
at com.webank.weid.service.impl.CptServiceImpl.validateCptJsonSchemaMap(CptServiceImpl.java:358)
at com.webank.weid.service.impl.CptServiceImpl.validateCptArgs(CptServiceImpl.java:325)
at com.webank.weid.service.impl.CptServiceImpl.registerCpt(CptServiceImpl.java:167)
at Issuer.main(Issuer.java:49)

该错误是由于包冲突引起,可尝试如下解决方法:

用IDE的类查找工具在项目中查找Equivalence类。如Idea使用快捷键Ctrl+N,再输入Equivalence,点击查找。
发现项目内有两个相同的类名,源于两个不同版本的guava的jar包,随后在pom.xml文件里注释其中一个依赖即可解决问题
(注:项目为maven才会有pom.xml文件)。

问题二

部署智能合约报错如下:

2020-09-24 15:27:47.275 [http-nio-6021-exec-10] ERROR DeployContractV2() - RoleController deploy exception
org.fisco.bcos.web3j.protocol.exceptions.TransactionException: Transaction receipt timeout.
at org.fisco.bcos.web3j.tx.Contract.executeTransaction(Contract.java:420) ~[web3sdk-2.4.1.jar:?]
at org.fisco.bcos.web3j.tx.Contract.create(Contract.java:498) ~[web3sdk-2.4.1.jar:?]
at org.fisco.bcos.web3j.tx.Contract.deploy(Contract.java:533) ~[web3sdk-2.4.1.jar:?]
at org.fisco.bcos.web3j.tx.Contract.lambda$deployRemoteCall$6(Contract.java:687) ~[web3sdk-2.4.1.jar:?]
at org.fisco.bcos.web3j.protocol.core.RemoteCall.send(RemoteCall.java:28) ~[web3sdk-2.4.1.jar:?]
at com.webank.weid.contract.deploy.v2.DeployContractV2.deployRoleControllerContracts(DeployContractV2.java:167) [weid-java-sdk-1.6.6.jar:?]
at com.webank.weid.contract.deploy.v2.DeployContractV2.deployContract(DeployContractV2.java:129) [weid-java-sdk-1.6.6.jar:?]
at com.webank.weid.service.DeployService.deploy(DeployService.java:110) [weid-build-tools-1.0.19.jar:?]
at com.webank.weid.controller.BuildToolController.deploy(BuildToolController.java:223) [weid-build-tools-1.0.19.jar:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_161]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_161]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_161]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_161]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-embed-websocket-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at com.webank.weid.filter.XssFilter.doFilter(XssFilter.java:46) [weid-build-tools-1.0.19.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at com.webank.weid.filter.BuildToolFilter.doFilter(BuildToolFilter.java:85) [weid-build-tools-1.0.19.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.29.jar:9.0.29]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_161]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_161]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.29.jar:9.0.29]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_161]
2020-09-24 15:27:47.346 [http-nio-6021-exec-10] ERROR DeployContractV2() - WeIdContract deploy error.
java.lang.NumberFormatException: Zero length BigInteger
at java.math.BigInteger.<init>(BigInteger.java:420) ~[?:1.8.0_161]
at org.fisco.bcos.web3j.utils.Numeric.toBigIntNoPrefix(Numeric.java:105) ~[web3sdk-2.4.1.jar:?]
at org.fisco.bcos.web3j.utils.Numeric.toBigInt(Numeric.java:101) ~[web3sdk-2.4.1.jar:?]
at org.fisco.bcos.web3j.abi.datatypes.Address.<init>(Address.java:26) ~[web3sdk-2.4.1.jar:?]
at com.webank.weid.contract.v2.WeIdContract.deploy(WeIdContract.java:399) ~[weid-contract-java-1.2.24.jar:?]
at com.webank.weid.contract.deploy.v2.DeployContractV2.deployWeIdContract(DeployContractV2.java:182) [weid-java-sdk-1.6.6.jar:?]
at com.webank.weid.contract.deploy.v2.DeployContractV2.deployContract(DeployContractV2.java:130) [weid-java-sdk-1.6.6.jar:?]
at com.webank.weid.service.DeployService.deploy(DeployService.java:110) [weid-build-tools-1.0.19.jar:?]
at com.webank.weid.controller.BuildToolController.deploy(BuildToolController.java:223) [weid-build-tools-1.0.19.jar:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_161]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_161]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_161]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_161]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) [spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-embed-websocket-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at com.webank.weid.filter.XssFilter.doFilter(XssFilter.java:46) [weid-build-tools-1.0.19.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at com.webank.weid.filter.BuildToolFilter.doFilter(BuildToolFilter.java:85) [weid-build-tools-1.0.19.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.29.jar:9.0.29]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_161]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_161]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.29.jar:9.0.29]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_161]

该错误是由于第一个合约部署失败导致整个部署失败,可尝试如下解决方法:

核实配置的群组是否正常共识出块, 如果不能共识出块请解决群组共识出块问题即可解决问题

  • 在 FISCO-BCOS 上部署 WeIdentity,WeIdentity常用接口的性能数据是?

详见 WeIdentity 性能数据


  • Evidence 链上结构

Evidence的key是数据的hash值(通过WeIdentity SDK提供的sha3函数计算得出)。

字段 类型 说明
hash bytes32[] 存证数据的 hash,同时也是key
signer address[] 签名人的地址(WeAddress)
sigs string[] 对应每个签名人的签名值
logs string[] 每一个 Evidence 可以附加额外数据,放入 logs 字段
updated uint256[] 更新时间
extraKey string[] Evidence 的另外一个key,用户可以设置一个额外的key,用于查询这个evidence(效率会低于通过hash查询)
  • 例子
"hash": "0x64e604787cbf194841e7b68d7cd28786f6c9a0a3ab9f8b0a0e87cb4387ab0107"

//hash 和extraKey是Evidence的key,Evidence的value在链上的结构类似下面

{
"signer": ["did:weid:1000:0x4d3091830e74235a9c2e2041700c162ff75cc13d"],
"logs": [
    "tempLog",
    "tempLog",
    "tempLog",
    "tempLog",
    "tempLog",
    "tempLog"
],
"signature": ["AELc1QRvC+OEwwIjzZ6KrffiHpTFoxanq29H6KO3juV1NKg5Ip59/c/8pgwISVNEV8mXaqhYVf2o\b0JuyZCc0f5Q="],
"updated": "1590136873"
}

  • CredentialService和CredentialPojoService的区别在哪?
  1. CredentialService相比CredentialPojoService是比较老的方式,现在使用的都是CredentialPojoService,后续版本会去掉CredentialService,请使用CredentialPojoService来创建凭证。
  2. CredentialPojo支持嵌套、Lite化、Presentation、PDF/二维码生成、零知识证明、时间戳服务等,前者不支持。
  3. AmopService中的requestIssueCredential方法是零知识证明使用环节中需要的一步,如果是普通的Credential 请使用CredentialPojoService来创建来创建。

  • 注意配置MYSQL的最大链接数

如果MYSQL有设置最大链接数,请注意将连接数设置为:业务系统连接池maxActive + SDK的maxActive。否则有可能出现数据库锁表的情况。


  • 联盟链管理员丢失了私钥怎么办?

在使用可视化工具过程中,如果联盟链管理员丢失了私钥,那当前的智能合约部分需要管理员才具有的写权限就丢失了,可以重新配置并部署智能合约,具体操作如下:

完成此操作之前请确保 output/admin 目录中存在新的私钥,如果没有新私钥,请先通过’WeIdentity 部署工具’生成一个新的管理员私钥。

1.清理历史数据

从’WeIdentity 部署工具’的根目录进入 tools/ 目录。

  • 注意,执行下面的命令会清理掉链上 CNS 和智能合约的数据,等于让之前的所有的数据都失效了(不能再通过CNS访问,而只能通过合约地址访问)。
  cd tools
  chmod u+x upgrade_databucket.sh
  ./upgrade_databucket.sh
  1. 删除 weid-build-tools 指引文件。
  cd ..
  rm -r output/other/guide
  1. 重启服务
  ./stop.sh
  ./start.sh
  1. 重新进入网页,即可从 0 开始。

WeIdentity RestService

WeIdentity RestService提供了简化的WeIdentity集成方式与访问能力。使用者仅需将RestService部署到一台Java的机器并连接到FISCO-BCOS区块链上,便可以通过HTTP/HTTPS协议访问WeIdentity的功能了。请参见:

部署RestService

您可以访问详细的 部署指引文档,按照文中步骤部署RestService到您的Server上。

WeIdentity RestService 部署文档
1. Server 部署说明
1.1 环境要求

Server 的环境要求与 WeIdentity-Java-SDK 的 环境要求 类似,但它不需要 fisco-solc 编译环境:

物料 版本 说明
CentOS/Ubuntu 7.2 / 16.04,64位 部署 RestServer 用
JDK 1.8+ 推荐使用 1.8u141 及以上
FISCO-BCOS 节点 1.3.8(即中央仓库的1.2.5)或 2.x 确保它可以和部署 Server 机器互相连通,可 telnet 其 channelPort 端口
Gradle 4.6+ 同时支持 4.x 和 5.x 版本的 Gradle
MySQL 5 + 需要MySQL存储必要的链上数据进行缓存
1.2 生成安装包

您可以从 GitHub下载 RestService 的源代码,并进行编译以生成安装包(默认置于 /dist 目录下):

$ git clone https://github.com/WeBankBlockchain/WeIdentity-Rest-Service.git
$ cd WeIdentity-Rest-Service
$ gradle build -x test
$ cd dist

默认为develop分支。您可以生成如下结构的安装包:

└─ dist
  ├─ app: 启动jar包
  ├─ lib: 依赖库
  ├─ conf: 配置文件
  ├─ keys/priv: 托管私钥
  ├─ server_status.sh:监控系统运行状态
  ├─ start.sh:启动RestServer
  └─ stop.sh:停止RestServer
1.3 修改配置文件
  • 首先,确认 WeIdentity 合约已部署完毕,同时您所部署的 FISCO-BCOS 节点可以正常连通。目前支持 2.x 的 FISCO-BCOS 节点。
  • 拷贝节点证书。您需要将节点的 ca.crt 、节点SDK目录下的 node.crt、node.key 拷贝到 dist/conf 目录下。
  • 修改合约地址。如果您使用部署工具部署了 WeIdentity 合约,那么只需将部署工具生成的 fisco.propertiesweidentity.properties 拷贝到 dist/conf 目录下即可。如果您使用源码部署,请手动修改 dist/conf/fisco.properties.tpldist/conf/weidentity.properties.tpl ,更新合约地址及区块链节点信息;修改完成后,将两个文件的子扩展名 .tpl 去掉。详情:

合约地址修改示例。更新 dist/conf/fisco.properties 下列属性中cns.contract.follow的值。

cns.contract.follow=0x161bcbd5afbdd2bb2c7f6cc31ed5897f041271c8c984284239370c1572e8545d

区块链节点信息修改示例:更新 dist/conf/weidentity.propertiesnodes 项的值,注意每一条信息都应包含区块链用户、节点IP、节点channel端口地址;多于一个区块链节点,请用 “,” 半角逗号分隔。

nodes=127.0.0.1:8812,127.0.0.1:8900
  • 拷贝您 WeIdentity 合约部署者的私钥到 keys/priv 目录下,并重命名为 ecdsa_key。如果您使用部署工具部署了 WeIdentity 合约,这个文件在 output/admin/ 目录。如果您使用源码部署,这个文件在源代码根目录下。
  • 修改 dist/conf/application.properties ,填入需要打开的监听端口地址(用于 RestServer 监听外来的 HTTP/HTTPS RESTful 请求,默认为 6001,不可被其他程序占用)。
# 默认的HTTP/HTTPS请求端口
server.port=6001
# 当开启HTTPS时的HTTP请求端口,默认未实现重定向,可忽略
server.http.port=6000
  • 如果您需要启用HTTPS,dist/conf/application.properties 中,请将SSL开启,同时配置服务端证书并将证书的详细信息填入以下配置项中。如果您暂时没有合适的证书,当前,Rest Service已经默认提供了一份自签(Self-Signed)证书,其keystore默认密码也已给出。
# 是否启用HTTPS:默认为否,需要改成true
server.ssl.enabled=true
# 证书的keystore
server.ssl.key-store=classpath:tomcat.keystore
# keystore的访问密码,默认的自签证书密码为123456
server.ssl.key-store-password=
# keystore种类(如JKS,PKCS12)
server.ssl.keyStoreType=JKS
# key的假名
server.ssl.keyAlias=tomcat

注解

当前,Rest Service 不论是 HTTP/HTTPS 方式,其访问 IP 均为 6001。出于安全考量,我们暂时未实现在启用 HTTPS 方式时的 HTTP 访问重定向功能。

注解

如果您使用了自签证书,且准备通过使用 Postman 作为客户端访问 HTTPS,您需要在 Postman 的设置 File -> Setting -> General 中,手动将 SSL certificate verification 关闭;如果您使用 CA 签名证书,则需要在 Postman 的设置菜单 File -> Setting -> Certificates 中,安装此证书(及其证书链)。

注解

关于如何生成您自己的自签名证书,可以参考以下文档:https://hutter.io/2016/02/09/java-create-self-signed-ssl-certificates-for-tomcat/ 。本教程不涉生成 CA 证书的步骤。

  • 同时,请在 dist/conf/application.properties 中确认用来调用默认合约部署者私钥的暗语;由于此暗语可直接调用 WeIdentity 合约部署者的私钥,权限较高(详见 RestService API 说明文档),因此请您务必对其进行修改。
# 合约部署者私钥暗语。改成admin,您就可以使用此来调用合约部署者的私钥发交易了。
default.passphrase=admin
  • 最后,如果您需要连接使用MySQL,则需要在``dist/conf/weidentity.properties``内修改关于datasource相关的MySQL配置。
2. Server 使用说明
2.1 Server 启动/停止

进入 dist 目录,执行以下命令以启动或停止 Rest Server:

# 为脚本文件增加权限
$ chmod +x start.sh server_status.sh stop.sh
# 启动应用
$ ./start.sh
# 观察应用状态
$ ./server_status.sh
# 停止应用
$ ./stop.sh

执行 ./start.sh 之后会输出以下提示,表示 RestServer 已经顺利启动:

========================================================
Starting com.webank.weid.http.Application ... [SUCCESS]
========================================================

请您通过执行 ./server_status.sh 确认 RestServer 已经成功启动:

========================================================
com.webank.weid.http.Application is running(PID=100891)
========================================================

如果需要停止服务,请执行 ./stop.sh ,之后会输出以下提示,表示 RestServer 已经顺利停止:

========================================================
Stopping com.webank.weid.http.Application ... [SUCCESS]
========================================================
3. 使用 Postman 访问 RestServer 的 API

RestServer 支持任何使用标准 HTTP/HTTPS 协议的 RESTful API 客户端访问,详细接口说明可见 API 文档。我们提供了一套 Postman 的环境与请求集供快速集成。使用步骤如下:

  • 点击Postman的Import按钮,导入环境文件 weidentity-restservice.postman_environment.json 和请求集 invoke.postman_collection.json 。这两个文件可以在 GitHub代码仓库的 对应目录下找到

  • 确认 weidentity-restservice 这个环境文件已导入成功,它包含两个环境变量 hosthttpport
    • 修改环境变量 host 属性的值为安装部署 RestServer 的服务器地址
    • 修改环境变量 httpport 属性的值配置文件中的 Server 监听端口地址
  • 接下来确认 Invoke 这个命令集已导入成功。如果成功,可以从侧边栏中看到

  • 现在,可以调用 Invoke 这个命令集中的各类API了。您可以从无参数请求 CreateWeId 开始,看看返回结果是不是和 API 文档中一致,成功创建了一个 WeIdentity DID。

使用RestService

您可以访问具体的 API接口文档,了解如何通过HTTP/HTTPS请求,访问WeIdentity的功能。

WeIdentity RestService API 说明文档
基于私钥托管模式的RestService API
1. 总体介绍

每个API的入参都是满足以下格式的json字符串:

{
    "functionArg": 随调用SDK方法而变的入参json字符串 {
        ...
    },
    "transactionArg": 交易参数json字符串 {
        "invokerWeId": 用于索引私钥的WeIdentity DID,服务器端会凭此找到所托管的私钥(非必须)
    }
    "functionName": 调用SDK方法名
    "v": API版本号
}

参数说明:

  • functionArg是随不同的SDK调用方法而变的,具体的参数可以查看SDK接口文档;后文会为每个所提及的接口给出对应的链接

  • transactionArg仅包括一个变量invokerWeId,由传入方决定使用在服务器端托管的具体哪个WeIdentity DID所对应的私钥
    • 非必需,只有在那些需要使用不同身份进行写交易签名的方法(如CreateAuthorityIssuer等)才会需要;后文中详细说明
  • functionName是调用的SDK方法名,用于决定具体调用WeIdentity Java SDK的什么功能

  • v是调用的API方法版本

每个API的接口返回都是满足以下格式的json字符串:

{
    "respBody": 随调用SDK方法而变的输出值json字符串 {
    }
    "ErrorCode": 错误码
    "ErrorMessage": 错误信息,成功时为"success"
}

其中具体的输出值result亦是随不同的SDK调用方法而变的。

在后文中,我们将会逐一说明目前所提供的功能及其使用方式。

2. 创建WeIdentity DID(无参创建方式)

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName createWeId Y
functionArg   Y
transactionArg   Y
v 版本号 Y

注解

基于托管模式的RestService不允许通过传入公钥参数的方式创建WeID。这是因为,这样生成的WeID私钥不存在于RestService本地,因此无法在后续过程中被调用。如果您需要通过传入公钥方式创建WeID,请使用基于轻客户端方式的接口创建。

接口入参示例:

{
    "functionArg": {
    },
    "transactionArg": {
    },
    "functionName": "createWeId",
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody WeIdentity DID

返回示例:

{
    "ErrorCode": 0,
    "ErrorMessage": "success",
    "respBody": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
}
3. 获取WeIdentity DID Document

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName getWeIdDocument Y
functionArg   Y
functionArg.weId WeIdentity DID,与SDK直接调用的方式入参要求一致,下同 Y
transactionArg   N,传空
v 版本号 Y

接口入参示例:

{
    "functionArg": {
        "weId": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
    },
    "transactionArg": {
    },
    "functionName": "getWeIdDocument",
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody WeIdentity DID Document

返回示例:

{
    "respBody": {
        "@context" : "https://w3id.org/did/v1",
        "id" : "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
        "created" : 1553224394993,
        "updated" : 1553224394993,
        "publicKey" : [ ],
        "authentication" : [ ],
        "service" : [ ]
    },
    "ErrorCode": 0,
    "ErrorMessage": "success"
}
4. 创建AuthorityIssuer

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName registerAuthorityIssuer Y
functionArg   Y
functionArg.weId WeIdentity DID,与SDK直接调用的方式入参一致,下同 Y
functionArg.name 机构名 Y
transactionArg   Y
transactionArg.invokerWeId 用于索引私钥的WeIdentity DID,服务器端会凭此找到所托管的私钥。注意:如果在这里填入了预先定义在application.properties里的暗语,则可确保有足够的权限。 Y
v 版本号 Y

接口调用示例:

{
    "functionArg": {
        "weid": "did:weid:0x1Ae5b88d37327830307ab8da0ec5D8E8692A35D3",
        "name": "Sample College"
    },
    "transactionArg": {
        "invokerWeId": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
    },
    "functionName": "registerAuthorityIssuer",
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody True/False

返回示例:

{
    "ErrorCode": 0,
    "ErrorMessage": "success",
    "respBody": True
}
5. 查询AuthorityIssuer

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName queryAuthorityIssuer Y
functionArg   Y
functionArg.weId WeIdentity DID,与SDK直接调用的方式入参一致,下同 Y
transactionArg   N,传空
v 版本号 Y

接口入参示例:

{
    "functionArg": {
        "weId": "did:weid:0x1ae5b88d37327830307ab8da0ec5d8e8692a35d3"
    },
    "transactionArg": {
    },
    "functionName": "queryAuthorityIssuer",
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody 完整的Authority Issuer信息
{
    "respBody": {
        "accValue": ,
        "created": 16845611984115,
        "name": "Sample College",
        "weid": "did:weid:0x1ae5b88d37327830307ab8da0ec5d8e8692a35d3"
    }
    "ErrorCode": 0
    "ErrorMessage": "success"
}
6. 创建CPT

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName registerCpt Y
functionArg   Y
functionArg.cptJsonSchema CPT Json Schema,与SDK直接调用的方式入参一致,下同 Y
functionArg.weId CPT创建者 Y
transactionArg   Y
transactionArg.invokerWeId 用于索引私钥的WeIdentity DID,服务器端会凭此找到所托管的私钥 Y
v 版本号 Y
CPT Json Schema是什么?应该满足什么格式?

答:Json Schema是一种用来定义Json字符串格式的Json字符串,它定义了CPT应包括的字段、属性及规则。
WeIdentity可以接受 http://json-schema.org/draft-04/schema# 所定义第四版及之前版本作为入参。

接口入参示例:

{
  "functionArg": {
      "weId": "did:weid:0x1ae5b88d37327830307ab8da0ec5d8e8692a35d3",
      "cptJsonSchema":{
          "title": "cpt",
          "description": "this is cpt",
          "properties": {
              "name": {
                  "type": "string",
                  "description": "the name of certificate owner"
              },
              "gender": {
                  "enum": [
                      "F",
                      "M"
                  ],
                  "type": "string",
                  "description": "the gender of certificate owner"
              },
              "age": {
                  "type": "number",
                  "description": "the age of certificate owner"
              }
          },
          "required": [
              "name",
              "age"
          ]
      }
  },
  "transactionArg": {
      "invokerWeId": "did:weid:0x1ae5b88d37327830307ab8da0ec5d8e8692a35d3"
  },
  "functionName": "registerCpt",
  "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody cptBaseInfo

返回示例:

{
    "respBody": {
        "cptId": 2000001,
        "cptVersion": 1
    },
    "ErrorCode": 0,
    "ErrorMessage": "success"
}
7. 查询CPT

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName queryCpt Y
functionArg   Y
functionArg.cptId CPT ID,与SDK直接调用的方式入参一致。 Y
transactionArg   N,传空
v 版本号 Y

接口入参示例:

{
    "functionArg": {
        "cptId": 10,
    },
    "transactionArg": {
    },
    "functionName": "queryCpt",
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody 完整的CPT信息

接口返回示例:

{
    "respBody": {
        "cptBaseInfo" : {
            "cptId" : 10,
            "cptVersion" : 1
        },
        "cptId" : 10,
        "cptJsonSchema" : {
            "$schema" : "http://json-schema.org/draft-04/schema#",
            "title" : "a CPT schema",
            "type" : "object"
        },
        "cptPublisher" : "did:weid:0x104a58c272e8ebde0c29083552ebe78581322908",
        "cptSignature" : "HJPbDmoi39xgZBGi/aj1zB6VQL5QLyt4qTV6GOvQwzfgUJEZTazKZXe1dRg5aCt8Q44GwNF2k+l1rfhpY1hc/ls=",
        "cptVersion" : 1,
        "created" : 1553503354555,
        "metaData" : {
            "cptPublisher" : "did:weid:0x104a58c272e8ebde0c29083552ebe78581322908",
            "cptSignature" : "HJPbDmoi39xgZBGi/aj1zB6VQL5QLyt4qTV6GOvQwzfgUJEZTazKZXe1dRg5aCt8Q44GwNF2k+l1rfhpY1hc/ls=",
            "created" : 1553503354555,
            "updated" : 0
        },
        "updated" : 0
    },
    "ErrorCode": 0,
    "ErrorMessage": "success"
}
8. 创建Credential

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName createCredential Y
functionArg   Y
functionArg.claim claim Json结构体,与SDK直接调用的方式入参一致,下同 Y
functionArg.cptId CPT ID Y
functionArg.issuer issuer WeIdentity DID Y
functionArg.expirationDate 过期时间(使用UTC格式) Y
transactionArg   Y
transactionArg.invokerWeId 用于索引私钥的WeIdentity DID,服务器端会凭此找到所托管的私钥 Y
v 版本号 Y

接口入参:Json,以signature代替私钥

{
    "functionArg": {
        "cptId": 10,
        "issuer": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
        "expirationDate": "2019-04-18T21:12:33Z",
        "claim": {
            "name": "zhang san",
            "gender": "F",
            "age": 18
        },
    },
    "transactionArg": {
        "invokerWeId": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
    },
    "functionName": "createCredential",
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody 完整的Credential信息

接口返回示例:

{
  "respBody": {
      "@context": "https://github.com/WeBankBlockchain/WeIdentity/blob/master/context/v1",
      "claim": {
          "content": "b1016358-cf72-42be-9f4b-a18fca610fca",
          "receiver": "did:weid:101:0x7ed16eca3b0737227bc986dd0f2851f644cf4754",
          "weid": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
      },
      "cptId": 2000156,
      "expirationDate": "2100-04-18T21:12:33Z",
      "id": "da6fbdbb-b5fa-4fbe-8b0c-8659da2d181b",
      "issuanceDate": "2020-02-06T22:24:00Z",
      "issuer": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
      "proof": {
          "created": "1580999040000",
          "creator": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
          "signature": "G0XzzLY+MqUAo3xXkS3lxVsgFLnTtvdXM24p+G5hSNNMSIa5vAXYXXKl+Y79CO2ho5DIGPPvSs2hvAixmfIJGbw=",
          "type": "Secp256k1"
      }
  },
  "errorCode": 0,
  "errorMessage": "success"
}
9. 验证Credential

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName verifyCredential Y
functionArg   Y
functionArg.claim claim Json 结构体,与SDK直接调用的方式入参一致,下同 Y
functionArg.cptId CPT ID Y
functionArg.context context值 Y
functionArg.uuid Credential的UUID Y
functionArg.issuer issuer WeIdentity DID Y
functionArg.issuranceDate 颁发时间 Y
functionArg.expirationDate 过期时间 Y
functionArg.proof Credential签名结构体 Y
transactionArg   N,传空
v 版本号 Y

接口入参:

{
    "functionArg": {
      "@context": "https://github.com/WeBankBlockchain/WeIdentity/blob/master/context/v1",
      "claim": {
          "content": "b1016358-cf72-42be-9f4b-a18fca610fca",
          "receiver": "did:weid:101:0x7ed16eca3b0737227bc986dd0f2851f644cf4754",
          "weid": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
      },
      "cptId": 2000156,
      "expirationDate": "2100-04-18T21:12:33Z",
      "id": "da6fbdbb-b5fa-4fbe-8b0c-8659da2d181b",
      "issuanceDate": "2020-02-06T22:24:00Z",
      "issuer": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
      "proof": {
          "created": "1580999040000",
          "creator": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
          "signature": "G0XzzLY+MqUAo3xXkS3lxVsgFLnTtvdXM24p+G5hSNNMSIa5vAXYXXKl+Y79CO2ho5DIGPPvSs2hvAixmfIJGbw=",
          "type": "Secp256k1"
      }
    },
    "transactionArg": {
    },
    "functionName": "verifyCredential"
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody True/False

接口返回:

{
    "respBody": true,
    "ErrorCode": 0,
    "ErrorMessage": "success"
}
10. 创建CredentialPojo

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName createCredentialPojo Y
functionArg   Y
functionArg.claim claim Json结构体,与SDK直接调用的方式入参一致,下同 Y
functionArg.cptId CPT ID Y
functionArg.issuer issuer WeIdentity DID Y
functionArg.expirationDate 过期时间(使用UTC格式) Y
transactionArg   Y
transactionArg.invokerWeId 用于索引私钥的WeIdentity DID,服务器端会凭此找到所托管的私钥 Y
v 版本号 Y

接口入参:Json,以signature代替私钥

{
    "functionArg": {
        "cptId": 10,
        "issuer": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
        "expirationDate": "2019-04-18T21:12:33Z",
        "claim": {
            "name": "zhang san",
            "gender": "F",
            "age": 18
        },
    },
    "transactionArg": {
        "invokerWeId": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
    },
    "functionName": "createCredentialPojo",
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody 完整的CredentialPojo信息

接口返回示例:

{
  "respBody": {
      "cptId": 2000156,
      "issuanceDate": 1580996777,
      "context": "https://github.com/WeBankBlockchain/WeIdentity/blob/master/context/v1",
      "claim": {
          "content": "b1016358-cf72-42be-9f4b-a18fca610fca",
          "receiver": "did:weid:101:0x7ed16eca3b0737227bc986dd0f2851f644cf4754",
          "weid": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
      },
      "id": "21d10ab1-75fe-4733-9f1d-f0bad71b5922",
      "proof": {
          "created": 1580996777,
          "creator": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa#keys-0",
          "salt": {
              "content": "ncZ5F",
              "receiver": "L0c40",
              "weid": "I4aop"
          },
          "signatureValue": "HEugP13uDVBg2G0kmmwbTkQXobsrWNqtGQJW6BoHU2Q2VQpwVhK382dArRMFN6BDq7ogozYBRC15QR8ueX5G3t8=",
          "type": "Secp256k1"
      },
      "type": [
          "VerifiableCredential",
          "hashTree"
      ],
      "issuer": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
      "expirationDate": 4111737153
  },
  "errorCode": 0,
  "errorMessage": "success"
}
11. 验证CredentialPojo

调用接口:

标题 描述
接口名 weid/api/invoke
Method POST
Content-Type application/json

接口入参:

Key Value Required
functionName verifyCredentialPojo Y
functionArg   Y
functionArg.claim claim Json 结构体,与SDK直接调用的方式入参要求一致,下同 Y
functionArg.cptId CPT ID Y
functionArg.context context值 Y
functionArg.uuid CredentialPojo的UUID Y
functionArg.issuer issuer WeIdentity DID Y
functionArg.issuranceDate 颁发时间 Y
functionArg.expirationDate 过期时间 Y
functionArg.proof Credential签名值 Y
transactionArg   N,传空
v 版本号 Y

接口入参:

{
    "functionArg": {
      "cptId": 2000156,
      "issuanceDate": 1580996777,
      "context": "https://github.com/WeBankBlockchain/WeIdentity/blob/master/context/v1",
      "claim": {
          "content": "b1016358-cf72-42be-9f4b-a18fca610fca",
          "receiver": "did:weid:101:0x7ed16eca3b0737227bc986dd0f2851f644cf4754",
          "weid": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
      },
      "id": "21d10ab1-75fe-4733-9f1d-f0bad71b5922",
      "proof": {
          "created": 1580996777,
          "creator": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa#keys-0",
          "salt": {
              "content": "ncZ5F",
              "receiver": "L0c40",
              "weid": "I4aop"
          },
          "signatureValue": "HEugP13uDVBg2G0kmmwbTkQXobsrWNqtGQJW6BoHU2Q2VQpwVhK382dArRMFN6BDq7ogozYBRC15QR8ueX5G3t8=",
          "type": "Secp256k1"
      },
      "type": [
          "VerifiableCredential",
          "hashTree"
      ],
      "issuer": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
      "expirationDate": 4111737153
    },
    "transactionArg": {
    },
    "functionName": "verifyCredentialPojo"
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody True/False

接口返回:

{
    "respBody": true,
    "ErrorCode": 0,
    "ErrorMessage": "success"
}
基于轻客户端模式的RestService API
1. 总体介绍

基于轻客户端模式的API的入参形式与基于私钥托管模式的API相同,也是满足以下格式的json字符串。 最大的区别在于,流程包括两次交互,第一次是轻客户端提供接口参数发给RestService服务端,后者进行组装、编码区块链原始交易串并返回;第二次是轻客户端在本地使用自己的私钥,对原始交易串进行符合ECDSA的sha3签名,发给RestService服务端,后者打包并直接执行交易。

  • 第一次交互

调用接口:

标题 描述
接口名 weid/api/encode
Method POST
Content-Type application/json

接口入参(Body):

{
    "functionArg": 随调用SDK方法而变的入参json字符串 {
        ...
    },
    "transactionArg": 交易参数json字符串 {
        "nonce": 用于防止重放攻击的交易随机数
    }
    "functionName": 调用SDK方法名
    "v": API版本号
}

参数说明:

  • functionArg是随不同的SDK调用方法而变的,具体的参数可以查看SDK接口文档;后文会为每个所提及的接口给出对应的链接

  • transactionArg仅包括一个变量nonce,用于防止重放攻击的交易随机数,您可以使用RestService jar的getNonce(),或其他类似方法生成此随机数。
    • 必需,且必须妥善保存,这个nonce在第二次交互中还会用到
  • functionName是调用的SDK方法名,用于决定具体调用WeIdentity Java SDK的什么功能

  • v是调用的API方法版本

第一次交互的接口返回是以下格式的json字符串:

{
    "respBody": {
        "encodedTransaction": 基于Base64编码的原始交易串信息
        "data": 交易特征值
    }
    "ErrorCode": 错误码
    "ErrorMessage": 错误信息,成功时为"success"
}

返回结构体包含encodedTransaction和data两项。 调用者随后需要使用自己的私钥对encodeTransaction进行交易签名,然后使用Base64对其进行编码,和data、nonce一起待用, 进行第二次交互。

注解

请注意使用的ECDSA签名算法的编码格式。WeID Java SDK所使用的是椭圆曲线Secp256k1算法,这也是WeID Go轻客户端的默认算法。一般来说,ECDSA的签名算法会生成R,S,V三个值,其中R和S是32个字节的二进制字节数组,而V,以太坊原生的Secp256k1算法的结果一般是0或1。

注解

当您生成签名的R,S,V之后,您需要将R,S,V存入一个65个字节长的二进制字节数组,再进行Base64编码方可正确由RestService解析。RestService只接受两种组成方式: 1. 按照R, S, V的顺序拼接成一个65个字节长的数组并使用Base64编码(这是WeID Go轻客户端默认方式,此时V的值为0或1) 2. 按照V+27, R, S的顺序拼接成一个65个字节长的数组并使用Base64编码(这是WeID Java SDK默认序列化方式,此时V的值为27或28)

您可以参考Java侧的客户端代码,使用默认Secp256k1私钥进行签名和Base64编码的范例代码见下:

// 依赖web3sdk 2.2.2和weid-java-sdk 1.5
byte[] encodedTransaction = DataToolUtils
        .base64Decode("<encodedTransaction的值>".getBytes());
    SignatureData clientSignedData = Sign.getSignInterface().signMessage(encodedTransactionClient, ecKeyPair);
    String base64SignedMsg = new String(
        DataToolUtils.base64Encode(TransactionEncoderUtilV2.simpleSignatureSerialization(clientSignedData)));
  • 第二次交互

调用接口:

标题 描述
接口名 weid/api/transact
Method POST
Content-Type application/json

接口入参:

{
    "functionArg": 空 {
    },
    "transactionArg": 交易参数json字符串 {
        "nonce": 用于防止重放攻击的交易随机数
        "data": 交易特征值,需和第一步中返回值一致
        "signedMessage": 基于Base64编码的、使用私钥签名之后的encodedTransaction签名值
    }
    "functionName": 调用SDK方法名
    "v": API版本号
}

这一步的目的是将已签名交易的参数发给RestService,并由后者将交易打包上链。参数说明:

  • functionArg此时为空
  • transactionArg包括第一次交互起始阶段生成的nonce、第一次交互收到的data,以及最重要的,使用调用者本地私钥签名并通过Base64编码的encodedTransaction的签名值
  • functionName是调用的SDK方法名,用于决定具体调用WeIdentity Java SDK的什么功能
  • v是调用的API方法版本

第二次交互的接口返回是以下格式的json字符串:

{
    "respBody": 随调用SDK方法而变的输出值json字符串 {
    }
    "ErrorCode": 错误码
    "ErrorMessage": 错误信息,成功时为"success"
}

其中具体的输出值result亦是随不同的SDK调用方法而变的。 可以看到,基于轻客户端的交易方式,本质上,是因为签名操作必须在本地完成,因此将原始交易串分成了两次交互完成。 基于轻客户端的每个API的入参,也仅仅在第一次交互中不同。因此,在下文的介绍中,我们会忽略第二次交互的入参,只提供第一次交互的入参和第二次交互的返回值。

2. 创建WeIdentity DID(有参创建方式)

第一次交互,POST /weid/api/encode 的接口入参:

Key Value Required
functionName createWeId Y
functionArg   Y
functionArg.publicKey ECDSA公钥,需要为10进制的整型数字,以字符串形式传入 Y
transactionArg   Y
transactionArg.nonce 交易随机数 Y
v 版本号 Y

第一次交互,POST /weid/api/encode 接口入参示例:

{
    "functionArg": {
        "publicKey": "712679236821355231513532168231727831978932132185632517152735621683128"
    },
    "transactionArg": {
        "nonce": "1474800601011307365506121304576347479508653499989424346408343855615822146039"
    },
    "functionName": "createWeId",
    "v": "1.0.0"
}

第二次交互,POST /weid/api/transact 接口入参示例:

{
    "functionArg": {},
    "transactionArg": {
        "nonce": "1474800601011307365506121304576347479508653499989424346408343855615822146039",
        "data": "809812638256c1235b1231000e000000001231287bacf213c",
        "signedMessage": "HEugP13uDVBg2G0kmmwbTkQXobsrWNqtGQJW6BoHU2Q2VQpwVhK382dArRMFN6BDq7ogozYBRC15QR8ueX5G3t8="
    },
    "functionName": "createWeId",
    "v": "1.0.0"
}

第二次交互的接口返回:

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody WeIdentity DID

返回示例:

{
    "ErrorCode": 0,
    "ErrorMessage": "success",
    "respBody": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
}
3. 注册Authority Issuer

第一次交互,POST /weid/api/encode 的接口入参:

Key Value Required
functionName registerAuthorityIssuer Y
functionArg   Y
functionArg.name 机构名 Y
functionArg.weId WeIdentity DID,与SDK直接调用的方式入参要求一致 Y
transactionArg   Y
transactionArg.nonce 交易随机数 Y
v 版本号 Y

第一次交互,POST /weid/api/encode 接口入参示例:

{
    "functionArg": {
        "name": "BV-College",
        "weId": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
    },
    "transactionArg": {
        "nonce": "1474800601011307365506121304576347479508653499989424346408343855615822146039"
    },
    "functionName": "registerAuthorityIssuer",
    "v": "1.0.0"
}

第二次交互,POST /weid/api/transact 接口入参示例:

{
    "functionArg": {},
    "transactionArg": {
        "nonce": "1474800601011307365506121304576347479508653499989424346408343855615822146039",
        "data": "809812638256c1235b1231000e000000001231287bacf213c",
        "signedMessage": "HEugP13uDVBg2G0kmmwbTkQXobsrWNqtGQJW6BoHU2Q2VQpwVhK382dArRMFN6BDq7ogozYBRC15QR8ueX5G3t8="
    },
    "functionName": "registerAuthorityIssuer",
    "v": "1.0.0"
}

第二次交互的接口返回:

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody Authority Isser信息

返回示例:

{
    "ErrorCode": 0,
    "ErrorMessage": "success",
    "respBody": {
        "accValue": ,
        "created": "1581420650",
        "name": "BV-College",
        "weId": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
    }
}
4. 创建CPT

第一次交互,POST /weid/api/encode 的接口入参:

Key Value Required
functionName registerCpt Y
functionArg   Y
functionArg.cptJsonSchema CPT Json Schema,与SDK直接调用的方式入参要求一致,下同 Y
functionArg.weId CPT创建者 Y
functionArg.cptSignature 创建者使用自己的私钥对cptJsonSchema的签名(和私钥托管方式不同,本方式特有) Y
transactionArg   Y
transactionArg.nonce 交易随机数 Y
v 版本号 Y

第一次交互,POST /weid/api/encode 接口入参示例:

{
    "functionArg": {
        "weId": "did:weid:0x1ae5b88d37327830307ab8da0ec5d8e8692a35d3",
        "cptJsonSchema": {
            "title": "cpt",
            "description": "this is cpt",
            "properties": {
                "name": {
                    "type": "string",
                    "description": "the name of certificate owner"
                },
                "gender": {
                    "enum": [
                        "F",
                        "M"
                    ],
                    "type": "string",
                    "description": "the gender of certificate owner"
                },
                "age": {
                    "type": "number",
                    "description": "the age of certificate owner"
                }
            },
            "required": [
                "name",
                "age"
            ]
        }
        "cptSignature": "BaUeP13uDVBg2G0kmmwbTkQXobsrWNqtGQJW6BoHU2Q2VQpwVhK382dArRMFN6BDq7ogozYBRC15QR8ueX5G3t8="
    },
    "transactionArg": {
        "nonce": "1474800601011307365506121304576347479508653499989424346408343855615822146039"
    },
    "functionName": "registerCpt",
    "v": "1.0.0"
}

第二次交互,POST /weid/api/transact 接口入参示例:

{
    "functionArg": {},
    "transactionArg": {
        "nonce": "1474800601011307365506121304576347479508653499989424346408343855615822146039",
        "data": "809812638256c1235b1231000e000000001231287bacf213c",
        "signedMessage": "HEugP13uDVBg2G0kmmwbTkQXobsrWNqtGQJW6BoHU2Q2VQpwVhK382dArRMFN6BDq7ogozYBRC15QR8ueX5G3t8="
    },
    "functionName": "registerAuthorityIssuer",
    "v": "1.0.0"
}

第二次交互的接口返回:

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody Authority Isser信息

返回示例:

{
    "ErrorCode": 0,
    "ErrorMessage": "success",
    "respBody": {
        "cptId": 2000001,
        "cptVersion": 1
    }
}
5. 创建CredentialPojo

创建CredentialPojo不需要进行区块链交互,因此,只需要进行一次交互POST /weid/api/encode,然后对返回的结果进行签名即可:

POST /weid/api/encode 接口入参

Key Value Required
functionName createCredentialPojo Y
functionArg   Y
functionArg.claim claim Json结构体,与SDK直接调用的方式入参一致,下同 Y
functionArg.cptId CPT ID Y
functionArg.issuer issuer WeIdentity DID Y
functionArg.expirationDate 过期时间(使用UTC格式) Y
transactionArg 为空 Y
v 版本号 Y

接口入参:Json,以signature代替私钥

{
    "functionArg": {
        "cptId": 10,
        "issuer": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
        "expirationDate": "2019-04-18T21:12:33Z",
        "claim": {
            "name": "zhang san",
            "gender": "F",
            "age": 18
        },
    },
    "transactionArg": {
    },
    "functionName": "createCredentialPojo",
    "v": "1.0.0"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody 完整的CredentialPojo信息

接口返回示例:

{
  "respBody": {
      "cptId": 2000156,
      "issuanceDate": 1580996777,
      "context": "https://github.com/WeBankBlockchain/WeIdentity/blob/master/context/v1",
      "claim": {
          "content": "b1016358-cf72-42be-9f4b-a18fca610fca",
          "receiver": "did:weid:101:0x7ed16eca3b0737227bc986dd0f2851f644cf4754",
          "weid": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa"
      },
      "id": "21d10ab1-75fe-4733-9f1d-f0bad71b5922",
      "proof": {
          "created": 1580996777,
          "creator": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa#keys-0",
          "salt": {
              "content": "ncZ5F",
              "receiver": "L0c40",
              "weid": "I4aop"
          },
          "signatureValue": "HJPbDmoi39xgZBGi/aj1zB6VQL5QLyt4qTV6GOvQwzfgUJEZTazKZXe1dRg5aCt8Q44GwNF2k+l1rfhpY1hc/ls=",
          "type": "Secp256k1"
      },
      "type": [
          "VerifiableCredential",
          "hashTree"
      ],
      "issuer": "did:weid:101:0xfd28ad212a2de77fee518b4914b8579a40c601fa",
      "expirationDate": 4111737153
  },
  "errorCode": 0,
  "errorMessage": "success"
}

请注意,这个生成的CredentialPojo的签名值(proof中的signatureValue项)并没有经过私钥签名。正确的签名方式包括下面几步: - base64解码,生成一个二进制字节数组 - 对解码的byte[]做一次secp256k1的hash - 对完成hash过byte[],再做一次hash(如果您使用的是Java web3sdk的SignMessage(),这一步它替您完成了) - 传入私钥,进行签名,得到r,s,v - 对进行序列化 - 把序列化的byte进行base64编码发回RestService

使用ECDSA私钥进行签名和Base64编码的范例代码见下(Java和Go):

String signature = DataToolUtils.sign(new String(DataToolUtils.base64Decode(signatureValue)), privateKey);
base64SignatureValue := credentialEncodeResponse.RespBody.Proof.SignatureValue
signatureValue, err3 := base64.StdEncoding.DecodeString(base64SignatureValue)
hashedMsg := Hash(signatureValue)
doubleHashedMsg := Hash(hashedMsg)
privateKeyBytes := ConvertPrivateKeyBigIntToPrivateKeyBytes(privateKeyBigInt)
signatureBytes, err4 := SignSignature(doubleHashedMsg, privateKeyBytes)
signatureBase64String := base64.StdEncoding.EncodeToString(signatureBytes)
WeIdentity Endpoint Service API
1. 获取所有已注册的Endpoint信息

调用接口:

标题 描述
接口名 weid/api/endpoint
Method GET
Content-Type application/json

接口入参:无

接口返回:

{
    "ErrorCode": 0,
    "ErrorMessage": "success",
    "respBody": [
        {
            "requestName": "create-passphrase",
            "inAddr": [
                "127.0.0.1:6010",
                "127.0.0.1:6011"
            ],
            "description": "Create a valid random passphrase"
        },
        {
            "requestName": "verify-passphrase",
            "inAddr": [
                "127.0.0.1:6012",
                "127.0.0.1:6013"
            ],
            "description": "Verify a passphrase"
        }
    ]
}
2. 进行Endpoint调用

调用接口:

标题 描述
接口名 weid/api/endpoint/{endpoint}
Method POST
Content-Type application/json

接口入参:

Key Value Required
/{endpoint} 在API路径中标明的API名,String Y
body 以```分隔的多个传入服务端用于执行API的参数 Y

接口入参示例:

{
    "body": "did:weid:0xfd28ad212a2de77fee518b4914b8579a40c601fa```25"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody SDK侧的返回值,String

接口返回:

{
    "ErrorCode": 0,
    "ErrorMessage": "success",
    "respBody": "did:weid:0x1Ae5b88d37327830307ab8da0ec5D8E8692A35D3",
}
WeIdentity 数据授权 API

调用接口:

标题 描述
接口名 weid/api/authorize/fetch-data
Method POST
Content-Type application/json

接口入参:

Key Value Required
authToken CPT101数据授权凭证,需通过DataToolUtils.serialize()方式生成 Y
signedNonce 签名的Nonce值(当前暂不校验,可任意填入) Y

接口入参示例:

{
  "authToken": {
      "claim": {
          "duration": 360000,
          "fromWeId": "did:weid:101:0x69cd071e4be5fd878e1519ff476563dc2f4c6168",
          "resourceId": "4b077c17-9612-42ee-9e36-3a3d46b27e81",
          "serviceUrl": "http://127.0.0.1:6010/fetch-data",
          "toWeId": "did:weid:101:0x68bedb2cbe55b4c8e3473faa63f121c278f6dba9"
      },
      "context": "https://github.com/WeBankBlockchain/WeIdentity/blob/master/context/v1",
      "cptId": 101,
      "expirationDate": 1581347039,
      "id": "48b75424-9411-4d22-b925-4e730b445a31",
      "issuanceDate": 1580987039,
      "issuer": "did:weid:101:0x69cd071e4be5fd878e1519ff476563dc2f4c6168",
      "proof": {
          "created": 1580987039,
          "creator": "did:weid:101:0x69cd071e4be5fd878e1519ff476563dc2f4c6168#keys-0",
          "salt": {
              "duration": "fmk5A",
              "fromWeId": "DEvFy",
              "resourceId": "ugVeN",
              "serviceUrl": "nVdeE",
              "toWeId": "93Z1E"
          },
          "signatureValue": "HCZwyTzGst87cjCDaUEzPrO8QRlsPvCYXvRTUVBUTDKRSoGDgu4h4HLrMZ+emDacRnmQ/yke38u1jBnilNnCh6c=",
          "type": "Secp256k1"
      },
      "type": ["VerifiableCredential", "hashTree"]
  },
  "signedNonce": "123123"
}

接口返回: application/json

Key Value
ErrorCode 错误码,0表示成功
ErrorMessage 错误信息
respBody SDK侧的返回值,String

接口返回:

{
    "ErrorCode": 0,
    "ErrorMessage": "success",
    "respBody": "sample data",
}
深入了解RestService

您可以访问RestService的 设计文档GitHub仓库,了解RestService的方方面面。

WeIdentity RestService 设计文档
1. 设计理念

RestService的设计秉持以下原则:

  • 私钥在网络上进行传输是有可能遭遇窃听或中间人攻击的。因此,如果牵扯到远程调用,应该尽可能避免把私钥作为输入参数。私钥本身,要么由客户端自行存储,要么托管在RestService服务端。对于前者,WeIdentity支持“轻客户端模式”,允许用户直接在客户端对交易进行私钥签名并直接经由RestService向区块链发送交易;对于后者,RestService支持“私钥托管模式”,允许用户在信任RestService的前提下将私钥托管到服务端本地。
  • 在某些场景下,机构或用户可能不支持使用Java环境部署调用SDK,但是任何机构或用户都可以发送HTTP请求。因此,使用RestService的另一个好处便是降低了多语言版本的SDK的开发成本。用户只要有发送HTTP请求的能力即可以调用WeIdentity的相关功能。
  • RestService服务端所有API均为无状态的。
2. RestService整体技术架构
2.1 架构图 _images/arch.jpg

RestService架构包括以下模块:

  • 用户应用:用户的业务app应用,发送HTTP请求
  • rest-server:Server服务器端
  • weid-java-sdk:WeIdentity的SDK jar包
2.2 交易模型

过去的做法是:

  • 用户传入接口参数(包括私钥),直接调用RESTful接口,RestService调用sdk然后发送交易;

改造后:

(轻客户端模式:两次交互)

  • 轻客户端传入接口参数(不包括私钥),然后POST /weid/api/encode,发送请求给RestService
  • RestService接受请求,根据接口参数组装、编码区块链原始交易串,返回给轻客户端
  • 轻客户端在本地使用自己的私钥,对原始交易串进行符合ECDSA的sha3签名,然后POST /weid/api/transact,发送请求给RestService
  • RestService接受请求,打包签名完成的交易串,直接发送交易给区块链节点

(托管模式:一次交互)

  • 用户应用调传入自己的私钥索引以指明自己使用哪个私钥,然后POST /weid/api/invoke,发送请求给RestService
  • RestService接受请求,依据索引载入所托管的私钥,调用weid-java-sdk的对应方法,发送交易给区块链节点
3. 调用时序说明 _images/fig2.jpg

步骤说明:

(托管模式)

  • 用户组装方法名、业务输入参数、API版本号、私钥索引,然后POST /weid/api/invoke,要求调用函数。
  • Server访问SDK进行入参检测,若通过,索引其托管在Server端的私钥,使用私钥进行签名,随后调用SDK相关合约方法并返回结果。
WeIdentity Endpoint Service

在开发过程中,您可能会有将自己的某些Java方法暴露给外部调用方调用的需求。在过去,您可能需要自己开发一套基于HTTP/HTTPS的请求,及基于RPC的Endpoint服务端点的创建、维护及处理服务,这可能会花费大量的开发人力和工时。

现在,WeIdentity提供了一套标准化的Endpoint服务的参考实现样例。只要您已经集成了WeIdentity及RestService,便可以直接使用这一功能。您只需要在自己的Java集成环境里调用WeIdentity Java SDK,进行Endpoint的注册,之后您的方法就会自动出现在RestService对外暴露的API列表里,并可供外部调用了。

部署Endpoint Service

您可以参照 部署指引文档,进行Endpoint Service的部署及初始化。

WeIdentity Endpoint Service部署指引

为了实现基于RPC的远程调用和转发机制,WeIdentity Endpoint Service分成两部分:代理端(也即RestService这一侧)和服务端(也即Java SDK这一侧)。

其中,代理端负责直接接收HTTP/HTTPS请求,解析、进行Endpoint查询,并转发给对应的服务端。服务端负责接收请求,并直接转发给预先注册的Endpoint实现。因此,您需要按照以下顺序,分别部署Endpoint Service的服务端和代理端。

1. 代理端(RestService)配置

Endpoint Service在代理端是内建于Rest Service的,环境要求也与其一致,因此您需要首先按照 Rest Service 部署文档的步骤对其进行部署。

部署完毕后,在 dist/conf 目录下,修改 application.properties 文件中的主机端口列表(server.hostport.list)这一项。您需要在此处以逗号分隔所有需要由Rest Service连接的远程服务端,指明其主机IP及端口。这样,Endpoint Service就会在后台以您配置的时间间隔(此处的 fetch.period.seconds )去远程拉取注册在服务端的Endpoint。

# 向服务端周期拉取Endpoint配置的时间间隔,单位为秒
fetch.period.seconds=60
# 服务端所有主机端口列表
server.hostport.list=127.0.0.1:6010,127.0.0.2:6011
2. 在服务端(Java SDK)注册您的Endpoint

Rest Service代理端,仅仅是一个收集来自各方端点请求并进行中转的角色,它不包含具体的Endpoint的处理逻辑和方法实现。每个主机仍然需要在自己的Java SDK侧注册Endpoint,并暴露之前在代理端声明的对外端口,以供代理端中转调用。您需要在您集成Java-SDK时执行以下步骤:

  • 为您需要注册的每个Endpoint,实现一个对应的 EndpointFunctor 接口。此接口包括两个方法: execute()getDescription()
    • 实现 execute() ,用来决定具体当代理端的RPC请求发送过来时需要进行的操作
    • 实现 getDescription() ,用来提供一段对此接口功能的描述
  • 在实现完成之后,通过 registerEndpoint() 方法向此EndpointHandler内注册您的Endpoint

  • 服务端会自动将您的Endpoint写入本地Endpoint数据配置项中

  • 本地Endpoint数据配置项会被周期性地同步到代理端

  • 为了确保代理端能够正确地获取这些Endpoint,您需要启动 RpcServermain() 进程

详细的相关实现,可以参考源代码的 EndpointSample.java

3. 启动服务端

Endpoint Service在服务端依托于WeIdentity-Java-SDK,环境要求也与其一致,请见 Java-SDK 部署环境要求

src/main/resources 目录下,修改 weidentity.properties 文件中的 rpc.listener.port 这一项内容,以具体确定在哪个端口上进行监听。

# RPC服务端的监听端口
rpc.listener.port=6010

随后,您需要执行您在上一步骤中注册Endpoint的方法。以 EndpointSample.java 为例,执行:

java -cp <$your class path> com.webank.weid.suite.endpoint.EndpointSample

您也可以在IDE环境里执行您的注册方法。当您看到以下提示,表示RPC Server已经启动完毕:

Trying to receive incoming traffic at Port: 6010
4. 在代理端调用您的Endpoint

当您注册Endpoint完毕之后,等待您之前所设定的拉取时间间隔(默认为60秒),以保证此Endpoint被代理端正确地拉取到。随后,您就可以在代理端调用此接口了。

您可以参见REST API文档中的“WeIdentity Endpoint Service API”一节,了解如何获取和调用注册的Endpoint。

使用Endpoint Service

您可以参照 RestService API文档中的Endpoint Service API部分,了解如何查询和调用您注册的Endpoint信息。

基于Endpoint Service的数据授权

WeIdentity提供依托于Endpoint Service而存在的数据授权。授权的详细信息会以可信可验证的凭证(Credential)方式展现,并由Endpoint Service服务端进行验证。

一个标准的数据授权凭证其CPT ID为101。它的Claim中包括以下内容:

  • 授权发起者(fromWeId):表示这个凭证是由哪个WeID所发起的;此发起者必须和凭证的Issuer一致,且在链上存在
  • 授权接收者(toWeId):表示这个凭证是由哪个WeID所接收的
  • 授权时间(duration):表示此授权的持续时间,单位为毫秒
  • 服务地址(serviceUrl):表示此授权的目的服务地址。只支持HTTP/HTTPS协议,且必须是以标准URL形式存储,必须包括主机名、端口及路径(如https://127.0.0.1:6010/data-auth/guest)
  • 资源ID(resourceId):表示此授权所请求的资源ID,以UUID形式表示

简而言之,调用者必须传入一个CPT101授权凭证,此凭证中包含数据授权的主机地址、资源ID,由Endpoint Service的服务端进行查验,确认凭证合法且服务地址(serviceUrl)已在本服务端后台注册过。查验通过后,Endpoint Service会将请求以RPC请求的形式转向在本服务端注册的各端点后台,并由各后台根据传入的资源ID,返回资源信息。具体的调用方式可以查阅API文档。

技术细节
  • Endpoint Service的RPC库基于 smart-socket 1.4.2这一Java AIO框架。
    • 目前Endpoint Service在此RPC库基础上自行实现了更加简单易用的按需断线重连机制。
    • 目前Endpoint Service以异步的方式实现远程调用,并通过指定时长内的本地轮询查询执行结果。如果您的方法可能耗时过长,可以调节轮询的最大等待时长。
  • Endpoint Service支持1:N的多集成端访问和多活机制。如果一个Endpoint可以由多台集成端访问,那么当其中某一台集成端访问出错时,访问其的请求会被自动导向其他注册了相同Endpoint的机器上。

WeIdentity 智能合约设计与实现
WeIdentity智能合约概述

WeIdentity使用基于Solidity的智能合约进行开发。Solidity的智能合约语义上是图灵完备的,该语言支持各种基础类型(Booleans,Integers,Address,Bytes,Enum等)、复杂类型(Struct,Mapping,Array等)、复杂的表达式、控制结构和远程调用,以及接口、继承等面向对象的高级语言特性。Solidity是以太坊和FISCO-BCOS所支持的智能合约语言。

智能合约功能强大,因而真实世界中的复杂商业逻辑和应用可以在区块链上轻松实现。然而,智能合约一旦部署,它会在所有区块链节点上独立重复运行,因此原则上认为,只有各业务方需要进行共识的、逻辑可复用的业务才有必要通过智能合约在链上实现。此外,智能合约发布之后,若出现问题需要修复或者业务逻辑变更,是无法通过简单地在原有合约基础上修改再重新发布来解决的。因此,在设计之初还需要结合业务场景思考合适的合约更新机制。总体上,WeIdentity合约的设计原则是:功能完备、逻辑清晰、模块解耦、结构清晰、安全完备、支持升级。

当前,WeIdentity合约层面的工作目标主要包括两部分:

  • WeIdentity DID智能合约,负责链上ID体系建立,具体包括生成DID(Distributed IDentity)、生成DID Document、DID在链上的读取与更新。
  • WeIdentity Authority智能合约,负责进行联盟链权限管理,具体包括链上DID角色的定义、操作与权限的定义与控制。

下文将会基于业务目标对这两部分展开描述。

WeIdentity DID智能合约
概述

从业务视角来看,DID智能合约只需要做一件事,就是如何定义DID Document的存储结构和读写方式。DID Document的结构并不复杂(见规范文档);但在实际的业务中,存在一些挑战:

  • 伴随着接入用户(人与物)的快速增长,DID的总量将会增长迅速,规模庞大。因此,设计一个大而全的映射表是不现实的,这会带来巨大的寻址开销,即使采用传统分库、分表、跨链的思路也难以应付。
  • DID存在更新的需求。因此,每次都存储完整的Document域在更新情况下会产生大量的历史数据。

因此,WeIdentity使用Linked Event:基于事件链的存储方法来解决以上问题。

存储结构

Linked Event的核心实现思路是借助Solidity的事件(Event)机制,采用类似链表的思路对DID Document的更新进行存储和读取。在Solidity里,每个区块都有对应的Event存储区,用于对区块相关的事件进行存储,并最终存入Event log。因此,存储层面上,在不同时间点DID的更新可以存入更新时当前块的Event里,同时将当前块高作为索引记录每次更新事件。读取层面上,如果要读取完整DID Document,只需按索引反向遍历对应的块的Event里即可。基于这一思路,进行以下设计:

  • 设计一个映射记录,使用DID的地址作为索引,用来存储每个DID最近的一次更新事件所对应的块高;
  • 设计一个更新事件,用来记录每次DID更新的相关属性及前一个块高;
  • 设计一个查询函数,用来读取映射记录找到某个DID的最近的块高,以便反向解析具体的更新事件。

以上数据和逻辑会被合并到一个整体合约里。具体流程为:

  • 每当触发一次DID Document的属性更新,就记入一次更新事件,同时记录更新事件所对应的当前块高,存入整体合约的记录映射部分;
  • 记录映射部分存入整体合约的存储区,更新事件最终会存入区块链的Event;
  • 当读取DID Document时,只需通过记录映射读取块高,反向遍历对应的块的Event,解析并找到Document更新相关的事件内容,然后合并即可。

这一流程图可见于:

linked-events.png
性能评估

使用Linked Event进行存储的优势有以下几点:

  • 非常适合更新的场景。由于Solidity Event的特性,本方案的写性能和存储开销会远远优于完整存储DID Document内容进入合约的解决方案。
  • 更方便的记录历史版本。通过记录每个事件的块高,可以快速的定位到每个事件,在溯源场景下有着广泛的应用;同时,又不需对那些未更新的属性项进行存储。
  • 读性能对更新事件是O(N)的时间增长。因此,在Document更新不频繁的场景下,读性能非常好。由于WeIdentity的DID本身更多地用来存储公钥等信息,更新频率大部分情况下并不高,因此非常适合WeIdentity的使用场景。
WeIdentity Authority智能合约
概述

Authority智能合约的主要任务是联盟链的权限管理。在WeIdentity的业务场景中,存在以下挑战:

  • 不同的DID实体拥有不同的权限。

例如,存在Authority Issuer这一角色用来描述现实世界中的「权威凭证发行者」,它们能够发行低段位授权CPT,权限高于一般的DID;更进一步地,在Authority Issuer之上存在着委员会(Committee),它们的权限更高,包括了对Authority Issuer的治理等内容。因此,WeIdentity需要设计合理的「角色—操作」二元权限控制。

  • 权限管理的业务逻辑会随着业务迭代而不断更新。

在真实业务场景中,随着业务变化,权限管理逻辑也可能随之改变;同时,不同的业务方可能会有定制化权限管理的需求。因此,WeIdentity需要进行合理的分层设计,将数据和行为逻辑分离,在升级的情况下就只需对行为逻辑部分进行升级,数据存储保持不变,尽可能地降低更新成本。

当前,业内已经有了一些对权限进行操作和维护的开源解决方案,如ds-auth和OpenZepplin的Role智能合约;但它们的权限管理逻辑可扩展性较差且不支持合约分层更新。下文将介绍WeIdentity的Authority智能合约实现。

架构
角色与权限

当前的WeIdentity角色设计了四种角色:

  • 一般DID。一般的实体(人或物),由WeIdentity的分布式多中心的ID注册机制生成,没有特定权限。
  • Authority Issuer。授权机构,具有发行低段位授权CPT的权限。
  • Committee Member。机构委员会成员。具有管理Authority Issuer成员资格的权限。
  • Administrator。系统管理员。具有管理Committee Member及Authority Issuer成员资格的权限,未来还包括修改合约地址的权限。

每个角色具体的权限表如下:

操作 一般DID Authority Issuer Committee Member Administrator
增删改Administrator N N N Y
增删改Committee Member N N N Y
增删改Authority Issuer N N Y Y
发行授权CPT N Y Y Y

合约分层

WeIdentity采用分层设计模式,即将合约分为逻辑合约、数据合约、及权限合约。

  • 逻辑合约:它专注于数据的逻辑处理和对外提供接口,通过访问数据合约获得数据,对数据做逻辑处理,写回数据合约。一般情况下,控制器合约不需要存储任何数据,它完全依赖外部的输入来决定对数据合约的访问。
  • 数据合约:它专注于数据结构的定义、数据内容的存储和数据读写的直接接口。
  • 权限合约:它专注于判断访问者的角色,并基于判断结果确定不同操作的权限。

上述架构图如下:

authority-contract-arch.png
权限与安全管理

当前的WeIdentity权限管理的挑战是:

  • 合约在链上部署之后,攻击者可能会绕过SDK直接以DApp的形式访问合约。因此合约层面必须要有自完善的权限处理逻辑,不能依赖SDK。
  • 数据合约是公开的,因此数据合约的操作也需要进行权限管理。

WeIdentity的权限管理依赖于一个独立的RoleManager权限管理器合约,它承担了合约所有的权限检查逻辑。WeIdentity的权限粒度是基于角色和操作的二元组,这也是当前大多数智能合约权限控制的通用做法。它的设计要点包括:

  • 将角色和操作权限分别存储。
  • 设计一个权限检查函数checkPermission()供外部调用,输入参数为「地址,操作」的二元组。
  • 对角色和权限分别设计增删改函数供外部调用。
  • 所有WeIdentity的数据合约里需要进行权限检查的操作,都通过外部合约函数调用的方式,调用checkPermission()。
  • 所有WeIdentity依赖权限管理器的合约,需要有更新权限管理器地址的能力。

WeIdentity的权限管理有以下特性:

  • 优秀的可扩展性。WeIdentity的权限控制合约使用外部调用而非继承(如ds-auth和OpenZepplin的Role智能合约实现角色管理方式)方式实现。在权限控制合约升级的场景中,外部调用方案只需简单地将权限管理器合约地址更新即可,极大地提升了灵活度。
  • 使用tx.origin而非msg.sender进行调用源追踪。这是因为用户的权限和自己的DID地址唯一绑定。因此所有权限的验证必须要以最原始用户地址作为判断标准,不能单纯地依赖msg.sender。此外,WeIdentity的权限控制合约需要支持更大的可扩展性,以支持更多公众联盟链的参与成员自行实现不同的Controller。因此,需要通过tx.origin追踪到调用者的WeIdentity DID,并根据DID确定权限。
Specific Issuer(Issuer链上类型声明)

WeIdentity支持为每位Authority Issuer在链上声明所属类型,即Specific Issuer。您可以指定某位Authority Issuer的具体类型属性,如学校、政府机构、医院等。当前,此属性与其对应的权限没有直接关系,仅作记录之目的。

WeIdentity Evidence智能合约

WeIdentity不仅提供了基于DID的公钥存储 + 数字签名用来防止凭证被篡改,同时也提供了Evidence存证功能,基于区块链不可篡改的特性,为创建出的凭证增信。简单来说,任何使用者,都可以将凭证的内容摘要上传到链上,以便在未来使用时可以根据链上内容比对,以防篡改。内容摘要使用Hash算法,抗逆向反推。

从WeIdentity SDK 1.5.2+版本开始,Evidence智能合约开始使用类似WeID智能合约的Linked-Event形式进行hash内容的存储。它支持多个不同的签名方对存证打上自己的签名进行背书;同时也支持签名方为存证填入额外记录(log)。不同的log会按照写入区块链的顺序垒叠起来,形成不可篡改的记录集。

存证合约有以下特性:

  • 签名的Hash值在创建时就已确定且不可更改。
  • 不同的签名方可以通过调用createEvidence接口为同一个存证增加来自于不同签名方的签名。
  • 签名方可以调用增加记录接口(addLog)为存证添加额外信息。
  • 添加的额外信息(log记录)只能追加,不能修改或删除。
  • 不同的签名方的签名信息和额外信息存储互不干扰。
  • 如果一不小心写错了存证信息,就创建一个新存证吧!
WeIdentity CPT智能合约

WeIdentity的CPT(Claim Protocol Type)合约,用于在链上存储凭证的Claim模板。CPT合约使用标准的数据-逻辑分离架构。一个数据CPT合约里,最重要的是其jsonSchema部分,它存储了以jsonSchema格式记载的Claim格式内容。区分不同CPT是通过其ID来进行的。

根据CPT使用目的、内容的不同,ID可以被划分成以下三个范围:1~1000(系统CPT),1000~2000000(授权CPT),2000000以上(普通CPT)。

系统CPT表

系统CPT的ID落在1~1000里,它们是在WeIdentity智能合约部署之初就创建好的内置CPT,用来完成所有WeIdentity实例的统一功能,它们在部署WeIdentity智能合约时,在初始化过程中部署在链上。系统CPT不支持任何角色创建。

当前,系统CPT表包括以下内容:

ID 标题 内容
101 授权凭证 某个WeID授权另一个WeID使用数据
102 挑战凭证 某个WeID对另一个WeID身份证明的挑战
103 身份验证凭证 某个WeID针对CPT102的挑战的回复
104 Claim Policy 某个选择性披露的Claim Policy定义
105 API Endpoint Endpoint端点服务的端点定义
106 嵌套凭证 嵌套的Credential,用来进行多签
107 嵌套凭证 嵌套的CredentialPojo,用来进行多签
108 整合可信时间戳 为某个嵌套凭证生成的可信时间戳,包含凭证原文
109 可分离可信时间戳 为某个嵌套凭证生成的可信时间戳,不包含凭证原文

关于每个系统CPT的详细字段要求,可以查阅代码中的 对应文件,此处不再详细展开。

授权CPT

授权CPT的ID落在1000~2000000里,如Authority合约中所述,授权CPT仅支持由Authority Issuer创建,一般是和具体的联盟链业务相关。

一般CPT

一般CPT的ID从2000000开始自增。任何WeID均可以创建此类CPT。

WeIdentity智能合约依赖关系
contractdep.jpg
如何贡献
规范和流程

开始贡献之前,建议阅读我们的代码规范文档和代码提交步骤介绍。

WeIdentity Java SDK贡献代码
使用 WeIdentity 部署工具完成部署(联盟链管理员)
整体介绍

一条区块链里,有多家机构,只需要一家机构部署 WeIdentity 智能合约,部署完成后,将智能合约地址给到其他机构即可。

1. 配置基本信息
cd weid-build-tools
vim run.config
  • 配置区块链节点信息,填入区块链节点 IP 和 Channel端口,示例如下:

注解

区块链节点的Channel端口说明见FISCO BCOS 2.0配置文件说明

blockchain_address=10.10.10.10:20200
  • 如果需要配置多个区块链节点,用逗号分隔,示例如下:
blockchain_address=10.10.10.10:20200,10.10.10.11:20200
  • 配置机构名称,该名称也被用作后续机构间的 AMOP 通信标识。假设您的机构名为 test,您可以配置为:
org_id=test
  • 配置 chain-id,该配置项用于路由到不同的网络,假设您的 chain-id 定义为1,则您可以配置为:
chain_id=1
  • 配置数据库相关,该配置用于SDK存储相关数据使用:
mysql_address=0.0.0.0:3306
mysql_database=database
mysql_username=username
mysql_password=password

保存退出,即完成基本配置。

注解

如果您使用FISCO-BCOS 2.x,且需要跨群组部署WeIdentity,请参考跨群组部署WeIdentity

2. 配置节点证书和秘钥文件
cd resources/

FISCO BCOS 2.0请参考2.0 web3sdk客户端配置,将证书文件 ca.crtnode.crtnode.key 复制出来,拷贝至当前目录下。

3. 部署智能合约并自动生成配置文件
  • 如果您是第一次使用本工具,您需要先进行编译:

注解

如果您重新修改了 run.config 里的配置项,您也需要重新编译。
cd ..
chmod +x compile.sh
./compile.sh

如果执行过程没报错,大约半分钟左右可以编译完成。

  • 执行脚本 deploy.sh 进行 WeIdentity 智能合约的发布。
chmod +x deploy.sh
./deploy.sh

运行成功后,会打印以下信息:

contract is deployed with success.
===========================================.
weid contract address is 0x4ba81103afbd5fc203db14322c3a48cd1abb7770
cpt contract address is 0xb1f3f13f772f3fc04b27ad8c377def5bc0c94200
authority issuer contract address is 0xabb97b3042d0f50b87eef3c49ffc8447560faf76
evidence contract address is 0x8cc0de880394cbde18ca17f6ce2cf7af5c51891e
specificIssuer contract address is 0xca5fe4a67da7e25a24d76d24efbf955c475ab9ca
===========================================.

注解

发布 WeIdentity 智能合约的机构将会自动注册为委员会机构成员( Committee Member )。
发布 WeIdentity 智能合约会同时会在 weid-build-tools/output/admin 目录下动态生成私钥文件 ecdsa_key,以及对应的公钥文件 ecdsa_key.pub,此私钥后续用于注册权威机构,您可以将其保存到您的其他存储库里。
在根目录下会生成一个hash文件,此文件用于给其他不部署合约的机构使用。

至此,您已经完成 weid-java-sdk 的安装部署,您可以开始您的 Java 应用集成以及便捷工具体验。

注解

一条区块链里,有一家机构负责部署 WeIdentity 智能合约,部署成功后,会将上述智能合约地址给到其他机构。

WeIdentity JAVA SDK安装部署文档(源码方式)
配置 说明
操作系统 CentOS (7.2.* 64位)或Ubuntu(16.04 64位)。
FISCO-BCOS区块链环境 您需要有一套可以运行的FISCO-BCOS区块链环境,如果没有,可以参考「FISCO-BCOS 2.0节点安装方法」来搭建一套区块链环境。
JDK 要求JDK1.8+,推荐使用jdk8u141。JDK 跟 WeID 直接的兼容性,可见兼容性文档
网络连通 检查部署 WeIdentity JAVA SDK 的服务器是否能 telnet 通 FISCO BCOS 节点的 channel 端口(channel端口是什么,详见),若telnet不通,需要检查网络连通性和安全策略。

注解

注意:如果您使用了针对oracle jdk1.8.0.231及以上版本,不论用哪种方式部署SDK,在使用时都需要配置jvm参数 -Djdk.tls.namedGroups=”secp256k1” 。详细原因,请见Oracle JDK 8u231的Release Notes: https://www.oracle.com/technetwork/java/javase/8u231-relnotes-5592812.html

1.下载源码

注解

注意:如果您使用了openjdk13,那么您需要手动修改build.gradle,禁用spotbugs相关功能。具体地,请打开build.gradle,然后将所有spotbugs相关项注释掉即可。

  • 下载最新的代码
git clone https://github.com/WeBankBlockchain/WeIdentity.git

或者

git clone https://gitee.com/WeBank/WeIdentity.git

WeIdentity Java SDK 工程见WeIdentity JAVA SDK

  • WeIdentity 编译
cd WeIdentity
chmod u+x ./gradlew
./gradlew build -x test

注解

如果出现 xx SpotBugs violations were found 这个提示,请忽略。

  • 配置节点证书和秘钥文件
cd src/main/resources/

若您使用FISCO BCOS 2.0, 请参考2.0 web3sdk客户端配置, 将证书文件 ca.crtnode.crtnode.key 复制出来,拷贝至当前目录下。

  • 配置基本信息
cd ../../../build-tools/bin/
vim run.config

主要的配置文件 run.config ,配置一些运行时需要的一些参数.

  • 配置说明:
blockchain_address : 区块链节点 IP 和channel端口, channel端口的配置可以参考FISCO BCOS 2.0 配置项说明 进行配置。
amop_id :机构间的通信标识,见配置说明
org_id :机构名称, 见配置说明
chain_id :用于标识您接入的区块链网络, 默认填写:101, 见配置说明
persistence_type :数据存储类型, 默认填写:mysql。
mysql_address :配置数据库的ip和port,例:0.0.0.0:3306
mysql_database :配置数据库名称
mysql_username :配置数据库用户名
mysql_password :配置数据库用户对应的密码
cns_profile_active :合约部署环境标识,可用于WeIdentity合约隔离。

配置样例:

#节点的连接串,节点IP为10.10.10.10,和channel端口为20200。
blockchain_address=10.10.10.10:20200

#机构间的通信标识
amop_id=organizationA

#机构名称
org_id=organizationA

#链标识
chain_id=101

#数据存储类型
persistence_type=mysql

#数据库ip和port
mysql_address=0.0.0.0:3306

#数据库名称
mysql_database=database

#数据库用户名
mysql_username=username

#数据库密码
mysql_password=password

#合约部署环境标识
cns_profile_active=prdA

注解

注意:如果您使用了Gradle 6.0+,那么您需要手动修改build.gradle中spotbug的Gradle插件版本号为2.0.0+。具体地,打开WeIdentity/build.gradle,将“classpath “gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.5””中的1.6.5改成2.0.0或更高版本。

注解

如果您使用FISCO-BCOS 2.x,且需要跨群组部署WeIdentity,请参考跨群组部署WeIdentity

2.安装部署

运行下面的命令,自动完成代码编译,智能合约编译,智能合约部署和所有配置文件的配置:

chmod +x run.sh
./run.sh

出现下列输出,则表示安装部署成功。

contract deployment done.
begin to modify sdk config...
modify sdk config finished...
begin to clean config...
clean finished...

到这里,您已经完成了weid-java-sdk的安装和部署的全部步骤,您可以开始使用WeIdentity来构建您的分布式身份管理的Java应用了。

注解

如果执行部署过程中出现 160016 - no premission for this cns. 异常,请修改(run.config)中的配置项 cns_profile_active 的值,修改成一个独有的值即可, 如:cns_profile_active=test456。

Have fun!!!

备注
查看WeIdentity JAVA SDK部署结果
  • 进入dist目录
cd ../../dist/
ls

dist目录包含以下目录: appconflib

目录名 说明
app 打包好的weid-java-sdk jar包。
conf weid-java-sdk运行时的一些配置,Java应用集成weid-java-sdk的时候,需要将此目录下的文件放到您自己的Java应用的classpath下。
lib 依赖的jar包。
  • 进入源码根目录
cd ../
ls

根目录下生成的文件 ecdsa_key 为weid-java-sdk部署合约动态生成的秘钥文件,您的Java应用集成weid-java-sdk的时候可能需要使用此文件,请妥善保管。

便捷工具

当您部署SDK完成之后,可以通过一些便捷工具快速上手体验SDK,请参考 WeIdentity 便捷工具使用

WeIdentity JAVA SDK 便捷使用工具
整体介绍
通过便捷工具,您可以快速的体验和使用 WeIdentity JAVA SDK。
部署步骤

在使用之前,要确保您已完成 WeIdentity JAVA SDK 的安装部署,若您还没有完成,请参考WeIdentity JAVA SDK 安装部署(部署智能合约)或者WeIdentity JAVA SDK 安装部署(不部署智能合约)

此步骤提供快速创建 WeIdentity DID、注册 Authority Issuer、发布 CPT、拉取 CPT 并生成 presentation policy 的能力。

1 创建您的 WeIdentity DID

这个步骤会帮您快速创建一个 WeIdentity DID。

cd ../tools
chmod +x create_weid.sh
./create_weid.sh

若执行成功,则会打印以下信息。

New weid has been created ----> did:weid:1:0x405a7ae297fc6d6fb02fb548db64b29f08114ca1
The related private key and public key can be found at /home/app/tonychen/test_gradle/weid-build-tools/output/create_weid/xxx/0x405a7ae297fc6d6fb02fb548db64b29f08114ca1.

表明创建的 WeID 是did:weid:1:0x405a7ae297fc6d6fb02fb548db64b29f08114ca1。

weid-build-tools/output/create_weid/xxx 目录下看到一些以 0x 开头的目录,找到跟刚刚生成的 WeIdentity DID 匹配的目录,里面包含了 WeIdentity DID,公钥 ecdsa_key.pub 和私钥 ecdsa_key

注解

xxx为您启用的CNS地址

2 注册 Authority Issuer (权威凭证发行者)

注解

只有 Committee Member(委员会机构成员)可以进行本节操作,若您不是 Committee Member,您可以将您的 WeIdentity DID 和机构名称发给 Committee Member,让其帮您注册成 Authority Issuer。

  • 注册 Authority Issuer

假设您要注册的 Authority Issuer 的 WeIdentity DID 为did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb,机构名称是 test。

chmod +x register_authority_issuer.sh
./register_authority_issuer.sh --org-id test --weid did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb

如果执行成功,会打印以下信息。

Registering authorityissuer ---> did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb, name is :test
Execute succeed.
  • 移除Authority Issuer

如果您需要移除某个Authority Issuer,比如您要移除did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb:

./register_authority_issuer.sh --remove-issuer did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb

若执行成功,则会打印以下信息。

Removing authority issuer ---> did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb...
Execute succeed.
3 注册 Specific Issuer(特定类型的发行者)

注解

只有委员会成员( Committee Member )可以进行本节操作,若您不是委员会成员,您可以将您的 WeIdentity DID 和机构名称发给委员会成员,让其帮您注册成 Specific Issuer。

  • 注册特定类型机构

假设您要注册的机构的 WeIdentity DID 为did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb,注册类型为 college,只需执行此下命令:

chmod +x register_specific_issuer.sh
./register_specific_issuer.sh --type college --weid did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb

执行成功,则会打印以下信息。

[RegisterIssuer] Adding WeIdentity DID did:weid:1:0xe10e52f6b7c6751bd03afc023b8e617d7fd0429c in type: college
Specific issuers and types have been successfully registered on blockchain.

如果您需要注册多个机构,请将其 WeIdentity DID 用逗号分割开,如下所示:

./register_specific_issuer.sh --type college --weid did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb,did:weid:0x6efd256d02c1a27675de085b86989fa2ac1baddb
  • 移除特定类型机构

比如您要从 college 类型中移除 WeID 为 did:weid:1:0x6efd256d02c1a27675de085b86989fa2ac1baddb 的 Specific Issuer:

./register_specific_issuer.sh --type college --remove-issuer did:weid:1:0x6efd256d02c1a27675de085b86989fa2ac1baddb
4 机构发布 CPT

此步骤会帮助机构发布指定的 CPT 到区块链上。

如果您的 WeIdentity DID 是执行第1节生成的,您可以不用传入私钥,只用指定 CPT 的路径和 WeID 即可。

注解

以下样例中的 test_data/single/ 已预置测试CPT。您也可以更改为其他包含您CPT的目录。

chmod +x register_cpt.sh
./register_cpt.sh --cpt-dir test_data/single/ --weid did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb

若执行成功,则会打印以下信息:

[RegisterCpt] register cpt file:JsonSchema.json result ---> success. cpt id ---> 1000
[RegisterCpt] register cpt file:JsonSchema.json with success.
Execute succeed.

如果您是通过其他途径创建的 WeIdentity DID,您需要自己指定私钥的位置。 假如机构的 WeID 是 did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb,私钥文件路径为 /home/test/private_key/ecdsa_key

./register_cpt.sh --cpt-dir test_data/single/ --weid did:weid:1:0x5efd256d02c1a27675de085b86989fa2ac1baddb --private-key /home/test/private_key/ecdsa_key

若执行成功,则会打印以下信息:

[RegisterCpt] register cpt file:JsonSchema.json result ---> success. cpt id ---> 1000
[RegisterCpt] register cpt file:JsonSchema.json with success.
Execute succeed.
5 拉取CPT并生成presentation policy模板

注解

此步骤,可以帮使用者从区块链上拉取指定的已发布的 CPT,并转化成 POJO,同时也会根据您生成一个 presentation policy 模板。

假如您需要将 CPT id 为 1000 的 CPT 从区块链上拉取下来,并基于 CPT 1000 生成 presentation policy 的配置模板。

chmod +x cpt_to_pojo.sh
./cpt_to_pojo.sh --cpt-list 1000

若执行成功,则会打印以下信息。

Begin to generate pojo from cpt...
All cpt:[1000] are successfully transformed to pojo.

The weidentity-cpt.jar can be found in /home/app/tonychen/test_gradle/weid-build-tools/output/pojo/0x8ce1fc7af86917b503d7d5aaa2987a33ccf97f767199a360712fee667a54ef80/d8acebb597d0428fac682ad188e4312d/weidentity-cpt.jar
Begin to generate presentation policy ...
Presentation policy template is successfully generated, you can find it at /home/app/tonychen/test_gradle/weid-build-tools/output/presentation_policy.

表明生成的 CPT 的 POJO 的jar包在 /home/app/tonychen/test_gradle/weid-build-tools/output/pojo/xxx/ 目录下, 生成的 presentation policy 模板在 /home/app/tonychen/test_gradle/weid-build-tools/output/presentation_policy

注解

xxx为您启用的CNS地址