4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

秘密鍵を WIF 形式に変換する処理を Python で実装する

Last updated at Posted at 2017-10-31

はじめに

巷でビットコインやブロックチェーンというものが流行っているみたいですね。流行に乗り遅れないように (今さらとか言わないでね :joy:)、最近 ビットコインとブロックチェーン という本を読み始めました。

この本の中で WIF 形式 (Wallet Import Format) というフォーマットが登場しました。ビットコインのウォレットでは秘密鍵をこの形式で扱います。今回、復習を兼ねて秘密鍵を WIF 形式に変換する処理を Python で実装してみました。

実装

具体的には Bitcoin Wiki の Wallet import format というページに書かれている内容をコードに起こしました。

from base58 import b58encode, b58decode
from binascii import hexlify, unhexlify
from hashlib import sha256
import unittest


def encode_private_key_as_wif(private_key):
  """
  秘密鍵 (16 進数文字列) を WIF 形式に変換する。
  """

  # 先頭の 0x80 は秘密鍵をエンコードする際の version byte を表す。
  key = '80' + private_key
  tmp = key
  
  # SHA-256 関数を 2 回適用する。
  tmp = sha256(unhexlify(tmp)).hexdigest()
  tmp = sha256(unhexlify(tmp)).hexdigest()

  # 末尾の 8 バイトをチェックサムとする。
  checksum = tmp[0:8]

  wif_private_key = b58encode(unhexlify(key + checksum))

  return wif_private_key


def decode_wif_private_key(wif_private_key):
  """
  秘密鍵 (WIF 形式) を 16 進数文字列に変換する。
  """
  tmp = hexlify(b58decode(wif_private_key)).decode('utf-8')
  # version byte とチェックサムを取り除く。
  private_key = tmp[2:-8]

  # b58decode の代わりに b58decode_check を使う場合
  # tmp = hexlify(b58decode_check(wif_private_key)).decode('utf-8')
  # private_key = tmp[2:]

  return private_key


def verify_checksum(wif_private_key):
  """
  秘密鍵のチェックサムを検証する。
  """
  b58decoded = hexlify(b58decode(wif_private_key)).decode('utf-8')
  tmp = b58decoded[:-8]
  tmp = sha256(unhexlify(tmp)).hexdigest()
  tmp = sha256(unhexlify(tmp)).hexdigest()

  return b58decoded[-8:] == tmp[:8]


class WIFTest(unittest.TestCase):
  """
  WIF 形式に関する関数の動作を検証するための単体テスト。
  検証のための値は https://en.bitcoin.it/wiki/Wallet_import_format から借用している。
  """

  def test_encode_private_key_as_wif(self):
    private_key = '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
    wif_private_key = encode_private_key_as_wif(private_key)

    self.assertEqual('5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ', wif_private_key)

  def test_decode_wif_private_key(self):
    wif_private_key = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
    private_key = decode_wif_private_key(wif_private_key)

    self.assertEqual('0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d', private_key)

  def test_verify_checksum(self):
    valid_wif_private_key = '5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
    invalid_wif_private_key = 'SHueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'

    self.assertTrue(verify_checksum(valid_wif_private_key))
    self.assertFalse(verify_checksum(invalid_wif_private_key))


if __name__ == '__main__':
  unittest.main()

この Python スクリプトを実行するには base58 というライブラリが必要です。

$ pip install base58

なお、スクリプトを実行すると unittest による単体テストが動きます。

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

参考

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?