1. DID 샘플 프로젝트 로컬에서 띄우기
1. web 을 띄워야함(node 서버)
- https://github.com/conanoc/indy-sample-agents
2. npm install 해야함
3. node version 16.13.0 으로 down grade 해야함. -> npm 과 궁합
4. indy-sdk 설치
$ brew tap conanoc/libindy
$ brew install --build-from-source libindy
$ sudo ln -s /opt/homebrew/lib/libindy.dylib /usr/local/lib/libindy.dylib (M1 mac)
- 참고 : https://github.com/hyperledger/indy-sdk/#macos
샘플 프로젝트는 node 서버다. node 서버의 역할은 아래 그림에서 VC 를 발급하는 발행기관(issuer)이고 코로나 백신 접종 증명서를 발급하는 병원 서버라고 생각하면 된다.
2. DID 생성
가장 먼저 DID 를 생성한다. 내부적으론 indy sdk 를 이용해 DID 를 생성한다. DID 는 내부 로컬 sqlite.db 스토리지에 저장된다.
router.post('/did', async function(req, res, next) {
try {
let info = {};
if (req.body.seed && req.body.seed.length == 32) {
info = { seed: req.body.seed };
}
let [did, key] = await indy.createAndStoreMyDid(global.wallet, info);
await indy.setDidMetadata(global.wallet, did, req.body.memo);
global.db.state = common.WalletState.DID_READY;
global.db.did = { did: did, verkey: key };
common.writeDB(global.db);
res.json({ newDID: { did: did, verkey: key, metadata: req.body.memo } });
} catch (e) {
res.json({ error: { message: e.message }});
}
})
DID : GXJGgTJgiSGF281Wr6d4Nt
verkey(publickey) : 9ThBrf6oFx5ao6s7zUD5tum1ctmWLZdaWEK3xvQ8do6d
2-1. DID 개념
DID 는 사용하는 사람 스스로 생성하고 제어할 수 있는 분산형 식별자(key 값이라고도 한다)다.
기존 중앙형 시스템에서는 알아서 식별자가 충돌나지 않게(동일한 key 값이 생성되지 않게) 만들어져 있다.
ex) 2023022400000000
오늘 연월일(20230224) 에다가 00000000 ~ 99999999(auto increase) 를 합쳐.
DID 처럼 분산형으로 key 값을 생성하고 제어하는 구조에선 이렇게 만들수가 없다.
그런데 중앙형, 분산형 워딩이 헷갈릴수 있다. key 값을 만들어내는 sequence generator(채번기) 가 중앙형인지 분산형인지가 관건이다. 아래 그림처럼 애플리케이션 서버들이 분산형인것과는 관계없다.
sequence generator 가 여러개인 상황에서는 동일한 key 값이 생성(충돌)될 수 있기 때문에 UUID(Universally Unique Identifiers) 과 같은걸 사용해서 해결할 수 있다. UUID 는 32개의 16진수와 4개의 하이픈(-) 으로 구성되어 있다. 충돌에 안전한 랜덤함수를 사용하기 때문에 동일한 UUID 가 생성될 확율은 1 / 2^122 이다.
그런데 UUID 는 흔하게 많이 쓰이고 있다. 네이버페이 상품 구매하기 눌러서 나오는 주문서 URL에도 UUID 사용된다.
https://order.pay.naver.com/orderSheet/a1048090-38af-4745-9007-78263d07cf7d/integrationCart
DID 도 UUID 와 마찬가지로 생성 시 충돌은 걱정없다. 그럼 두개의 차이점은 뭘까? UUID 는 객체(ex 주문서)를 식별하는 식별자로만 사용 할 수 있고, 해당 객체를 인증하기 위해서는 별도의 인증 수단(네이버 회원 조회)이 필요하다.
하지만 DID 는 DID 를 사용하는 객체에 대한 식별자로 사용될 뿐만 아니라 인증 수단인 DID document 를 참조할 수 있는 URI 역할까지 동시에 수행한다. 다시말해 DID 는 DID document 의 위치를 나타낼 수 있는 주소다.
DID 는 DID scheme, DID method, method-specific identifier 3가지로 구성되어 있다.
ex did(scheme):btcr(method):aaaa-bbbb-cccc(method-specific identifier, btcr 저장소 내 DID document 가 저장된 주소)
근데 샘플 프로젝트 DID 스펙에서는 scheme, method 생략되어 있다.
DID: GXJGgTJgiSGF281Wr6d4Nt(method-specific identifier)
verkey(publickey): 9ThBrf6oFx5ao6s7zUD5tum1ctmWLZdaWEK3xvQ8do6d
2-2. DID 저장
증명서 발급자(issuer)는 자신의 DID 를 생성했으면 이 DID 를 블록체인에 ENDORSER 권한으로 등록해줘야 한다. 샘플 프로젝트에서는 캐나다 주정부 dev 사이트 를 이용해 DID 를 등록한다.
3. Credential schema 생성
schema 는 코로나 백신 접종 증명서가 담게 될 속성값들을 지정하는 json 데이터다. 아래는 scheme 생성 코드다.
const schemaProperties = ['organization', 'vaccine', 'doses', 'date', 'target'];
router.post('/schema', async function(req, res, next) {
try {
let did = global.db.did.did;
// pool 중요
let poolHandle = await common.openPoolLedger();
let [schemaId, schema] = await indy.issuerCreateSchema(did, 'Covid-Certificate', '0.1', schemaProperties);
let schemaRequest = await indy.buildSchemaRequest(did, schema);
await indy.signAndSubmitRequest(poolHandle, global.wallet, did, schemaRequest)
await indy.closePoolLedger(poolHandle);
global.db.schemaId = schemaId;
global.db.state = common.WalletState.SCHEMA_READY;
common.writeDB(global.db);
res.json({state: global.db.state, schemaId: schemaId});
} catch (e) {
res.json({ error: { message: e.message }});
}
});
Indy 블록체인을 구성하는 노드들의 집합을 Pool이라 표현한다. Pool에 연결하려면 노드 정보를 담고 있는 pool_transactions_genesis json 파일이 필요하다.
async function openPoolLedger() {
let poolConfig = {
"genesis_txn": path.resolve(__dirname, '../pool_transactions_genesis')
};
try {
await indy.createPoolLedgerConfig(global.poolName, poolConfig);
} catch(e) {
if(e.message !== "PoolLedgerConfigAlreadyExistsError") {
throw e;
}
}
await indy.setProtocolVersion(2);
return await indy.openPoolLedger(global.poolName);
}
// pool_transactions_genesis.json 파일
{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","client_ip":"10.0.0.2","client_port":9702,"node_ip":"10.0.0.2","node_port":9701,"services":["VALIDATOR"]}, ...생략 "ver":"1"}
{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node2","client_ip":"10.0.0.2","client_port":9704,"node_ip":"10.0.0.2","node_port":9703,"services":["VALIDATOR"]}, ...생략 "ver":"1"}
{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node3","client_ip":"10.0.0.2","client_port":9706,"node_ip":"10.0.0.2","node_port":9705,"services":["VALIDATOR"]}, ...생략 "ver":"1"}
{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node4","client_ip":"10.0.0.2","client_port":9708,"node_ip":"10.0.0.2","node_port":9707,"services":["VALIDATOR"]}, ...생략 "ver":"1"}
schema json 데이터는 아래와 같다.
"schema": {
"ver": "1.0",
"id": "GXJGgTJgiSGF281Wr6d4Nt:2:Covid-Certificate:0.1",
"name": "Covid-Certificate",
"version": "0.1",
"attrNames": [
"target",
"vaccine",
"organization",
"doses",
"date"
],
"seqNo": 196233
}
4. Credential definition 생성
지금까지 백신 접종 증명서를 발급해주기 위한 템플릿을 만들었다고 보면 된다. 이제 credential definition 를 생성할 단계다.
credential definition 을 생성하면 wallet 내에 증명서를 서명할 private key가 생성된다.
코로나 백신 접종 증명서 발급자(issuer)인 병원은 'issue credential' 과정을 통해 접종자에게 아래 양식에 맞춰 증명서를 발급해준다.
그러면 접증 증명에 대한 qrcode 가 제공된다.
관련 코드는 아래와 같다.
router.post('/credential', async function(req, res, next) {
let definitionId = global.db.definitionId;
let credOffer = await indy.issuerCreateCredentialOffer(global.wallet, definitionId);
let offerId = crypto.randomBytes(8).toString('hex');
global.offers[offerId] = credOffer;
global.credentialContent[offerId] = req.body;
let qrcodeContent = {
"type": "cred_offer",
"title": "Vaccination certificate",
"to": req.body.target,
"server": global.server,
"offer_id": offerId
};
console.log(JSON.stringify(qrcodeContent, null, 2));
let dataUrl = await qrcode.toDataURL(JSON.stringify(qrcodeContent, null, 2));
res.json({qrcode: dataUrl});
});
5. 다음 계획
증명서 검증
'BlockChain' 카테고리의 다른 글
DID 스터디 1회차 (0) | 2023.01.28 |
---|---|
[논문분석] 블록체인 기반 분산 스토리지 시스템Storj Frameup 공격 및 대책 (0) | 2022.11.12 |