🔑

Python で学ぶビットコインの WIF(Wallet Import Format)アルゴリズム

2023/04/15に公開

WIF とは

WIF[1] は、BIP178[2] で定義されているビットコインで使用される ECDSA 秘密鍵をエンコーディングする方法です。WIF は秘密鍵のバックアップやウォレットへ秘密鍵をインポートする際のフォーマットとして利用されています。

秘密鍵 -> WIF

ビットコインの秘密鍵は、乱数から作成された 256bit の整数です。通常は、16 進数で表現するので、64 桁の英数字が並んでいます。

# 秘密鍵の例
0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D
  1. mainnet 用秘密鍵には、0x80 をプレフィックスとして付加します。testnet 用秘密鍵には、0xef をプレフィックスとして付加します。(公開鍵が圧縮形式のときは、さらに 0x01 を末尾に付加します。)
# mainnet
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D
  1. プレフィックスを付けた秘密鍵にハッシュ関数(SHA-256)を2回適用します。
# 1回目
8147786C4D15106333BF278D71DADAF1079EF2D2440A4DDE37D747DED5403592
# 2回目
507A5B8DFED0FC6FE8801743720CEDEC06AA5C6FCA72B07C49964492FB98A714
  1. ハッシュ値の先頭 4bytes をチェックサムとして「1.」の末尾に付加します。
# 先頭 4bytes
507A5B8D

# チェックサムを付加した値
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D507A5B8D
  1. 最後に、Base58Check エンコーディングを行います。
5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ

WIF -> 秘密鍵

WIF から秘密鍵にデコーディングするには、エンコーディングを逆順に実行する必要があります。

  1. Base58Check デコーディングを行います。
# WIF
5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ

# Decoded WIF
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D507A5B8D
  1. チェックサムである末尾の 4bytes を取り除きます。
800C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D
  1. 「2.」で得られた秘密鍵が正しいか検証するために、SHA-256 を2回適用します。
# 1回目
8147786C4D15106333BF278D71DADAF1079EF2D2440A4DDE37D747DED5403592
# 2回目
507A5B8DFED0FC6FE8801743720CEDEC06AA5C6FCA72B07C49964492FB98A714
  1. 「3.」で得られたハッシュ値の先頭 4bytes と「2.」で取り除いた値を比較します。同じであれば、正しい秘密鍵であるとわかります。
507A5B8D = 507A5B8D

実験

ビットコインのアドレス作成ができる Python ライブラリ bitcoinaddress[3] を使用します。

pip3 install bitcoinaddress
>>> from bitcoinaddress import Wallet
>>> wallet = Wallet()
>>> print(wallet)

Private Key HEX: c8e1a0d444b9329bc6d55447ba5c9ca264b8b1cda0b2e55507c7b41b4419316e
Private Key WIF: 5KLkpskNAaxqpFRQzjayZ9hTdQtvdsYvZ2XjthvwK7ic8pCtZr2
Private Key WIF compressed: L3xCSJzQRJuUTYekupxxcuPaXiUBjk7uNMkgKDf4ixee53mmP3Fh 
            
Public Key: 0476131553c4252d5055f5d0185be5cf42597875b7a65139fc73da3d6cb8d0acb5c98aafd9ad7d98d3a0147bdd64276d22f60693feb9e9959c1f372c3e9f6c7926 
Public Key compressed: 0276131553c4252d5055f5d0185be5cf42597875b7a65139fc73da3d6cb8d0acb5

Public Address 1: 1JcfvrNbFyGSnHyjCTHRs6sCW7d7Ya5Lhi   
Public Address 1 compressed: 12LD7nUPrE2Qa218zQxqvxKnV38ct6Ho7G   
Public Address 3: 37mEowyMRmveHvb1ZWLmFGZi36pzbntjM4  
Public Address bc1 P2WPKH: bc1qp6dzfnups79z44xfe4ta02r9t6sj4v732rdwct    
Public Address bc1 P2WSH: bc1qs8psvr8z8s8hagl95zrlqmtuxqds9hha0ch7h5wgek3efst42wjsf55aav

bitcoinaddress を使用すると、鍵一式を作ってくれるみたいです。今回は Private Key HEX を使用して、上記の手順通りに WIF を作ってみて Private Key WIF と一致することを確認したいと思います。

0x80 を付加した値を SHA-256 に2回適用します。ハッシュ値を求めるには、Python のライブラリを使用してもいいですが、面倒なので Web サイト[4] を使用します。

# 秘密鍵
c8e1a0d444b9329bc6d55447ba5c9ca264b8b1cda0b2e55507c7b41b4419316e
80c8e1a0d444b9329bc6d55447ba5c9ca264b8b1cda0b2e55507c7b41b4419316e

# 1回目
8068F65E851C448438058A40080DC1151F8CB022B1269EB9066963D7675335D9

# 2回目
E497D5E376A78124568E73C28BDA1426D8FFD86CB1D6BDC1EFB4B80B4B49839B

# 先頭 4bytes
E497D5E3

秘密鍵の末尾に 62a04327 を付加した値を Base58Check エンコーディング(Base58 エンコーディングしてくれる Web サイト[5] を使用)します。

80c8e1a0d444b9329bc6d55447ba5c9ca264b8b1cda0b2e55507c7b41b4419316eE497D5E3

# Base58Check エンコーディング結果
5KLkpskNAaxqpFRQzjayZ9hTdQtvdsYvZ2XjthvwK7ic8pCtZr2

最初に導出した WIF 秘密鍵と同じ値になりました。

参考文献

脚注
  1. https://en.bitcoin.it/wiki/Wallet_import_format ↩︎

  2. https://en.bitcoin.it/wiki/BIP_0178 ↩︎

  3. https://github.com/fortesp/bitcoinaddress ↩︎

  4. https://www.pelock.com/products/hash-calculator ↩︎

  5. https://learnmeabitcoin.com/technical/base58 ↩︎

Discussion