본문 바로가기
BlockChain

DID 스터디 2회차

by ybs 2023. 2. 25.
반응형

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 주문서)를 식별하는 식별자로만 사용 할 수 있고, 해당 객체를 인증하기 위해서는 별도의 인증 수단(네이버 회원 조회)이 필요하다.

 

하지만 DIDDID 를 사용하는 객체에 대한 식별자로 사용될 뿐만 아니라 인증 수단인 DID document 를 참조할 수 있는 URI 역할까지 동시에 수행한다. 다시말해 DIDDID document 의 위치를 나타낼 수 있는 주소다.

 

DIDDID 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. 다음 계획

증명서 검증

 

 

 

반응형