これはなに?
ビットコインの秘密鍵ってそもそも何から作られているの?WIFってたまに聞くけど何?という疑問をなんとなく解消するための,初心者による初心者のためのメモ書きです."なんとなく"というのはあくまでも作り方がメインであって「なぜその操作をするのか」や「いきさつ」はほとんど扱わないからです.
秘密鍵は何からできている?
ビットコインアドレスにおける秘密鍵とは
$ 1 $ ~ 約 $ 2^{256} $ までの数字のどれか です.
とても大きくて美しい数字が出てきました.$ 2^{256} $ は78桁にも及ぶ非常に大きな数であり、衝突(秘密鍵が被ること)が起こる確率は限りなく0に近いと言えます.
約 $ 2^{256} $とありますが,正確な値は $ 2^{256} $ よりすこし小さな
$ 115792089237316195423570985008687907852837564279074904382605163141518161494336 $
となります.この値が上限となる理由は楕円曲線暗号だとかSecp256k1といったものが絡んでいます.気になる場合は調べると良いでしょう.
以上のことより,もう少し正確に秘密鍵を定義します.
秘密鍵を$p \in \mathbb{N}$とすると
$ 1 \leq p \leq 115792089237316195423570985008687907852837564279074904382605163141518161494336$
ちなみに,範囲外の値を用いるとウォレットに取り込む際に Private key outside allowed range (code -5)
と怒られます.
WIFってなに?
Wallet Import Format
の頭字語
長くて扱いにくい生の秘密鍵(先ほどのただの整数)を変形して便利に表そう,というものです.名前の通りウォレットに取り込む際はこの形式を用います.コピーミスを減らすためチェックサムがついており,以下で示すように可逆的な変換です.
生の秘密鍵をWIFに変形してみる
2019年11月4日追記あり
この例では,この文を書いている今現在のUnix time1572787251
を使います.
①16進数に変換
1572787251
を16進数に変換します.5dbed433
$_{(16)}$ となります.
秘密鍵は256bitである必要があるので適切に0埋めします(16進数なので合計64文字).
000000000000000000000000000000000000000000000000000000005dbed433
②バージョンバイトを先頭に付ける
バージョンバイトとは?: この秘密鍵がメインネットで使われるのか、テストネットで使われるのかを判別するもの
メインネットなら 0x80
,テストネットなら 0xEF
を追加します.分かりやすい表があります.
80000000000000000000000000000000000000000000000000000000005dbed433
なお,ビットコイン以外のオルトコインはバージョンバイトの値が異なる場合があります.
③コンプレッションバイトを後ろに付ける(オプション)
コンプレッションバイトとは?: 圧縮公開鍵を使いたければ後ろに 0x01
を追加する
ここでは圧縮公開鍵についての説明はしません.
---追記はじめ---
現状圧縮鍵(と言いつつbyte数は増えている())の方が主流になっているのと圧縮の意味ではなくなる可能性があります。
とTwitterで教えていただきました.現在アドレスのフォーマットには P2PK, P2PKH, P2SH, P2WPKH, P2WSH, P2SH-P2WPKH など複数あります.これらはすべて同じ秘密鍵から計算した公開鍵を使って生成できますが,WIFな秘密鍵を見ただけではどの種類のアドレスなのか判別できません.そこでsuffix(接尾辞)で判別できるようにしようという提案が為されているようです.分かりやすく説明されている方がいます.
つまり: 本来suffixには圧縮公開鍵か否かを示すものしかなかったが,それ以外の判別用suffixを追加しようとしているためsuffix=コンプレッションバイト
ではなくなる可能性がある
---追記おわり---
80000000000000000000000000000000000000000000000000000000005dbed43301
ここで 0x01
を付けると最終的なWIFがK
かL
で,付けなければ5
で始まります.
④チェックサム
チェックサムとは?: 調べるとたくさん出てきます
チェックサムは2回sha256にかけた値の先頭4byteを用い,それを値の後ろに追加します.
echo -n '値' | xxd -r -p | sha256sum
sha256 1回目
a5ca0c4c17ff8b619920593d2eb4147399b21db61c0619b63e209c269dd0b913
2回目
df0fdd1a514374630c07177dac8bef7194b73529bf33ea9463f7abe862cabaf9
この先頭4byte(16進数なので8文字)を抜きます.df0fdd1a
$_{(16)}$ これを元々の値の後ろに付けます.
80000000000000000000000000000000000000000000000000000000005dbed43301df0fdd1a
⑤Base58 エンコードする
Base58とは?: Base64に似たもの.間違えの元になるいくつかの紛らわしい文字を使わないようにしている.可逆的な変換.
実験的に行うならこのサイトでBase58が使えます.
ところで,実際に使用することを想定して秘密鍵を作るならば絶対にオンライン上に晒さずローカルな環境で行うべきです.
これに関連したよく知られた事実: 安全に秘密鍵を作るならば,施錠可能な何もない部屋に紙と鉛筆と表裏の区別が付くコインのみを持ち込み,そのコインを256回振って秘密鍵を生成したのち手計算のみでアドレスの生成まで一貫して行うべきである.しかし現実的ではないためコンピュータを利用せざるをえない.
KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M93TaondtKu5Nh
これでWIFへの変換は完了です.ウォレットにインポートすると以下のアドレスが得られます(秘密鍵から公開鍵及びアドレスを生成する手順はより複雑です).
15wDihorPChZK9huff49nf7EQoTJYxHNUc
このアドレスは使用可能です(が,もちろん使用するべきではありません).