LoginSignup
47
27

More than 5 years have passed since last update.

Ethereumのアドレス生成アルゴリズム

Last updated at Posted at 2017-12-18

これはBlockChain Advent Calendar 2017で書いた記事です。

この記事ではEthereumのアドレス生成アルゴリズムを実装を交えて紹介します。
実装は主にSSLプロトコル・TLSプロトコルのオープンソースであるOpenSSLを利用しています。

アドレス生成の流れ

さっそくEthereumのアドレス生成の流れを見ていきましょう。以下のステップでEthereumのアドレスを生成することができます。

  • ランダムな秘密鍵の生成
  • 秘密鍵からECDSA(楕円曲線DSA)を用いて、公開鍵の導出
  • 公開鍵をハッシュ関数Keccak-256に通し公開鍵ハッシュの生成
  • 公開鍵ハッシュの後ろ20バイトを抜き出しアドレスとする

それでは実際にプログラムでEthereumのアドレスを生成していきます。

ECDSAによる鍵ペアの生成

EthereumではEthereumネットワークへのアクセス制御のために公開鍵暗号のECDSA(楕円曲線DSA)を利用しています。ECDSAの鍵ペアをOpenSSLを利用し導出していきます。

ECDSAではまずどの楕円曲線を利用するかを指定する必要があります。OpenSSLで扱える楕円曲線のリストを表示してみましょう。

$ openssl ecparam -list_curves
  secp112r1 : SECG/WTLS curve over a 112 bit prime field
  secp112r2 : SECG curve over a 112 bit prime field
  secp128r1 : SECG curve over a 128 bit prime field
  secp128r2 : SECG curve over a 128 bit prime field
  secp160k1 : SECG curve over a 160 bit prime field
  secp160r1 : SECG curve over a 160 bit prime field
  secp160r2 : SECG/WTLS curve over a 160 bit prime field
  secp192k1 : SECG curve over a 192 bit prime field
  secp224k1 : SECG curve over a 224 bit prime field
  secp224r1 : NIST/SECG curve over a 224 bit prime field
  secp256k1 : SECG curve over a 256 bit prime field
  secp384r1 : NIST/SECG curve over a 384 bit prime field
()

EthereumではBitcoinと同じsecp256k1曲線を利用しています。これはSEC(Standards for Efficient Cryptography)で標準パラメータとして策定されており( http://www.secg.org/sec2-v2.pdf )、Bitcoinで利用されてから人気があるようです。

opensslのecparamコマンドでsecp256k1曲線のパラメータを表示してみます。base64にエンコードされた形でsecp256k1曲線のパラメータが表示されます。詳細なパラメータを参照したい方はこちらをご覧ください。

$ openssl ecparam -name secp256k1
-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----

それでは秘密鍵を作成してみます。opensslのecparamコマンドを利用し、pem形式の秘密鍵を作成します。

$ openssl ecparam -name secp256k1 -genkey > privkey.pem
$ cat privkey.pem
-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIPcpcx7I4sgaM0+oadEvaGhx2M2ZuhbxWlqEzH9LHZN6oAcGBSuBBAAK
oUQDQgAE9pSf3D/Zbvgo78JVwVK509nVyrrWI1oa9sx1UEox6hsvsLKjiJ3WO/Sb
9E97BhCcqz8j78zmxTnOosbT1OLZmA==
-----END EC PRIVATE KEY-----

これでランダムな秘密鍵を導出することができました。次に作成したpem形式の秘密鍵を利用し、opensslのecコマンドで公開鍵を作成します。

$ openssl ec < privkey.pem -text -noout > Key
$ cat Key
read EC key
Private-Key: (256 bit)
priv:
    00:f7:29:73:1e:c8:e2:c8:1a:33:4f:a8:69:d1:2f:
    68:68:71:d8:cd:99:ba:16:f1:5a:5a:84:cc:7f:4b:
    1d:93:7a
pub:
    04:f6:94:9f:dc:3f:d9:6e:f8:28:ef:c2:55:c1:52:
    b9:d3:d9:d5:ca:ba:d6:23:5a:1a:f6:cc:75:50:4a:
    31:ea:1b:2f:b0:b2:a3:88:9d:d6:3b:f4:9b:f4:4f:
    7b:06:10:9c:ab:3f:23:ef:cc:e6:c5:39:ce:a2:c6:
    d3:d4:e2:d9:98
ASN1 OID: secp256k1

16進数形式の秘密鍵と公開鍵のペアを作成することができました。公開鍵は65バイトの0x04から始まる文字列となります。
Bitcoinの場合、公開鍵を圧縮して利用しますが、Ethereumでは非圧縮のまま利用します。

$ cat Key | grep pub -A 5 | tail -n +2 | tr -d '\n[:space:]:' | sed 's/^04//' > pub
$ cat pub
f6949fdc3fd96ef828efc255c152b9d3d9d5cabad6235a1af6cc75504a31ea1b2fb0b2a3889dd63bf49bf44f7b06109cab3f23efcce6c539cea2c6d3d4e2d998

ECDSAで導出した公開鍵は先頭に非圧縮形式を表す0x04のプレフィックスが付きます。ハッシュ値を計算する際これを除く必要があるため、上記の工程で先頭の0x04を削除しています。

公開鍵からアドレスを導出する

公開鍵からアドレスを生成するには、まず公開鍵のハッシュを生成する必要があります。Ethereumでは公開鍵のハッシュ化のためのハッシュ関数にKeccak-256が利用されています。
Keccak-256はSHA-3として選定されたアルゴリズムですが、EthereumではオリジナルのKeccak-256が利用されており、SHA-3標準とアルゴリズムが異なるため注意が必要です。
https://ethereum.stackexchange.com/questions/550/which-cryptographic-hash-function-does-ethereum-use

今回はKeccak-256に以下のライブラリを利用しています。
https://github.com/maandree/sha3su

先ほど生成した公開鍵をKeccak-256でハッシュ化します。

$ cat pub | keccak-256sum -x -l | tr -d ' -' > pubHash
$ cat pubHash
e8814fc9d1265ca4cc1b111b8b5d4078bc122cb300e667b3cd64b1fd5d841240

公開鍵のハッシュが作成できました。Ethereumのアドレスには後ろの20バイトが必要なので、前の12バイトを削除します。
tailコマンドを利用して後ろから40文字(20バイト)を抜き出します。(1文字は改行分)

$ cat pubHash | tail -c 41
8b5d4078bc122cb300e667b3cd64b1fd5d841240

これで、Ethereumのアドレスが生成できました。

0x8b5d4078bc122cb300e667b3cd64b1fd5d841240

16進数を表すためのプレフィックスとして0xを付けています。

Gethに秘密鍵をインポートする

実装の確認のために、Gethに秘密鍵をインポートしてアドレスを導出してみます。
先ほど生成した16進数の鍵ペアから秘密鍵を抜き出します。ここで公開鍵と同様に、先頭の0x00を削除する必要があります。

$ cat Key | grep priv -A 3 | tail -n +2 | tr -d '\n[:space:]:' | sed 's/^00//' > priv
$ cat priv
f729731ec8e2c81a334fa869d12f686871d8cd99ba16f15a5a84cc7f4b1d937a

抜き出した秘密鍵をGethのaccountコマンドでインポートしアドレスを生成します。

$ geth account import priv
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {8b5d4078bc122cb300e667b3cd64b1fd5d841240}

先ほどと同じアドレスの値が導出できました。うまくできているみたいですね。

おわりに

Ethereumのアドレス生成アルゴリズムを実装を交えて紹介しました。
今回は実装しませんでしたが、EthereumにもBitcoinのようにアドレスをエンコードする仕様が存在します。
https://github.com/ethereum/wiki/wiki/ICAP:-Inter-exchange-Client-Address-Protocol
ウォレットを作成する場合は、こちらに対応するとユーザービリティの向上に繋がります。

参考

Generating a usable Ethereum wallet and its corresponding keys
https://kobl.one/blog/create-full-ethereum-keypair-and-address/

maandree/libkeccak
https://github.com/maandree/libkeccak

$ git clone https://github.com/maandree/libkeccak.git
$ cd libkeccak
$ make LIBEXT=dylib LIBFLAGS=-dynamiclib
$ sudo make install

maandree/sha3sum
https://github.com/maandree/sha3su

$ git clone https://github.com/maandree/sha3sum.git
$ cd sha3sum
$ make
$ sudo make install
47
27
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
47
27