Develop with pleasure!

福岡でCloudとかBlockchainとか。

BIP-70のPayment Protocolの検証環境を作ってみた

Bitcoinを決済に利用する際に、決済先との間の中間者攻撃やマルウェアなどに感染した環境で、本当の支払先でないアドレスに送金するといったことが起きないよう、送金先をPKIを使って検証する仕組みがBIP-70で定義されているPayment Protocol↓

techmedia-think.hatenablog.com

bitcoinrbでもこれらのプロトコルに対応しようと思って、動作検証する環境をまず作ってみた。

BIP-70自体にはTest Vectorは掲載されていないけど、BIP-70の著者の1人であるGavin AndresenがPayment Protocolのデモ環境を公開してたのでそれを使ってみる↓

github.com

(以前はbitcoincore.orgのサイトでこれがホストされてたっぽいけど、今はなくなってる。)

Payment Requestのテスト環境のセットアップ

↑では以下のツールが提供されてる。

  • PaymentRequestの作成・検証のためのC++コマンドラインツール
  • PaymentRequeststの作成・検証のためのPHPのコードとデモサイト
  • テスト用の証明書作成シェル

まず以下のライブラリをインストールしておく。

テスト用の証明書とルート認証局の作成

BIP-70はPKIを利用して決済先の情報を検証するので、テスト用にルート認証局と、その認証局が発行した決済先用の証明書を作成する。

↑のリポジトリca_in_a_boxにテスト用の認証局と証明書を作成するスクリプトが用意されてるのでそれを実行するだけ。

$ cd ca_in_a_box
$ ./sh create_ca.sh
CA: Creating self-signed root certificate authority (CA) certificate:
Generating a 2048 bit RSA private key
.....+++
............+++
writing new private key to './private/cakey.pem'
-----
MERCHANT: Creating merchant private key and certificate signing request (CSR):
..++++++
...................++++++
CA: Issuing new merchant certificate
Using configuration from openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'testmerchant.org'
organizationName      :ASN.1 12:'Payment Request Test Merchant'
Certificate is to be certified until Mar 11 05:27:23 2028 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
Done.
 Root CA certificate is certs/cacert.pem
 Merchant certificate is certs/demomerchant.pem , private (signing) key is private/demomerchantkey.pem

スクリプトの中身はopensslコマンドを使って、認証局とマーチャントの証明書のファイルを作ってるだけで、実行が完了するとcertsディレクトリができ、そこに認証局とマーチャントの証明書が作成され、privateディレクトリに認証局とマーチャントの秘密鍵が格納される。

  • certs/cacert.pem
    ルート認証局の証明書
  • certs/demomerchant.pem
    マーチャントの証明書
  • private/demomerchantkey.pem
    マーチャントの証明書の秘密鍵

ちなみに生成されたマーチャントの証明書が

X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:TRUE

とCAの証明書になってたりする。同じopenssl.cnf使ってるからそのオプションのせいっぽい。なのでこれで証明書作ららなくても自前で認証局とマーチャントの証明書作る方がいいかも。

PHPのデモサイト

依存ライブラリの関係でPHP5系でしか動作しないみたいなので、PHP7系でなくPHP5系の環境が必要。今回はphpenv使ってPHP5.6.34を入れる。何気にPHPの環境用意するのが一番面倒だった。。

最初にphpenvをインストール↓

参考:phpenvで最新版のPHPをインストールしてWebサイトで使用する - Qiita

PHP 5.6.34のインストール時にpearも欲しかったので以下のように--with-pearを指定する。

$ CONFIGURE_OPTS="--with-pear" phpenv install 5.6.34

続いて、PHPでProtocol Bufferを扱うのに必要なProtobuf-PHPをインストールする。

$ pear channel-discover pear.pollinimini.net
$ pear install drslump/Protobuf-beta

続いてmemcacheモジュールをインストールするんだけど、pearpeclでは見つからなかったのでソースをダウンロードしてきてビルドした。

$ wget https://pecl.php.net/get/memcache-3.0.8.tgz
$ tar xvfz memcache-3.0.8.tgz
$ cd memcache-3.0.8
$ CFLAGS="-fgnu89-inline" ./configure
$ make
$ make install

./configureを実行する際にCFLAGS="-fgnu89-inline" しておかないと後でエラー吐く。

以上でセットアップは環境。

後は簡単で、paymentrequestをcloneしてdemo_websiteをrootに指定して実行し、

$ php -S localhost:8000 -t <clonしてきたパス>/paymentrequest/php/demo_website

http://localhost:8000/createpaymentrequest.php

にアクセスすると↓のような画面が表示される。

f:id:techmedia-think:20180312135529p:plain

証明書とその秘密鍵のロケーションを変更

PaymentRequestsを作成する際に、PKIの署名データが付与されるんだけど、この署名を生成するのに証明書を作成する際に使用した秘密鍵が必要になる。

証明書と鍵のロケーションは/home/gavin/.certs/以下のハードコードされてるので、

https://github.com/gavinandresen/paymentrequest/blob/master/php/demo_website/createpaymentrequest.php#L190

https://github.com/gavinandresen/paymentrequest/blob/master/php/demo_website/createpaymentrequest.php#L205

自分の環境に合わせて書き換える必要がある。

// 証明書
$leafCert = file_get_contents("使用するマーチャント証明書ファイルのパス");
...
// 鍵
$priv_key = file_get_contents("使用するマーチャント秘密鍵ファイルのパス");

書き換えたら、↑のURLにアクセスして、最低限アドレスと量(Amount)を入力して、「Create Payment Request」を押せばPayment Requestのメッセージがダウンロードできる。

データを確認すると、Payment Requestのsignatureには↑のマーチャントの秘密鍵を使って生成した署名データがセットされている。

Payment Requestのpki_dataには、仕様的には、まず必須なのが最初に↑のマーチャントの証明書がセットされ、それ以降は任意でその証明書からルート証明書までの証明書のチェーンが一式セットされるようになってる。PHPのデモコードではX.509証明書の拡張プロファイルのauthorityInfoAccessに記載されているURIから親の証明書を辿る仕組みになってるんだけど、↑で生成した証明書にはこのプロファイルが入ってないので、親が辿れず↑のメッセージにはマーチャントの証明書しか入ってない。

なので、PHPのコードを書き換えて↑の認証局の証明書まで含めるようにするか、↑のテスト用の証明書をメッセージを受け取ったクライアント側のOSに仕込んで証明書チェーンを検証するかする必要がある。まぁ実際にルート証明書が入っていないケースも考慮すべきなので、後者の方法を取る方がいいかな。

と、一応動く検証環境はできたけど、環境周りや設定変更とか必要なので、検証終わったらSinatraで簡単なAPIとか作りたいなー。