エンジニア

Androidアプリ内課金の不正購入チェックをPHPで

投稿日:2013年7月26日 更新日:

サーバー担当の山内です。

今回は、サーバー側のAndroidアプリ内課金についてです。
アプリ側の課金については、当ブログの下記エントリをご覧ください。

AndroidにおけるConsumableタイプのアプリ内課金

本エントリでは、安全性を高めるためサーバー側で不正購入のチェックをします。

大まかな流れはこうです。

  1. 「購入情報(json形式)」と秘密鍵で「暗号化された署名(Base64形式)」の2つを受け取る
  2. 「購入情報のSHA1ハッシュ値」と「公開鍵」で「復号化した署名」を照合させる

「公開鍵」を使用するために用意するものは、PEM形式の証明書です。まずDeveloper Console上で取得したDER(Distinguished Encoding Rules)形式を、PEM(Privacy Enhanced Mail)形式に変換したものを用います。PEMはBase64エンコードされたデータを含み、ヘッダーとフッターを付加したフォーマットですので、ヘッダーとフッターの間にBase64エンコードされた文字列が入っていればOKです。ヘッダーとフッターは次のものを利用します。

-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----

ちなみに、秘密鍵の場合は次のものを利用します。

-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----

RFC 1421の規約によれば、PEMのBase64エンコードの行最大長は64なので、PHPのビルトイン関数であるchunk_splitを用いて、文字列を64文字ごとに改行(分割)させる必要があります。chunk_splitの第2引数に64を、第3引数にPHP_EOL定数(OSに依存しない改行コード)を指定することで、PEM形式の証明書を得ることができます。この証明書をopenssl_get_publickeyに渡せば、PHPで公開鍵を生成することができます。

ここでは「購入情報」を$signed_data、「暗号化された署名」を$signatureとします。

$signed_data = '{"orderId":"12999763161054705751.2363231872472291",
"packageName":"jp.wonderplanet.zuma","productId":"jp.wonderplanet.zuma",
"purchaseTime":1366356881000,"purchaseState":0,
"purchaseToken":"xpvtbbfdjbnpbrhaunfvzqrf.AO-J1OxcEyM05mHhDPKewcCDyXfGx
9af8NR4VwVVSPJH0T0gTvswikPkGO6ASziXkWkslmZtGRXvKvZa_AHdTEXpDpX7naJdPTWU
3f07l1Y5uNJ6hluDwUJbOgRILqQvMGaUa0LeLC-_aORWGUuOeU5NQWDrqiweQl"}';

$signature = 'KufRryqT0TFlxMv\/4YQh3548wv3weaqM6YdK\/c67yR5PPqyTrfM
0Tk9DuIdilSTb6oX54c6U5xx+TciaikTq45CCvk\/PTIbR\/KwmsoK8r7tvi9PTEavA
9I\/iKtAdi\/vxOKmyt1TZBVRdKid\/PF9MkCVhffllcTwh6HD3ikTXaZLeXfmYBtAU
AO8zXXQ2BlcLwukKcLkzuQHNYef31FDamoqVyDTCbOqq3KUDAa\/+OKQT06qA5zwTlb
Io2R3Kp0oYXVOSbYwKK929cKIe0o\/1mzApZCaFSlYlDtRTOoTpKDTmxUQdKiKhkVYs
FSLvKCR3wQUiYDF4Xb8FktDTi9QvwA==';

// RSA(PEM形式)公開鍵の生成
$cert = "-----BEGIN PUBLIC KEY-----" . PHP_EOL .
        chunk_split($rsa_key, 64, PHP_EOL) .
        "-----END PUBLIC KEY-----";

// openssl_get_publickey()はopenssl_pkey_get_public()のエイリアス
$pubkey = openssl_get_publickey($cert);

// 署名をBase64デコードする(バイナリになる)
$signature = base64_decode($signature);

// openssl_verify()のデフォルトアルゴリズムはSHA1なので、
// OPENSSL_ALGO_SHA1は渡さなくてもよい
$result = openssl_verify($signed_data, $signature, $pubkey, OPENSSL_ALGO_SHA1);

// キーをメモリから解放
openssl_free_key($pubkeyid);

openssl_verify()は、成功すると1を返します。
このケースでは変数$resultの値が1以外であれば不正購入の疑いがあるということになります。

採用情報

ワンダープラネットでは、一緒に働く仲間を幅広い職種で募集しております。

-エンジニア
-,

© WonderPlanet Inc.