web-dev-qa-db-ja.com

Androidアプリの購入:署名の確認に失敗しました

SDKに付属のDungeonsデモコードを使用して、この問題を解決するために数日間試しました。 Googleで回答を求めましたが、見つかりません。

  • Dungeonsデモでは、開発者コンソールから公開キーを渡しました。
  • Apkに署名し、公開せずにコンソールにアップロードしました。
  • Android.test.purchasedとサブスクリプション用に公開されたコンソールで作成された製品リストの両方のテスト(アプリに必要な主な機能)。

しかし、それでもSignature verification failedというエラーが発生し、署名がデータと一致しません。どうすれば解決できますか?

public static ArrayList<VerifiedPurchase> verifyPurchase(String signedData, String signature)
{
    if (signedData == null) {
        Log.e(TAG, "data is null");
        return null;
    }
    if (Consts.DEBUG) {
        Log.i(TAG, "signedData: " + signedData);
    }
    boolean verified = false;
    if (!TextUtils.isEmpty(signature)) {

        String base64EncodedPublicKey = "MIIBIjA....AQAB";
        PublicKey key = Security.generatePublicKey(base64EncodedPublicKey);
        verified = Security.verify(key, signedData, signature);
        if (!verified) {
            Log.w(TAG, "signature does not match data.");
            return null;
        }
    }
}

public static boolean verify(PublicKey publicKey, String signedData, String signature)
{
    if (Consts.DEBUG) {
        Log.i(TAG, "signature: " + signature);
    }
    Signature sig;
    try {
        sig = Signature.getInstance(SIGNATURE_ALGORITHM);
        sig.initVerify(publicKey);
        sig.update(signedData.getBytes());
        if (!sig.verify(Base64.decode(signature))) {
            Log.e(TAG, "Signature verification failed.");
            return false;
        }
        return true;
    } catch (NoSuchAlgorithmException e) {
        Log.e(TAG, "NoSuchAlgorithmException.");
    } catch (InvalidKeyException e) {
        Log.e(TAG, "Invalid key specification.");
    } catch (SignatureException e) {
        Log.e(TAG, "Signature exception.");
    } catch (Base64DecoderException e) {
        Log.e(TAG, "Base64 decoding failed.");
    }
    return false;
}
65
user1926092

この問題は、現在のGoogle請求バージョンでも引き続き発生しています。基本的にAndroid.test.purchasedは壊れています。 Android.test.purchasedを購入した後、verifyPurchaseSecurity.Javaの関数は常に失敗し、QueryInventoryFinishedListenerは行で停止しますif(result.isFailure());これは、Android.test.purchasedアイテムが常に失敗するためですTextUtils.isEmpty(signature)実際のアイテムではなく、署名が返されないため、Security.Javaをチェックインするサーバーによって。

私のアドバイスは(他のソリューションがないため)、「Android.test.purchased」を使用しないことです。ネットにはさまざまなコード調整がありますが、100%動作するものはありません。

Android.test.purchasedを使用している場合、エラーを取り除く1つの方法は次のことです:

  1. Security.Javaを編集し、verifyPurchaseの「return false」行を「return true」に変更します。これは一時的なものであり、すぐに元に戻します。
  2. QueryInventoryFinishedListenerで、「if(result.isFailure()){...}」行の後に以下を追加して、終了しないAndroid.test.purchasedアイテムを消費して削除します。

    if (inventory.hasPurchase(SKU_Android_TEST_PURCHASE_GOOD)) {  
       mHelper.consumeAsync(inventory.getPurchase(SKU_Android_TEST_PURCHASE_GOOD),null);
       }
    
  3. ConsunmeAsyncが発生するようにアプリを実行します。これにより、サーバー上の「Android.test.purchased」アイテムが削除されます。

  4. ConsumerAsyncコードを削除します(またはコメント化します)。
  5. Security.Javaに戻り、「return true」を「return false」に戻します。

QueryInventoryFinishedListenerは検証時にエラーにならず、すべてが「正常」に戻ります(呼び出すことができる場合)。覚えておいてください-Android.test.purchasedを再度使用しないでください。このエラーが再び発生するだけです...壊れました!購入をテストしてAPKをアップロードし、表示されるのを待ってから、ログを有効にしてデバイスで同じAPKをテストする唯一の実際の方法です。

147
Gavin Thornton

はい、問題は引き続き発生します。 Android.test.purchasedを購入した後、在庫の検索でエラーが発生し始めました。 Google Playストアアプリケーションのデータを消去して、Google Playを1回実行するだけで、お使いの携帯電話を修正できます。 Google Playのデータを消去すると、Android.test.purchasedを購入したことを忘れます

46
Robert

base64EncodedPublicKeyPlay Developer Consoleからのものが等しいことを確認してください。 Developer ConsoleでAPKを再アップロードすると、base64EncodedPublicKeyを更新する場合、公開キーが変更される場合があります。

20
user2032617

これらの「Android.test。*」製品IDの検証プロセスをスキップできます。 TrivialDriveの例のサンプルコードを使用している場合は、IabHelper.Javaを開き、次の行コードを見つけて、変更します。

   if (Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... }

   boolean verifySignature = !sku.startsWith("Android.test."); // or inplace the condition in the following line
   if (verifySignature && !Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... }

コードをロールバックするのを忘れたとしても、それは無害です。そのため、引き続きワークフローのステップをテストできます。

9
douyw

GMTDevの答えに基づいて、これは最も簡単な方法で製品を消費する際のテストの問題を修正するために私がすることです。 Security.Javaで、verifyPurchase()メソッドを次のように置き換えます。

public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
    if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
            TextUtils.isEmpty(signature)) {
        Log.e(TAG, "Purchase verification failed: missing data.");
        return BuildConfig.DEBUG; // Line modified by Cristian. Original line was: return false;
    }

    PublicKey key = Security.generatePublicKey(base64PublicKey);
    return Security.verify(key, signedData, signature);
}

1行だけ変更しました(コメントを参照)。このようにして、デバッグ用にそのようなコードを保持し、リリースバージョンを安全に公開できます。

7
cprcrack

エラーは、間違ったライセンスキーが原因で発生します。たぶん、ライセンスキーはおそらくあなたの別のアプリからのものでしょう。

解決策は、次の適切なライセンスキーを使用することです。

Playコンソール>アプリ>開発ツール>ライセンスとアプリ内課金

5
Nabin Bhandari

アプリ内課金v3と付属のユーティリティクラスを使用しているときに、私にとっては、返されたonActivityResult呼び出し内でテスト購入を消費していました。

今後のテスト購入のためにこれを回避するために、IabHelper、セキュリティ、またはアプリ内課金ユーティリティクラスのいずれも変更する必要はありません。

すでにテスト製品を購入しようとして、購入署名の検証に失敗したというエラーが発生した場合は、このエラーの回答を探しているためである可能性があります。

  1. gMTDevが推奨した変更を行います
  2. アプリを実行して、製品を消費することを確認します
  3. gMTDevの変更を削除/元に戻す
  4. onActivityResult内に以下のコードを実装します。

これにより、購入テストプロセスがスムーズになるだけでなく、"Item Already Owned"エラーを返すiabとの競合する問題を回避できるはずです。テスト製品を再購入しようとするとき。

これがフラグメント内から呼び出されており、フラグメントのonActivityResultが呼び出されていない場合は、必要に応じて親ActivityFragmentからYourFragmentName.onActivityResult(requestCode、resultCode、data)を呼び出してください。これについては、 FragmentからstartIntentSenderForResultを呼び出す(Android Billing v3)で詳しく説明しています。

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_PURCHASE) {

        //this ensures that the mHelper.flagEndAsync() gets called 
        //prior to starting a new async request.
        mHelper.handleActivityResult(requestCode, resultCode, data);

        //get needed data from Intent extra to recreate product object
        int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
        String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
        String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");

        // Strip out getActivity() if not being used within a fragment
        if (resultCode == getActivity().RESULT_OK) {
            try {
                JSONObject jo = new JSONObject(purchaseData);
                String sku = jo.getString("productId");

                //only auto consume the Android.test.purchased product
                if (sku.equals("Android.test.purchased")) {
                    //build the purchase object from the response data
                    Purchase purchase = new Purchase("inapp", purchaseData, dataSignature);
                    //consume Android.test.purchased
                    mHelper.consumeAsync(purchase,null);
                }
            } catch (JSONException je) {
                //failed to parse the purchase data
                je.printStackTrace();
            } catch (IllegalStateException ise) {
                //most likely either disposed, not setup, or 
                //another billing async process is already running
                ise.printStackTrace();
            } catch (Exception e) {
                //unexpected error
                e.printStackTrace();
            }
        }
    }
}

SKUが「Android.test.purchased」である場合にのみ購入を削除するため、安全に使用できます。

3
lodlock

これ ソリューションは私のために働いた。購入クラスの新しいverifyPurchaseメソッドを古いものに変更しました。

2
Khayam Gondal

今日(2018年10月30日)に、同じ問題(署名の検証、およびテスト購入の廃止)を実行しました。

署名の問題は、これらのテストskuが実際にはアプリの一部ではないため、アプリの署名がないという事実が原因である可能性があります。 Googleでチケットを開きましたが、これを修正できるかどうかはわかりません。回避策は、他の人が指摘したように、コードを置き換えることです

if (verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {

if (verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature()) ||
                        (purchase.getSku().startsWith("Android.test.")) ) { 

「Android.test.purchased SKUの購入を削除する方法」に関して、デバイスを簡単に再起動し、1分ほど待つか、アプリを数回再起動すると修正されることがわかりました。私にとっては(つまり、コードで購入を「消費」する必要はありませんでした)。 PlayストアがGoogleのサーバーとの同期を完了するために待機が必要であると推測しています。 (これが今後もこの方法で機能し続けるかどうかはわかりませんが、今はうまく機能していれば、これが前進に役立つかもしれません。)

1
Venu G.

署名検証は、デフォルトのテスト製品に対してのみ失敗します。簡単な修正:

  • IabHelperクラスに移動します。
  • Security.verifyPurchaseのif条件を反転します。

それでおしまい!

テスト製品が実際の製品に置き換えられた場合、変更を元に戻すことを忘れないでください

1
Shree Harsha S

これを確認してください answer

テストデバイスのプライマリアカウントは、Google Play開発者アカウントと同じですか?

そうでない場合、アプリが以前にPlayで公開されていない限り、Android.test。*静的応答で署名を取得できません。

条件の完全なセットについては、 http://developer.Android.com/guide/market/billing/billing_testing.html#static-responses-table の表をご覧ください。

そしてそれはコメントです:

静的IDはもはや署名を返さないと思います。 https://groups.google.com/d/topic/Android-developers/PCbCJdOl480/discussion を参照してください

また、以前はGoogle Play請求ライブラリのサンプルコード(多くの大きなアプリで使用されていました)で空の署名が許可されていました。それがそこで静的な購入が働いた理由です。
しかし、セキュリティホールだったため、 published の場合、Googleは pdate を提出しました。

0
Luten

私は同じ問題を抱えており、 https://www.gaffga.de/implementing-in-app-billing-for-Android/

重要な点は、在庫クエリの結果が失敗した場合でも、SKUを消耗品にする必要があることです。以下は、私がそれをどのようにしたかのサンプルです。

IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
        public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
            Log.d(TAG, "Query inventory finished.");

            // Have we been disposed of in the meantime? If so, quit.
            if (mHelper == null) return;

            // Is it a failure?
            if (result.isFailure()) {
                try {
                    Purchase purchase = new Purchase("inapp", "{\"packageName\":\"PACKAGE_NAME\","+
                            "\"orderId\":\"transactionId.Android.test.purchased\","+
                            "\"productId\":\"Android.test.purchased\",\"developerPayload\":\"\",\"purchaseTime\":0,"+
                            "\"purchaseState\":0,\"purchaseToken\":\"inapp:PACKAGE_NAME :Android.test.purchased\"}",
                            "");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                mHelper.consumeAsync(purchase, null);
                complain("Failed to query inventory: " + result);
                return;
            }

            Log.d(TAG, "Query inventory was successful.");

            /*
             * Check for items we own. Notice that for each purchase, we check
             * the developer payload to see if it's correct! See
             * verifyDeveloperPayload().
             */                   
        }
    };

上記のコードのPACKAGE_NAMEをアプリのパッケージ名に置き換えます。

0
Plugie

これは私のために働いたものです:

  1. BillingClient.querySkuDetailsAsyncを呼び出して、アイテムが利用可能かどうかを照会します
  2. SkuDetailsResponseListener.onSkuDetailsResponseを待つ
  3. さらに500ms待つ
  4. BillingClient.launchBillingFlowを使用して購入を開始...

OnSkuDetailsResponseを受け取ったときには問題ないはずですが、そうではないので、少し待つ必要があるため、手順3は不要です。その購入が機能した後、「アイテムが利用できないエラー」はもうありません。これは私がそれをテストした方法です:

  1. アプリのデータを消去する
  2. google Playデータを消去する
  3. アプリを実行する
  4. android.test.purchasedを購入する
  5. アイテムを購入しようとします(アイテムが利用できないため失敗します)
  6. 上記の私のソリューションを使用して、それは動作します
0
ghostbanana34