🎃

DIDKit(Node)でVerifiable Credentialsの発行と署名をやってみる

2023/01/10に公開
2

DIDKitを使ってローカル環境でVCの発行と検証を試してみます。

やること

  • DIDKitのビルド
  • DIDKitを使ってVCの発行
  • DIDKitを使ってVCの検証

やらないこと

  • DIDKitの説明
  • Verifiable Credentialsの説明

環境用意

DIDKitのnpmパッケージがあるのですが、バージョンが古い(0.2.1)ので手元でビルドしたものを今回は使います。

使うバージョンはTagがついてないのですが、
https://github.com/spruceid/didkit/commit/b4491bf607eed12b2a21a8fab9a0f3fa89c1c70b
のcommitが反映されているバージョンを使います。

git cloneでソースを落としてきます。

git clone git@github.com:spruceid/didkit.git

DIDKitのリポジトリの他にspruceid/ssiのリポジトリも必要なので落としてきます。
参考: https://github.com/spruceid/didkit#manual

git clone https://github.com/spruceid/ssi --recurse-submodules

Node用にビルドします。
参考: https://github.com/spruceid/didkit/tree/main/lib/web#readme

今回はRustの環境はDockerコンテナで用意します。
build用のスクリプトを作成してwasm-pack buildします。

build.sh
docker run -v $PWD:/app -it --rm rust bash -c "cargo install wasm-pack && cd /app/didkit/lib/web && wasm-pack build --out-dir /app/didkit-wasm-node --target nodejs"
sh build.sh

これでDIDKitのNodeパッケージが生成されて、ディレクトリとしては以下の状態になっています。

VCの発行

JWK生成

最初にJWKファイルを生成しておきます。

index.js
const DIDKit = require("./didkit-wasm-node");
const fs = require("fs");

async function main() {
  generateKey()
}

function generateKey() {
  const key = DIDKit.generateEd25519Key()
  fs.writeFileSync("issuer_key.jwk", key)
}

main().then((res) => {
  
}).catch((err) => {
  console.log("err", err)
})
node index.js

以下のような、JWKが生成されます。

issuer_key.jwk
{"kty":"OKP","crv":"Ed25519","x":"52_9TqZnoF9CC-QjMh1RoVMg4hQNC4rvvvJi0dNNhBE","d":"qP8nGCOrYJ4J4mTzcA--oM8iTPzV_cImhEiR1Zy3f3k"}

VC署名

署名前のVCのJSONを用意した後に、issuer_key.jwkで署名します。
参考: https://www.spruceid.dev/quickstart

index.js
const DIDKit = require("./didkit-wasm-node");
const fs = require("fs");
const crypto = require("crypto")

async function main() {
  // generateKey()

  const key = fs.readFileSync("issuer_key.jwk").toString()
  const issuerDid = DIDKit.keyToDID("key", key)

  const unsignedVc = {
    "@context": "https://www.w3.org/2018/credentials/v1",
    "id": `urn:uuid:${crypto.randomUUID()}`,
    "type": ["VerifiableCredential"],
    "issuer": issuerDid,
    "issuanceDate": "2023-01-08T18:23:56Z",
    "credentialSubject": {
      "id": "did:example:my-data-subject-identifier"
    }
  }

  const proofOptions = {};
  const signedVc = await DIDKit.issueCredential(
    JSON.stringify(unsignedVc),
    JSON.stringify(proofOptions),
    key
  )

  fs.writeFileSync("signed-vc.json", signedVc)
}

function generateKey() {
  const key = DIDKit.generateEd25519Key()
  fs.writeFileSync("issuer_key.jwk", key)
}

main().then((res) => {

}).catch((err) => {
  console.log("err", err)
})

以下のようなjsonファイルが出力されます。
これでVCの発行に成功しました。

signed-vc.json
{
  "@context": "https://www.w3.org/2018/credentials/v1",
  "id": "urn:uuid:86a109aa-a3f6-4374-b46f-92c58fcb16a1",
  "type": [
    "VerifiableCredential"
  ],
  "credentialSubject": {
    "id": "did:example:my-data-subject-identifier"
  },
  "issuer": "did:key:z6Mkv2hGUtUdKdEVdqc7esowafyriuqPvxQFnVTrRqjMknj2",
  "issuanceDate": "2023-01-08T18:23:56Z",
  "proof": {
    "type": "Ed25519Signature2018",
    "proofPurpose": "assertionMethod",
    "verificationMethod": "did:key:z6Mkv2hGUtUdKdEVdqc7esowafyriuqPvxQFnVTrRqjMknj2#z6Mkv2hGUtUdKdEVdqc7esowafyriuqPvxQFnVTrRqjMknj2",
    "created": "2023-01-08T07:43:50.818Z",
    "jws": "eyJhbGciOiJFZERTQSIsImNyaXQiOlsiYjY0Il0sImI2NCI6ZmFsc2V9..jyR9O8nb-ino0TXSCAhUdP2Z9iBc0E-aX7tyTHcFuOzOGd_uWpwHhA4gTOX961SUHB0un34e2YV41qc2lk0nCw"
  }
}

VCの検証

先程生成したsigned-vc.jsonを検証します。

verify.js
const DIDKit = require("./didkit-wasm-node");
const fs = require("fs");

async function main() {
  const signedVc = fs.readFileSync("signed-vc.json").toString()

  const proofOptions = {};
  const result = await DIDKit.verifyCredential(
    signedVc,
    JSON.stringify(proofOptions)
  )

  console.log("result", result)
}

main().then((res) => {

}).catch((err) => {
  console.log("err", err)
})

これで検証に成功しました。

hide@hidenoMacBook-Pro didkit_vc % node verify.js
result {"checks":["proof"],"warnings":[],"errors":[]}

試しに、signed-vc.jsonを改ざんして検証をしてみます。
signed-vc.jsonissuanceDateが2023-01-08だったのを2023-01-09に変更してみます。

"issuanceDate": "2023-01-08T18:23:56Z",
↓
"issuanceDate": "2023-01-09T18:23:56Z",

すると、以下のように検証に失敗することが確認できました。

hide@hidenoMacBook-Pro didkit_vc % node verify.js
result {"checks":["proof"],"warnings":[],"errors":["signature error: Verification equation was not satisfied"]}

Discussion

ruharuha

初めまして、記事を閲覧させていただきました。私は現在VCを用いた、DAppの開発をしており、本記事を参考にさせていただいております。同じようにフォルダも作って見ました。コマンドライン上では、上手く動き、unsigned-vcの作成から、署名、認証まで同じように行うことができました。このプログラムをベースにし、Javascriptで、このVCの認証システムをブラウザ上で動かしたいと思ったのですが、上手くいきません(VCの情報を投げると認証するようなシステムです)。具体的にはそもそも、コンソールでエラーが出ており、認証以前の段階です(requireがダメだったり、Failed to resolve module specifier "util"だったりです。)。いろいろ調べて見たのですが、キーワードとしては、ES Modules、CommonJS、Node.js上でビルドしているから動かない、など様々な情報が出てきました。プログラムの経験は浅く、Web3.0の勉強は3ヶ月ほどであり、分からないところが多いです。少しでもお力添えいただけると嬉しいです。

Hide(ひで)Hide(ひで)

コメントありがとうございます。

確かにこの記事ではNode.jsで動かす用に書いているので、ブラウザで使用しようとするとエラーがでるかと思います。
なので、didkitでブラウザで検証できるようにするためには、ES Modules形式でJSを書いたり、wasmなのでJSとはまた少し違った対処が必要だったりします。

Next.jsを使っていますが、一応ブラウザでVCを検証できるような記事を書いてみました。
https://zenn.dev/hid3/articles/8cf5ed69ba75ee
もし少しでも役に立てば幸いです。