分布式身份DID实践

转自https://blog.csdn.net/studyzy 关于DID说明这个讲的比较好,参考他的。
假设一个场景,个人,公安局,第三方。 第三方需要个人信息用于登录和确认身份。则DID流程如下图

did流程图

描述一下流程

1. 生成DID

个人和公安局分别生成did标识和did文档,生成did部分代码如下

// 第一步生成自己的did和did文档
    // 生成自己的公私钥
    _,ownPublicKeyPem,ownPrivateKey,ownPublicKey:=GenerateKey()
    // 生成唯一标识符UUID
    didId := dids.GetUUID()
    // 生成自己的did
    myDid := CreateOwnDID(didId)
    fmt.Println("已生成我自己的did:",myDid)
    // 生成自己的did文档
    ownDoc,jsonDoc:=CreateOwnDIDDocument(didId,myDid,ownPublicKeyPem)
    fmt.Println("已生成我自己的did文档:",jsonDoc)
    // 此时将生成的didId 作为Key 将did文档存到区块链链上

生成的did文档如下

{
  "@context": "https://w3id.org/did/v1", //did 协议
  "id": "did:ccp:7f8ca8982f6cc6e8ea087bd9457ab8024bd2", //did 标识符
// 公钥信息
  "publicKey": [ 
    {
      "id": "did:ccp:7f8ca8982f6cc6e8ea087bd9457ab8024bd2#keys-1",
      "type": "Secp256k1",
  // 公钥
      "publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71"
    }
  ],
// 采用哪一个公钥认证
  "authentication": ["did:ccp:7f8ca8982f6cc6e8ea087bd9457ab8024bd2#key-1"],
// 通过服务端来验证公钥 可以通过服务端来查询公钥确实属于该服务端的
  "service": [
    {
      "id": "did:ccp:7f8ca8982f6cc6e8ea087bd9457ab8024bd2#resolver",
      "type": "DIDResolve",
      "serviceEndpoint": "https://xxxxxxx"
    }
  ]
}

did文档最核心的功能就是存着你的公钥,并且这个公钥还能通过serviceEndpoint 指定的服务器来验证

2. 上链

然后将did标识作为key,did文档作为值分别存储于区块链上。这里什么区块链都行,不做要求

3. 个人申请VC

个人向公安局提出申请VC请求,需要向公安局提交个人的验证资料,身份证什么的,同时将你的did标识,和签名发给公安局

4. 验证个人DID

公安局通过did标识从链上下载你的did文档,得的你的公钥,然后验证签名。

5. 生成VC 并上链

公安局开始创建VC 部分代码如下

// 第三步 我来到公安局 申请可验证声明VC,于是警察叔叔来我家 调查一下,确定没有问题。可以发证
    // 于是要求我上传身份证等信息,同时还需要上传我did
     own:="我就是孙博,和我上传的一堆东西"
     //用我的私钥签名
     ownSign,_:= ownPrivateKey.Sign([]byte(own))
    // 当警察叔叔拿到我的数据后,更具我的did 从区块链上拿到我的did文档,因为did文档有我的公钥呀
    // 1 验证一下我的did是否正确
      if isok,_:= ownPublicKey.Verify([]byte(own),ownSign);isok{
        fmt.Println("嗯 上传的数据就是孙博的签名的没有问题")
      }
    // 2 验证通过后 警察叔叔开始生成 可验证声明VC 里面的敏感信息可以用申请的人公钥加密
    nameP,_:=ownPublicKey.Encrypt([]byte("孙博"))
    name:=&CredentialSubjectValue{Value:hex.EncodeToString(nameP),Index: "0"}
    birthdayP,_:=ownPublicKey.Encrypt([]byte("1999"))
    birthday:=&CredentialSubjectValue{Value:hex.EncodeToString(birthdayP),Index: "1"}
    dataList := [][]byte{
        []byte("孙博"),
        []byte("1999"),
    }
    // 最重要就是生成MerkleRoot,用户数据披露和校验
    root := dids.CalcMerkleRoot(dataList)
    // 警察叔叔对Merkle根签名
    policeSignRoot,_:= policePrivateKey.Sign(root)
    policeSignRootString:=hex.EncodeToString(policeSignRoot)
    // 生成VC
    credentialSubject:=&CredentialSubject{Id: myDid,Name: name,Birthday: birthday}
    proof:=&Proof{Type: "Secp256k1",SignatureValue:policeSignRootString,VerificationMethod:policeDoc.Authentication[0]}
    vc:=&VC{Context:[]string{"https://www.w3.org/2018/credentials/v1"},
        Id:dids.GetUUID(),
        Type:[]string{"VerifiableCredential", "AlumniCredential"},
        Issuer:policeDid,
        IssuanceDate:time.Now().String(),
        CredentialSubject:credentialSubject,
        Proof:proof,
    }
    vcjson,_:= jsoniter.Marshal(vc)
    fmt.Println("警察叔叔已经生成VC:"+string(vcjson))
    // 警察叔叔这时候将生成的vc,将vc 上链,因为vc中的个人数据都由我的公钥加密,所以安全
    // 我从链上下载后 需要验签一下Merkle根,验签通过则表示这个VC就是警察叔叔发的

对应的VC如下

{
// did 协议
  "@context": [
    "https://www.w3.org/2018/credentials/v1"
  ],
  "id": "唯一标识符",
  "type": ["VerifiableCredential", "AlumniCredential"],
  // 发证人DID 也就是 公安局
  "issuer": "did:ccp:7f8ca8982f6cc6e8ea087bd9457ab8024bd2",
  "issuanceDate": "2010-01-01T19:73:24Z",
 // 我的数据呀 这里目前只有姓名和出生年,
  "credentialSubject": {
    // 申请人did 也就是我did
    "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
    "name":{
     // 用我的公钥加密了
      "value": "用申请方的公钥加密",
    //  这个是顺序 主要是用在生成MerkleTree的
      "index": "0"
    },
    "birthday":{
      "value": "用申请方的公钥加密",
      "index": "1"
    },
   // 证明 用来进行属性披露的 VC 的时候用不着 ,空字符就行
    "evidence": ""
  },
// 验证方式
  "proof": {
    "type": "Secp256k1",
   // 签名数据
    "signatureValue": "将credentialSubject原始数据生成MerkleRoot后签名",
 // 这个很重要 告诉我 使用的是公安局did 的那个公钥 才能验签signatureValue
    "verificationMethod": "did:ccp:7f8ca8982f6cc6e8ea087bd9457ab8024bd2#key-1"
  }
}

因为VC里数据都用公钥加密里 所以将VC上链,key就是VC的id,这里上链的好处 一会说

6. 个人获取VC并验证

个人通过VC的ID 从链上下载VC

  1. 通过我的私钥解密credentialSubject 获得明文所有属性
  2. 根据所有属性生成MerkleTree,并获取MerkleRoot
  3. 验证VC的signatureValue 是否是公安局的MerkleRoot 签名
  4. 通过签名后,表示该VC就是公安局背书的,并且数据没有被修改
  5. 将VC属性明文保存到本地

7. 第三方向个人发送验证申请

这里可以是个人主动 发送 也可以是第三方申请,其实都一样

8. 个人生成VP

收到第三方申请后,我决定只告诉他我的姓名,不暴露我的出生日期,所以我选择性披露姓名

  1. 私钥解密credentialSubject,获取所有属性,并生成证明
    // 1 解密VC数据 得到明文
    named,_:= hex.DecodeString(vc.CredentialSubject.Name.Value)
    ownNamed,_:=ownPrivateKey.Decrypt(named)
    birthdayd,_:= hex.DecodeString(vc.CredentialSubject.Birthday.Value)
    ownBirthdayd,_:=ownPrivateKey.Decrypt(birthdayd)
    newDataList := [][]byte{
        ownNamed,
        ownBirthdayd,
    }
    // 生成证明
    evidence, _ := dids.CalcMerkleEvidence(newDataList, 0)

这里说下证明类型

type Evidence struct {
    RawData       []byte   //需要披露的属性
    MerkleSibling [][]byte //路径
    Index         uint     //需要披露的属性所在Merkle树中的位置从0开始
    MerkleRoot    []byte   //根
}

证明里最重要的其实就是MerkleRoot和MerkleSibling 路径,没有带任何明文数据,至于MerkleRoot和MerkleSibling 的算法https://blog.csdn.net/studyzy
生成的VP 如下

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://www.w3.org/2018/credentials/examples/v1"
  ],
  "type": "VerifiablePresentation",
// 这里是将VC直接带过来
  "verifiableCredential": [{
    "@context": [
      "https://www.w3.org/2018/credentials/v1"
    ],
    "id": "http://example.edu/credentials/1872",
    "type": ["VerifiableCredential", "AlumniCredential"],
    "issuer": "did:ccp:7f8ca8982f6cc6e8ea087bd9457ab8024bd2",
    "issuanceDate": "2010-01-01T19:73:24Z",
    "credentialSubject": {
      "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
      "name":{
        "value": "用申请方did:example:ebfeb1f712ebc6f1c276e12ec21的公钥加密",
        "index": "0"
      },
      "birthday":{
        "value": "用申请方did:example:ebfeb1f712ebc6f1c276e12ec21的公钥加密",
        "index": "1"
      },
// 此时证明数据存在
      "evidence": "生成属性披露证明,属性证明里带有本次的新的MerkleRoot"
    },
    "proof": {
      "type": "Secp256k1",
      "signatureValue": "将credentialSubject原始数据生成MerkleTree后签名",
      "verificationMethod": "did:ccp:7f8ca8982f6cc6e8ea087bd9457ab8024bd2#key-1"
    }
  }],
// VP 验证
  "proof": {
    "type": "Secp256k1",
   // 将evidence证明进行签名,用我的私钥签名
    "signatureValue": "将evidence证明进行签名",
  //  验证signatureValue 采用的公钥ID
    "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1"
  }
}

将生成的VP 发送给第三方

9 验证VP是否是我的

第三方等到我VP后,从链上分别通过我的DID 和公安局的DID 获取DID文档。获取VP-> verifiableCredential-> evidence证明 和 vp-> proof-> signatureValue证明签名 来验证VP是否是我发送的

    // 1 从链上获取孙博的DID文档,假设这里已经获取到了
    // 根据vp的验证方法 VerificationMethod 找到公钥
    var publicKey crypto.PublicKey
    for _,pk:=range ownDoc.PublicKey{
        // 找到我的公钥
        if pk.ID==vp.Proof.VerificationMethod{
            publicKey,_=dids.PublicKeyFromPEM([]byte(pk.PublicKeyHex))
        }
    }
    // 获取证明签名
    newOwnSign,_:=hex.DecodeString(vp.Proof.SignatureValue)
    if isok,_:= publicKey.Verify([]byte(vc.CredentialSubject.Evidence),newOwnSign);isok{
        fmt.Println("VP验证成功就是孙博的DID签发的")
    }

10 验证VP里的VC数据是否正确

有一种可能我的个人正确信息其实只有公安局才有,他发的VC才可信,但是我在生成VP的时候,如果修改了信息,第三方如何知道验证呢?因此VC的上链才能解决这个问题。
解决方法如下

  1. 从链上下载VC 验证后 获取MerkleRoot签名,这个是有公安局背书的 没有问题
  2. 从PV里反序列化证明,可以从证明里得到我自己生成的MerkleRoot
  3. 将证明里的MerkleRoot和VC签名的MerkleRoot 用公安局公钥验证
    通过则表示我生成证明的数据就是公安局背书的数据
    代码如下
// 3 验证数据是否是警察叔叔背书
    zk:=&dids.ZKEvidence{}
    // 反序列等到我的证书
    jsoniter.Unmarshal([]byte(vp.VC.CredentialSubject.Evidence),&zk)
    // 获取VC里警察叔叔的merkleRoot签名
    merkleRootSign,_:= hex.DecodeString(vp.VC.Proof.SignatureValue)
    // 用警察叔叔的公钥 对我证明里的MerkleRoot 和 签名的merkleRootSign 验证一下
    if isOk,_:= policePublicKey.Verify(zk.MerkleRoot,merkleRootSign);isOk{
        fmt.Println("数据完整新验证完成")
    }
    // 4 获取披露数据
    pass := dids.ZKProve(zk)
    if pass {
        fmt.Printf("证据验证通过!断言「%s」成立", zk.RawData)
    } else {
        fmt.Println("证据验证失败")
    }

以前经常遇到,有人问有什么场景是只有区块链能做的,其他系统不能做的。除了公链数字货币,分布式身份算一个,因为VC 和DID文档 必须是可信的,不能恶意篡改!
至于优点么,1. 能解决不同系统的用户统一问题,2. 用户的隐私保护。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • 去中心化身份(Decentralized ID, DID)介绍 DID可以说是区块链领域一个偏冷门的方向,但是其实...
    Treaser阅读 2,493评论 1 3
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,033评论 0 4
  • 公元:2019年11月28日19时42分农历:二零一九年 十一月 初三日 戌时干支:己亥乙亥己巳甲戌当月节气:立冬...
    石放阅读 6,870评论 0 2