数字身份之VP签发源码解读

阅读此文,需要具备一定的DID数字身份体系知识

目前只关注Blockchain部署方式下VP的生成过程

签发者基础身份信息:

Weid:

did:weid:iot:0x670f39b3dd92ae1cb97bc137793635c06cfed76f

Private_key: 55908972729025521152709411706112459391833725585437529733832811281080145383459

Public_key:

1049546905065573960760084439650965135523067151041694071311599899979899899017785831423352665548046543544539176746548789086180279253285007496367545719488819

1、参数解析基础校验分装

// 获取到所有的VC凭证

credentialListNode = functionArgNode.get(WeIdentityParamKeyConstant.CREDENTIAL_LIST);

// 获取到所有可披露策略

presentationPolicyENode = functionArgNode.get(WeIdentityParamKeyConstant.PRESENTATION_POLICY_E);

2、参数解析

获取签发者weid

// 获取 transactionArg 入参内容

JsonNode txnArgNode = new ObjectMapper().readTree(createPresentationFuncArgs.getTransactionArg());

// 获取 transactionArg 中 invokerWeId 签名者weid

JsonNode keyIndexNode = txnArgNode.get(WeIdentityParamKeyConstant.KEY_INDEX);

3、寻址及转化

//根据签发者weid获取到私钥文件名及存储地址
//文件名为 weid 去除前缀 did:weid:iot:后的字符串
String fileName = weId.substring(weId.lastIndexOf(":") + 1);
// check the default passphrase 从application配置文件中获取default.passphrase=private_key
String passphrase = PropertiesUtil.getProperty("default.passphrase");
// admin的私钥单独存储需特殊处理 文件名为private_key 路径为 ./
// 其他的私钥 文件名为weid 去除前缀 did:weid:iot:后的字符串 路径为 ./keys/
if (fileName.equalsIgnoreCase(passphrase)) {
    fileName = WeIdentityParamKeyConstant.DEFAULT_PRIVATE_KEY_FILE_NAME;
    path = ADMIN_PRIVKEY_PATH;
}

4、从私钥中获取公钥

DataToolUtils.publicKeyStrFromPrivate(new BigInteger(privateKey))

5、从公钥中获取weid(keyWeId)

convertPublicKeyToWeId(publicKey);

其中在convertPublicKeyToWeId 函数中调用address = DataToolUtils.addressFromPublic(new BigInteger(publicKey));这里address = 0x670f39b3dd92ae1cb97bc137793635c06cfed76f即为存储的文件名

private static String buildWeIdByAddress(String address) {
    if (StringUtils.isEmpty(getChainId())) {
        throw new WeIdBaseException("the chain Id is illegal.");
    }
    StringBuffer weId = new StringBuffer();
    weId.append(WeIdConstant.WEID_PREFIX)// did:weid:
        .append(getChainId())// iot 在weidentity配置文件中设置的chain.id
        .append(WeIdConstant.WEID_SEPARATOR); // did:weid:
    if (!StringUtils.contains(address, WeIdConstant.HEX_PREFIX)) {
        weId.append(WeIdConstant.HEX_PREFIX);
    }
    weId.append(address);
    return weId.toString();
}

6、身份校验

检查通过weid拿到的私钥重构的地址和weid中指向的地址是否匹配

函数:validatePrivateKeyWeIdMatches

检查weId是否与私钥匹配。

参数:

privateKey—私钥

weId——weId

返回:

匹配为True,不匹配为false

public static boolean validatePrivateKeyWeIdMatches(WeIdPrivateKey privateKey, String weId) {
    boolean isMatch = false;
    try {
        //通过weid拿到的私钥重构的地址
        String address1 = DataToolUtils.addressFromPrivate(new             BigInteger(privateKey.getPrivateKey()));
        //weid中指向的地址
        String address2 = WeIdUtils.convertWeIdToAddress(weId);
        if (address1.equals(address2)) {
            isMatch = true;
        }
    } catch (Exception e) {
        logger.error("Validate private key We Id matches failed. Error message :{}", e);
        return false;
    }
    return isMatch;
}

7、从传入的私钥构建默认的身份验证weIdAuthentication

weIdAuthentication包含三项:WeId、AuthenticationMethodId、PrivateKey

其中WeId由经由4、5两步获取,AuthenticationMethodId由下列公式生成:

setAuthenticationMethodId=keyWeId + "#keys-" + 公钥

(公钥获取方式:DataToolUtils.publicKeyStrFromPrivate(new BigInteger(privateKey))));

8、生成challenge

指定一个随机的字母数字nonce, WeIdentity DID所有者将签署一个包含nonce的凭证,以证明该WeIdentity DID的所有权。依赖方应在挑战中包含随机字母数字(即nonce),以防止重放攻击。这也被称为动态挑战。(后文中会用到)

入参为weid和当前时间的毫秒数

challenge = Challenge.create(WeId, currentTimeMillis)
//这里的seed就是currentTimeMillis
//生成随机种子=当前时间戳+uuid(字符串拼接)
String randomSeed = seed + DataToolUtils.getUuId32()
//字符集将此randomSeed编码为字节序列,生成新的字节数组存储到Seed中。
random.setSeed(randomSeed.getBytes());
byte[] bytes = new byte[15];
random.nextBytes(bytes);
//经由base64转码封装成nonce
String nonce = Base64.encodeBase64String(bytes);
Challenge challenge = new Challenge();
challenge.setNonce(nonce);
challenge.setWeId(userWeId);
return challenge;

9、封装vp前参数校验

(1)校验PolicyPublisherWeId边界、链信息、格式合法性(WeIdUtils.isWeIdValid)

(2)遍历并校验原始校凭证:

校验Issuer边界、链信息、格式合法性(WeIdUtils.isWeIdValid)

校验凭证过期时间expirationDate

validDateExpired(args.getIssuanceDate(), args.getExpirationDate());

校验凭证内容:credentialId非空且为有效uuid、content和type非空、proof校验等

(3)根据原始证书和claimPolicy去创建选择性披露凭证

获取vc列表中的cptId组装成list,获取策略主键组装成Set(策略主键为cptId);

    策略预处理:获取盐值map(来源于policy)、claim map(来源于VC)及disclosure map来源于policy)(待处理的入参fieldsToBeDisclosed转换为map)进行校验

策略校验:逐一校验vc中的claim字段与policy中的fieldsToBeDisclosed是否匹配(注意:字段策略可缺失的情况默认补全(0)即为披露,但不能全部缺失

加盐处理:对不披露的字段进行加盐处理,具体运算规则如下: DataToolUtils.hash(String.valueOf(field) + String.valueOf(salt));

DataToolUtils.hash使用的方法为:Keccak-256 hash最终返回16进制编码的字符串。

Vp主要由verifiableCredential和proof组成,至此,原始凭证已处理完毕封装到verifiableCredential中。

10、组装vp及签名

这里我们注重关注proof的生成,首先看一个标准的vp proof

"proof": {
   "created": 1711530265,//创建时间戳
   "nonce": "q3MxHERSFqoFd6BA2h6R",
   "signatureValue": "oEvoNZeFYNTdvC6PsBxEQUsdTk4eWJ2G3Z/yf/7Rwb8MJKbh3A0iPgSk38niHRzgls5FzsETa+QRoU49owLoBgE=",
   "type": "Secp256k1",//签名算法
   "verificationMethodId":// keyWeId + "#keys-" + 公钥;
    "did:weid:iot:0x670f39b3dd92ae1cb97bc137793635c06cfed76f#keys-            1049546905065573960760084439650965135523067151041694071311599899979899899017785831423352665548046543544539176746548789086180279253285007496367545719488819"
},

verifySignatureFromWeId 入参:presentationE(为:VP中 proof置空的内容)、VP中的签名、签发者链上的公钥

DataToolUtils.verifySignatureFromWeId(presentationE.toRawData(), signature, weIdDocument, null);

注意我们看toRawData这段源码: 

public String toRawData() {
​    PresentationE presentation = DataToolUtils.clone(this);
​    presentation.proof = null;
​    return DataToolUtils.serialize(presentation);
}

proof主要有created(创建时间戳)、nonce(8中已生成)、signatureValue、type(即签名类型,weid中固定为Secp256k1)、verificationMethodId(7中已生成,生成格式为:keyWeId + "#keys-" + 公钥)

PresentationE presentation) {
    String proofType = CredentialProofType.ECDSA.getTypeName();
    //这里PROOF_TYPE用的是Secp256k1作为签名算法
    presentation.putProofValue(ParamKeyConstant.PROOF_TYPE, proofType);
    //获取当前时间戳
    Long proofCreated = DateUtils.getNoMillisecondTimeStamp();
    presentation.putProofValue(ParamKeyConstant.PROOF_CREATED, proofCreated);
    //封装数组
    String methodId = weIdAuthentication.getAuthenticationMethodId();
    presentation.putProofValue(ParamKeyConstant.PROOF_VERIFICATION_METHOD_ID, methodId);
    presentation.putProofValue(ParamKeyConstant.PROOF_NONCE, challenge.getNonce());
    //签名使用Secp256k1 sign to Signature.其中签名的范围包含:proofType、proofCreated、AuthenticationMethodId以及nonce
    String signature = DataToolUtils.SigBase64Serialization(
        DataToolUtils.signToRsvSignature(presentation.toRawData(),             
             weIdAuthentication.getWeIdPrivateKey().getPrivateKey())
    );;
    presentation.putProofValue(ParamKeyConstant.PROOF_SIGNATURE, signature);
}

至此VP的签发已完成。

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值