转自https://blog.csdn.net/studyzy 关于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
- 通过我的私钥解密
credentialSubject
获得明文所有属性 - 根据所有属性生成MerkleTree,并获取MerkleRoot
- 验证VC的
signatureValue
是否是公安局的MerkleRoot 签名 - 通过签名后,表示该VC就是公安局背书的,并且数据没有被修改
- 将VC属性明文保存到本地
7. 第三方向个人发送验证申请
这里可以是个人主动 发送 也可以是第三方申请,其实都一样
8. 个人生成VP
收到第三方申请后,我决定只告诉他我的姓名,不暴露我的出生日期,所以我选择性披露姓名
- 私钥解密
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的上链才能解决这个问题。
解决方法如下
- 从链上下载VC 验证后 获取MerkleRoot签名,这个是有公安局背书的 没有问题
- 从PV里反序列化证明,可以从证明里得到我自己生成的MerkleRoot
- 将证明里的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. 用户的隐私保护。