仮想通貨の仕組みについての勉強の続き。
仮想通貨にはなくてはならないデジタル署名を実装して勉強した。
デジタル署名自体は目新しいものではなく昔からある技術で、今でもネット決済など広く使われている。
デジタル署名とは
ザックリとした説明
デジタル署名とは、その名の通り文書に「署名」すること。(ここでいう文書とは文字列や数字列といったデータのこと)
では署名に必要な要件とは何か。
- 第一に、署名をすることができるのは署名者(アリスとする)だけだが、署名を見た人は誰でもそれが有効であることが確認できなければいけない。
- 第二に、署名と文書は密接に結びついており、別の文書に対するアリスの保証を示すためには使えない。つまり色々な署名からアリスの署名の仕方を推測すること(本当の署名でいうところの筆跡など真似に相当するような行為)、はできないということ。
という2つの要件が必要になってくる。
踏み込んだ説明
暗号理論を使えば上の条件が実現できる。
公開鍵暗号を用いる方法だ。(公開鍵暗号については RSA暗号を実装してみる(知識編) - 技術メモ )
具体的にはデジタル署名には3つのスキームで成り立つ。
署名者(アリスとする)はgenerateKeyを使って秘密鍵skと公開鍵pkを生成し、公開鍵を公開する。
アリスは署名したい文書Dataをハッシュ関数にかけたうえで秘密鍵skを使って署名signatureを生成する。
承認者は公開されている公開鍵pk・文書Data・アリスから受け取った署名signatureを使って、pk,Data,signatureの組が正当であればTrueを返し違っていればFalseを返す。署名が間違っているか文書が書き換えられていれば承認がうまくいかないことになる。
普通の公開鍵暗号方式と違うところは、文書自体はそのまま(暗号化せずに)通信するというところだ。
だから、文書は誰でも見れるものでも良いという事。
文書を見れる人なら誰でも署名と公開鍵さえ渡されればそれがアリスによる署名だとわかる。
デジタル署名に対する攻撃
N通りの{文書、署名}および公開鍵の組み合わせから秘密鍵を見破ることができればデジタル署名は破られてしまう。(chosen message attack)
だが、現在一般に使われている署名方式ではこれを行うのはほぼ不可能に近い。
コード
python (python3.5)で書いた。RSAやハッシュ関数(SHA256)など暗号技術諸々はpycryptoを使った。
ちなみに実際のビットコインでは公開鍵暗号方式にRSAではなく楕円曲線暗号が使われている。
digitalsignature.pyという名前で保存
from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 from Crypto.Hash import SHA256 from base64 import b64decode, b64encode import sys def generate_key(keysize=2048, passphrase = None): new_key = RSA.generate(keysize) public_key = new_key.publickey().exportKey() secret_key = new_key.exportKey(passphrase = passphrase) return secret_key, public_key def sign(secret_key, data, passphrase = None): try: rsakey = RSA.importKey(secret_key, passphrase = passphrase) except ValueError as e: print(e) sys.exit(1) signer = PKCS1_v1_5.new(rsakey) digest = SHA256.new() digest.update(b64decode(data)) sign = signer.sign(digest) return b64encode(sign) def verify(pub_key, signature, data): rsakey = RSA.importKey(pub_key) signer = PKCS1_v1_5.new(rsakey) digest = SHA256.new() digest.update(b64decode(data)) if signer.verify(digest, b64decode(signature)): return True else: return False
テスト
digitalsignature.pyと同じフォルダ内で実行
from digitalsignature import * # 秘密鍵と公開鍵を作る。(パスワードはなくても良い) password = "password" sk, pk = generate_key(passphrase = password) # メッセージに署名する(署名者が行う) message = "hoge" sig = sign(sk, message, passphrase = password) # 承認テスト(承認者が行う) if verify(pk, sig, message): print("承認 OK") else: print("承認 NG") # メッセージの書き換えに対するテスト changed_message = "hogehoge" if verify(pk, sig, changed_message): print("書き換えテスト NG") else: print("書き換えテスト OK") # 承認されなければOK # 間違った秘密鍵の署名に対するテスト sk2, pk2 = generate_key(passphrase = password) sig2 = sign(sk2, message, passphrase = password) if verify(pk, sig2, message): print("不正署名テスト NG") else: print("不正署名テスト OK") # 承認されなければOK
- 実行結果
承認 OK 書き換えテスト OK 不正署名テスト OK
参考サイト
Python PyCrypto: Verify Signature Example.py · GitHub
Python PyCrypto: Sign Data Example.py · GitHub
参考にした本
- 作者: アーヴィンド・ナラヤナン,ジョセフ・ボノー,エドワード・W・フェルテン,アンドリュー・ミラー,スティーヴン・ゴールドフェダー
- 出版社/メーカー: 日経BP社
- 発売日: 2016/12/13
- メディア: Kindle版
- この商品を含むブログを見る