単純なdid記述としてdid:keyというmethodがある。その仕様書はThe did:key Method v0.7にあり、具体的には、以下のように書けるそうだ。
did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N
これが何を意味しているかは、universal resolverに放り込むとわかるそうで、放り込んだ結果は以下のとおりである。
EcdsaSecp256k1VerificationKey2019
{
"kty":"EC",
"crv":"secp256k1",
"x":"tS0TJpT9-UUpJvjMZUyA0C0oI9l7VW8d2ADptYRJVdM",
"y":"RQEb5Z7oO52oHNpYk9lbbuwZmA_GFNenqSjX4joDh-A"
}
この変換に関する理論的な解説は、以下の記事に詳しいものの、具体的にsecp256k1 jwkからdid:keyを算出方法が知りたかったので調べてみた。
DID Method 探訪:The did:key Method
上記記事によると、base58を使う場合のkey formatは下記の通りになるとのこと。
"did:key:" + "z" + base58(multicodec + pubKey)
step1 multicodeを決定
まずsecp256k1 jwkから、did:keyを算出するためには、The did:key Method v0.7仕様に従って、multicodeを決める必要がある。下記の表に従えば、secp256k1は、0xe7
である。これをunsigned variant表記にすると0xe701
となる。
step2 pubkeyを決定
0xe7
の定義にあるように、pukeyには、compressedのkey formatを記載すればいいことがわかる。compressedのkey formatに関しては、「learn me a bitcoin」に詳しいが、簡単にいえばy値がoddのときは0x03+x値, y値がevenのときは、0x02+x値となる。
上記のsecp256k1 jwkをhex表記に変更
"x":"tS0TJpT9-UUpJvjMZUyA0C0oI9l7VW8d2ADptYRJVdM"
"y":"RQEb5Z7oO52oHNpYk9lbbuwZmA_GFNenqSjX4joDh-A"
→
"x": 0xB52D132694FDF9452926F8CC654C80D02D2823D97B556F1DD800E9B5844955D3
"y": 0x45011BE59EE83B9DA81CDA5893D95B6EEC19980FC614D7A7A928D7E23A0387E0
"y"値が偶数なので、compressed なkey formatは次のようになる
0x02 B52D132694FDF9452926F8CC654C80D02D2823D97B556F1DD800E9B5844955D3
step3 did:keyを作る
secp256k1のdid:key formatにstep1, step2の結果を入れると以下のようになる
did:key: + "z" + base58("E701"+
"02B52D132694FDF9452926F8CC654C80D02D2823D97B556F1DD800E9B5844955D3")
base58エンコードすると以下のようになり、仕様書に書かれていたsample値(did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N
)と一致する
did:key: + "z" + Q3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N
サンプルコード
上記ステップをコーディングし、動作を確認してみた
実行
node server.js
→
--------generate key for did -------------------
d : XoxqzQ/r9P/XCu/RdL8cx/x94boZuoFf0izq//RevN4=
x (b64): BzYnIwfodS6WVAIc3JTbNfPOqNUxz9s3d+JpdEBVckM=
y (b64): OOaOapbUUCEzQJaXhF9CBovBB3pLkQIGUE1/W4Gkyzk=
-----------------------------
did -> did:key:zQ3shf8FsfJSvDGmM4GNmhgNocBQfUkEZwAAHTCNb7Kkicpw4
const crypto = require('crypto');
const elliptic = require('elliptic');
const base58 = require('bs58');
let did = createId();
console.log("did -> "+did)
function createId(){
const key = crypto.randomBytes(32).toString("hex");
const ec = new elliptic.ec('secp256k1');
const prv = ec.keyFromPrivate(key, 'hex');
const pub = prv.getPublic();
const ecjwk = {
kty:"EC",
crv:"secp256k1",
"x":pub.x.toBuffer().toString('base64'),
"y":pub.y.toBuffer().toString('base64')
};
const ecjwkpri = {
kty:"EC",
crv:"secp256k1",
"x":pub.x.toBuffer().toString('base64'),
"y":pub.y.toBuffer().toString('base64'),
"d":prv.getPrivate().toBuffer().toString('base64')
};
console.log("--------generate key for did -------------------")
console.log(` d : ${prv.getPrivate().toBuffer().toString('base64')}`);
console.log(` x (b64): ${pub.x.toBuffer().toString('base64')}`);
console.log(` y (b64): ${pub.y.toBuffer().toString('base64')}`);
console.log("-----------------------------\n")
let _key={"publicJwk":ecjwk,"privateJwk":ecjwkpri};
let yhex = pub.y.toBuffer().toString('hex')
if ((parseInt(yhex.slice( -1 ), 16) %2)==1){
parity_flag = "03";
}else{
parity_flag = "02";
}
let tmp = "E701"+parity_flag+pub.x.toBuffer().toString('hex');
let _did = "did:key:z"+base58.encode(Buffer.from(tmp, 'hex'));
return _did;
}