QHID SDK 分角色示例

角色介绍

你可能会以以下的四种角色开展DID业务

1. Service-Provider:
   - 首先你需要向cert-service申请cert-DID
   - 部署自己的基础业务服务,用于批准的参与-DID的修改、创建;使用cert-DID私钥签名对业务DID的操作,并将操作及签名发送至auth-service
2. DID-Holder: 
   - 向Service-Provider申请或修改业务DID
   - 基于DID签名VC
3. VC-Holder: 
  - 申请VC
  - 选择性的披露VC
4. VC-Verifier:
  - 通过resolver验证VC-issuer(即DID-Holder)
  - 通过chanellege-response的模式验证VC

Service-Provider 示例

创建账户并向cert-service申请cert-DID

import axios from 'axios'
import * as path from 'path'
import {Accounts, DIDClient} from 'qihoo-did-sdk'

const keystore = "./keystore"
const mnemonic = "radar blur cabbage chef fix engine embark joy scheme fiction master release";
const recoveryM = 'can neutral bright novel garlic rubber glove slide useful daring distance able'
const signingM = 'hard betray gain stadium arrive puppy civil name cover emerge citizen taste'
const recoverykey = 'recovery'
const signingkey = 'signing'
const password = 'passw0rd'
const domain = 'cert'
const applyinfo = {
    "name": "brandunion",
    "contact": "010-2242321",
    "website": "www.union.com",
    "subDomains": [ "brand" ]
}
const cert_service_url = 'http://10.217.36.124:15001'
const certDIDInfoFile = path.join(keystore, `certDID.json`)

const accounts = new Accounts(keystore)
await accounts.new(recoveryM, password, signingkey)
await accounts.new(signingM, password, signingkey)

await accounts.unlock(password, '', recoverykey)
await accounts.unlock(password, '', signingkey)

const client = new DIDClient(accounts, recoverykey, signingkey)
const [err,op] = await client.GenerateCreateOperation(domain, applyinfo)
if(err != undefined){
    console.log(`GenerateCreateOperation failed with err ${err}`)
    process.exit(1)
}
axios.post(cert_service_url + '/did', {
    op: op,
    info: applyinfo
}).then(function (response) {
    if(response.data.code === 200){
        const didInfo = {
            'id' : response.data.id,
            'subDomains': applyinfo.subDomains || [],
            'recoveryRevealValue': client.recoveryRevealValue,
            'updateRevealValue': client.updateRevealValue,
            'recoverykey': recoverykey,
            'signingkey': signingkey,
        }
        fs.writeFileSync(certDIDInfoFile, JSON.stringify(didInfo), {    encoding: 'utf8' });
        console.log(`${didInfo.id}`)
    }else{
        console.log(`failed to send request with error ${JSON.stringify(response.data)}`)
        process.exit(1)
    }
})
.catch(function (error) {
    console.log(JSON.stringify(error));
    process.exit(1)
});

使用cert-DID私钥签名创建业务DID,并将操作及签名发送至auth-service

import axios from 'axios'
import * as path from 'path'
import * as crypto from 'crypto';
import {Accounts, Claims} from 'qihoo-did-sdk'

const keystore = "./keystore"
const domain = "brand"
const services = {
    "id": "vcRepository",
    "type": "VCVerifyService",
    "serviceEndpoint": "http://127.0.0.1:13000"
}
auth_service_url = 'http://10.16.133.178:15006'
const didmethod = 'trust'
const recoverykey = 'recovery'
const signingkey = 'signing'
const password = 'passw0rd'
const certDIDInfoFile = path.join(keystore, `certDID.json`)
const businessDIDInfoFile = path.join(keystore, `businessDID.json`)

const certDIDInfo = require(certDIDInfoFile)
const businessDIDInfo = require(businessDIDInfoFile)
const payload = JSON.stringify({
    issuer : certDIDInfo.id,
    issuanceDate : Date.now() ,
    expireDate : Date.now()+10000000,
    op : businessDIDInfo.op
})
const hashedPayload = crypto.createHash('sha256').update(Buffer.from(payload)).digest().toString('hex');
await accounts.unlock(password,undefined,certDIDInfo.signingkey)
const signature = await accounts.sign(hashedPayload,password,undefined,certDIDInfo.signingkey)
axios.post(auth_service_url + '/did', {
    data: payload,
    signature: signature
})
.then(function (response) {
    if(response.status === 200){
        console.log(`business did creation request succ with response ${response.data}`)
    }else{
        console.error(`failed to send request with error ${response.data}`)
        process.exit(1)
    }
})
.catch(function (error) {
    console.error(`failed to send request with error: ${error}`);
    process.exit(1)
});

DID-Holder 示例

向Service-Provider申请业务DID

import axios from 'axios'
import * as path from 'path'
import {Accounts, DIDClient} from 'qihoo-did-sdk'

const keystore = "./keystore"
const domain = "brand"
const services = {
    "id": "vcRepository",
    "type": "VCVerifyService",
    "serviceEndpoint": "http://127.0.0.1:13000"
}
const didmethod = 'trust'
const recoverykey = 'recovery'
const signingkey = 'signing'
const password = 'passw0rd'
const certDIDInfoFile = path.join(keystore, `certDID.json`)
const businessDIDInfoFile = path.join(keystore, `businessDID.json`)

const accounts = new Accounts(keystore)
await accounts.unlock(password, '', recoverykey)
await accounts.unlock(password, '', signingkey)

const client = new DIDClient(accounts, recoverykey, signingkey)
const [err,op] = await client.GenerateCreateOperation(domain, services)
if(err != undefined){
    console.log(`GenerateCreateOperation failed with err ${err}`)
    process.exit(1)
}
const businessDIDInfo = {
    'id' : `did:${didmethod}:` + client.didUniqueSuffix,
    'recoveryRevealValue': client.recoveryRevealValue,
    'updateRevealValue': client.updateRevealValue,
    'recoverykey': recoverykey,
    'signingkey': signingkey,
    'op': op
}
fs.writeFileSync(businessDIDInfoFile, JSON.stringify(businessDIDInfo), { encoding: 'utf8' });
console.log(`businessDID: ${businessDIDInfo.id}`)

基于DID签名VC

import * as path from 'path'
import * as jose from 'jose'
import {v1} from 'uuid'
import {Accounts, Claims} from 'qihoo-did-sdk'

const randoms = [
    0x10, 0x91, 0x56, 0xbe, 0xc4, 0xfb, 0xc1, 0xea,
    0x71, 0xb4, 0xef, 0xe1, 0x67, 0x1c, 0x58, 0x36
];

const keystore = "./keystore"
const signingkey = 'signing'
const vcJWKFile = path.join(keystore, `vcJWK.json`)
const vcInfoFile = path.join(keystore, `vcInfo.json`)
const businessDIDInfoFile = path.join(keystore, `businessDID.json`)

const {JWS} = jose

const businessDIDInfo = require(businessDIDInfoFile)
const vcInfo = require(vcInfoFile)
let vcJWK = require(vcJWKFile)

let vcid:string;
if (vcJWK.vcid == '') {
    vcid = v1({
        msecs: new Date().getTime(),
        nsecs: 5678,
        rng: () => randoms,
    });
    vcJWK.vcid = vcid;
    fs.writeFileSync(vcJWKFile, JSON.stringify(vcJWK), { encoding: 'utf8' });
} else {
    vcid = vcJWK.vcid;
}

if (vcInfo.status == 'pending') {
    const accounts = new Accounts(keystore)
    const issuer = businessDIDInfo.id
    const issuanceDate = Date.now() 
    const expireDate =  Date.now()+10000000

    const vcPayload = {
        "@context": ["https://www.w3.org/2018/credentials/v1"],
        "type": vcInfo.vc.type,
        "vcid": vcid,
        "issuanceDate":issuanceDate,
        "expireDate": expireDate,
        "issuer": issuer, 
        "vc": vcInfo.vc
    };

    await accounts.unlock(password, '', signingkey)
    const kp = accounts.keypair('', signingkey)
    vcJwt = JWS.sign(
        vcPayload,
        kp[1],
        {
            algorithm: 'ES256',
        }
    )
    let props = vcInfo.vc.credentialSubject.alumniOf
    let c = new Claims()
    const rootHash = c.build(props).root()
    const vc = {
        "@context": ["https://www.w3.org/2018/credentials/v1"],
        "id": vcid,
        "type": vcInfo.vc.type,
        "issuer": issuer,
        "issuanceDate": issuanceDate,
        "expireDate": expireDate, 
        "credentialSubject": {
            "id": vcInfo.vc.credentialSubject.id, 
            "alumniOf": rootHash,
            "claims": vcInfo.vc.credentialSubject.alumniOf 
        },
        "proof": {
            "created": Date.now(),
            "proofPurpose": vcInfo.vc.proof.proofPurpose,
            "verificationMethod": kp[0], //issuer pub key
            "jws": vcJwt
        }
    }
    const vcInfo = {
        'status':'created',
        'vc': vc
    }
    fs.writeFileSync(vcInfoFile, JSON.stringify(vcInfo), { encoding: 'utf8' });
    console.log(`vc create ok`);
} else {
    console.log(`vc status pending`);
}

验证VC

import * as path from 'path'
import * as Koa from 'koa';
import * as Router from 'koa-router';
import * as jose from 'jose'
import {Accounts} from 'qihoo-did-sdk'

const keystore = "./keystore"
const signingkey = 'signing'
const password = 'passw0rd'
const vcShowFile = path.join(keystore, `vcShow.json`)

const {JWS} = jose
const router = new Router();

router.get('/vcv/:vcJwt', async (ctx:any, _next:any) => {
    const vcJwt = ctx.params.vcJwt
    let response = {
        'code': 200,
        'body': ''
    }
    try {
        if(vcJwt == undefined) {
            response = {
                'code': 400,
            }
        }else{
            await accounts.unlock(password, ’‘, signingkey)
            const kp = accounts.keypair('', signingkey)
            const r = JWS.verify(
                vcJwt,
                kp[0], //publicKey,
                {'complete': true} 
            )
            response = {
                'code': 200,
                'body': r
            }
        }
    }catch(err) {
        console.log("error:", err)
        response = {
            'code': 500,
        }
    }
    var koaResponse: Koa.Response;
    koaResponse.set('Content-Type', 'application/json');
    koaResponse.set('Access-Control-Allow-Origin', '*');
    koaResponse.status = Response.toHttpStatus(200);
    koaResponse.body = response;
});

VC-Holder 示例

向DID-Holder申请VC

import * as path from 'path'
import * as crypto from 'crypto';
import {Accounts} from 'qihoo-did-sdk'

const keystore = "./keystore"
const role = "vc-holder"
const vcInfo = {
    'name': 'name',
    'contact': 'contact',
    'address': 'address',
}
const proofPurpose = ''
const vcJWKFile = path.join(keystore, `vcJWK.json`)
const vcInfoFile = path.join(keystore, `vcInfo.json`)

const accounts = new Accounts(keystore)

const ethers = require('ethers');
let mnemonic = ethers.utils.HDNode.entropyToMnemonic(ethers.utils.randomBytes(16));
await accounts.new(mnemonic, '', role)

var publicKey: any;
var privateKey: any;
[publicKey, privateKey] = await accounts.unlock('', '', role)
var vcJWK = {
    'publicKey': publicKey,
    'privateKey': privateKey,
    'vcid': '',
}

const hash = crypto.createHash('sha256').update(Buffer.from(JSON.stringify(vcInfo))).digest().toString('hex');
const signature = await accounts.sign(hash, '', '', role)

const applyVC = {
    'type': ['VerifiableCredential'],
    'credentialSubject': {
        'id': publicKey,
        'alumniOf': storeInfo,
    },
    'proof': {
        'proofPurpose': proofPurpose,
    },
    'signature': signature,
}
const applyInfo = {
    'status':'pending',
    'vc': applyVC
}
fs.writeFileSync(vcInfoFile, JSON.stringify(applyInfo), { encoding: 'utf8' });
fs.writeFileSync(vcJWKFile, JSON.stringify(vcJWK), { encoding: 'utf8' });
console.log(`vc apply ok`);

选择性的披露VC

import * as path from 'path'
import {Claims} from 'qihoo-did-sdk'

const keystore = "./keystore"
const vcHiddens = {"hiddens":[false,true,false]}

const vcInfoFile = path.join(keystore, `vcInfo.json`)
const vcShowFile = path.join(keystore, `vcShow.json`)
const vcInfo = require(vcInfoFile)

if (vcInfo.status == "created") {
    let c = new Claims();
    let claims = vcInfo.vc.credentialSubject.claims;
    c.build(claims);
    if (vcHiddens) {
        for (let i in vcHiddens.hiddens) {
            if (vcHiddens.hiddens[i]) {
                c.hide(Number(i));
            }
        }
    }
    let props = c.present();
    if (c.root() == vcInfo.vc.credentialSubject.alumniOf) {
        let vcShow = {
            'vcid': vcInfo.vc.id,
            'vcjwk': vcInfo.vc.credentialSubject.id,
            'issuer': vcInfo.vc.issuer,
            'issuanceDate': vcInfo.vc.issuanceDate,
            'expireDate': vcInfo.vc.expireDate,
            'claims': props,
            'verificationMethod': vcInfo.vc.proof.verificationMethod,
            'jws': vcInfo.vc.proof.jws,
            'proofPurpose': vcInfo.vc.proof.proofPurpose,
        }
        fs.writeFileSync(vcShowFile, JSON.stringify(vcShow), { encoding: 'utf8' });
        console.log(JSON.stringify(vcShow))
    } else {
        console.log(`vc hash error`);
    }
} else {
    console.log(`vc status error`);
}

根据收到的nonce返回签名信息

import * as path from 'path'
import * as Koa from 'koa';
import * as Router from 'koa-router';
import {Accounts} from 'qihoo-did-sdk'

const keystore = "./keystore"
const vcShowFile = path.join(keystore, `vcShow.json`)

const router = new Router();
router.post('/sign', async (ctx:any, _next:any) => {
    const request = <Buffer>ctx.body
    const requestObj = JSON.parse(request.toString())
    const nonce = requestObj.nonce

    const hash = crypto.createHash('sha256').update(Buffer.from(nonce)).digest().toString('hex');
    const accounts = new Accounts(keystore);
    const vcShow = require(vcShowFile)
    const signature = await accounts.sign(hash, '', vcShow.vcjwk, '')
    let body: {
        'code': 200,
        'nonce': nonce,
        'signature': signature,
    }
    var koaResponse: Koa.Response;
    koaResponse.set('Content-Type', 'application/json');
    koaResponse.set('Access-Control-Allow-Origin', '*');
    koaResponse.status = Response.toHttpStatus(200);
    koaResponse.body = body;
});

VC-Verifier 示例

通过resolver验证VC-issuer(即DID-Holder)

const keystore = "./keystore"
const resolver_service_url = "http://10.209.214.196:3000"
const vcShowFile = path.join(keystore, `vcShow.json`)
const vcShow = require(vcShowFile)

const issuer = vcShow.issuer
const jws = vcShow.jws
const vcjwk = vcShow.vcjwk

axios.get(`${vc_holder_service_url}/${issuer}`
}).then(function (response) {
    if(response.data.code === 200){
        const vcServiceAddress = response.data.didDocument.service[0].serviceEndpoint;
        console.log(`vcServiceAddress: ${vcServiceAddress}`);
        axios.get(`${vcServiceAddress}/vcv/${jws}`
        }).then(function (response) {
            if(response.data.code === 200){
                console.log(`verify vc ok:` + JSON.stringify(response.data));
            } else {
                console.log(`verify vc fail:` + JSON.stringify(response.data));
            }
        })
        .catch(function (error) {
            console.log(JSON.stringify(error));
            process.exit(1)
        });
    }else{
        console.log(`failed to send request with error ${JSON.stringify(response.data)}`)
        process.exit(1)
    }
})
.catch(function (error) {
    console.log(JSON.stringify(error));
    process.exit(1)
});

通过chanellege-response的模式验证VC

import axios from 'axios'
import * as path from 'path'
import {Accounts} from 'qihoo-did-sdk'
import {v1} from 'uuid';

const randoms = [
    0x11, 0x92, 0x57, 0xbe, 0xc5, 0xfb, 0xc2, 0xea,
    0x72, 0xb5, 0xef, 0xe2, 0x68, 0x1c, 0x59, 0x37
];
const keystore = "./keystore"
const vc_holder_service_url = 'http://127.0.0.1:13002'
const vcShowFile = path.join(keystore, `vcShow.json`)
const vcShow = require(vcShowFile)

const nonce = v1({
    msecs: new Date().getTime(),
    nsecs: 1234,
    rng: () => randoms,
});
axios.post(vc_holder_service_url + '/sign', {
    'nonce': nonce,
}).then(function (response) {
    if(response.data.code === 200){
        const signature = response.data.signature;
        const accounts = new Accounts(keystore);
        const hash = crypto.createHash('sha256').update(Buffer.from(nonce)).digest().toString('hex');
        const signatureValid = accounts.verify(signature, hash, vcShow.vcjwk);
        console.log(`verify signature: ${signatureValid}`);
    }else{
        console.log(`failed to send request with error ${JSON.stringify(response.data)}`)
        process.exit(1)
    }
})
.catch(function (error) {
    console.log(JSON.stringify(error));
    process.exit(1)
});