分布式身份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. 用户的隐私保护。

你可能感兴趣的:(分布式身份DID实践)