Android在应用程序内购买:签名验证失败(Android in app purchase: Si

2019-07-19 00:08发布

我已经尝试了好几天来解决这个问题,使用SDK附带的地牢演示代码。 我试图谷歌的答案,但无法找到一个。

  • 在地牢演示中,我通过从开发者控制台我的公钥。
  • 签名的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;
}

Answer 1:

这个问题仍然是当前谷歌的计费版本回事。 基本上android.test.purchased坏; 你买android.test.purchased后在Security.javaverifyPurchase函数总是失败,QueryInventoryFinishedListener将在该行停止,如果(result.isFailure()); 这是因为android.test.purchased项目总是失败在Security.java的TextUtils.isEmpty(签名)检查,因为它不是一个真正的项目,并没有通过服务器返回的签名。

我的建议(由于缺乏任何其他解决方案)是永远不会使用“android.test.purchased”。 有在网络上的各种代码的调整,但他们没有工作100%。

如果你已经使用了android.test.purchased再一个办法摆脱错误的是要做到以下几点: -

  1. 编辑Security.java并修改“返回false”线在verifyPurchase为“返回true” - 这是暂时的,我们会装回去一分钟。
  2. 在你QueryInventoryFinishedListener,经过“如果(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. 卸下consumeAsync代码(或将其注释掉)。
  5. 早在Security.java,改变“返回true”回“返回false”。

您QueryInventoryFinishedListener将上不再错误的验证,一切都恢复到“正常”(如果你可以称呼它)。 请记住 - 不要打扰再次使用android.test.purchased,因为它只会再次引起这个错误......这是爆发了! 测试的唯一途径,你购买它来上传APK,等待它出现,然后你的设备上测试它(相同的APK)与启用日志记录。



Answer 2:

是的,问题仍然存在。 之后我买android.test.purchased我开始越来越对quering清单中的错误。 有可能通过清除只是谷歌的数据Play商店应用程序和运行谷歌播放一个时间来修复您的手机。 当你清楚谷歌的Play的数据,它忘记了,你买android.test.purchased



Answer 3:

请检查base64EncodedPublicKey ,并从Play开发者控制台是相等的一个。 一旦你在开发者控制台重新上传APK,公钥可能会改变,如果是更新base64EncodedPublicKey



Answer 4:

你可以跳过那些“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)) { ... }

这是无害的,即使你忘了回滚代码。 所以,你可以继续测试其它工作流程的步骤。



Answer 5:

基于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);
}

我只修改了一条线(见注释),这样一来你可以保持这样的代码,对调试和仍然发布发行版本安全。



Answer 6:

该错误是由于错误的许可证造成的。 也许许可证密钥可能是从其他应用程序。

解决的办法是使用正确的许可证密钥来自:

游戏控制台>应用程序>开发工具>许可和应用内结算



Answer 7:

该解决方案为我工作。 我与旧购类换了新verifyPurchase方法。



Answer 8:

什么工作对我来说,在使用应用内结算v3和附带的实用工具类,是消费返回的onActivityResult调用内测试购买。

都需要IabHelper,安全,或任何应用内结算UTIL类的没有变化,以避免这为未来的测试购买。

如果你已经尝试过购买测试产品,现在被困在购买签名验证失败的错误,你有可能是因为你是这个错误查找答案,那么你应该:

  1. 作出这样的GMTDev推荐的变化
  2. 运行应用程序,以确保它消耗的产品
  3. 删除/撤消GMTDev的变化
  4. 实施以下内onActivityResult的代码。

这不仅让购买测试过程是流体,但是这也应该避免任何冲突的问题与IAB试图回购测试产品时,返回项目已经拥有的错误。

如果这是从片段中调用和你的片段的onActivityResult不会被调用,那么一定在必要时从父ActivityFragment调用YourFragmentName.onActivityResult(requestCode,resultCode为,数据)。 此进行更详细的说明中调用从片段startIntentSenderForResult(Android电子帐单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”,所以它应该是安全的使用它只会删除购买。



Answer 9:

签名验证只为默认的测试产品的失败。 快速修复:

  • 转到IabHelper类。
  • 反转的条件,如果Security.verifyPurchase

而已!

记住恢复的变更时,测试产品由实际产品所取代



Answer 10:

遇到同样的问题(签名验证,并摆脱了测试购买的)今日(2018年10月30日)。

签名的问题可能是由事实是,这些测试的SKU是不是真的你的应用程序的一部分,因此没有你的应用程序的签名造成的。 我没有打开与谷歌一票,但不知道他们是否可以解决这个问题。 解决方法,因为别人指出的那样,是替代代码

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

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

关于“如何摆脱购买android.test.purchased SKU的”,我发现该设备的简单的重新启动,然后等待一分钟左右,和/或重新启动您的应用程序几次固定它对我来说(即我没有到“消费”通过代码购买者除外)。 我猜测,需要等待,以便在Play商店完成了与谷歌的服务器同步。 (不知道这是否会继续这种工作方式在未来,但如果它现在为你工作,这可能会帮助你前进。)



Answer 11:

检查这个答案 :

是主帐户在测试设备上为您的谷歌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

此外,以前的示例代码从谷歌(许多大型应用中使用)播放结算图书馆允许一个空的签名。 这就是为什么它是静态的采购在那里工作。
但它是一个安全漏洞,所以当它被发布 ,谷歌提交了更新 。



Answer 12:

我有同样的问题,并按照@Deadolus说,根据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。



Answer 13:

这是对我工作:

  1. 呼叫BillingClient.querySkuDetailsAsync来查询项(如果可用)
  2. 等待SkuDetailsResponseListener.onSkuDetailsResponse
  3. 等待另一500ms的
  4. 使用BillingClient.launchBillingFlow开始购买...

因为当我收到onSkuDetailsResponse它应该是确定的,但它没有,不得不等待一点点的第3步不应该是必要的。 该购买作品后,没有更多的“项目不可用错误”。 这是我测试过它:

  1. 清除我的应用程序数据
  2. 明确谷歌Play资料
  3. 运行应用程序
  4. 购买android.test.purchased
  5. 尽量购买我的产品(它失败的项目不适用)
  6. 用我的解决方案上面,它的工作原理


Answer 14:

对于科尔多瓦和混合的应用程序,你需要使用this.iap.subscribe(this.productId)方法来订阅InAppPurchase。

以下是代码的工作对我罚款:

 getProdutIAP() {
        this.navCtrl.push('subscribeDialogPage');
        this.iap
            .getProducts(['productID1']).then((products: any) => {
                this.buy(products);
            })
            .catch((err) => {
                console.log(JSON.stringify(err));
                alert('Finished Purchase' + JSON.stringify(err));
                console.log(err);
            });
    }

    buy(products: any) {
        // this.getProdutIAP();
        // alert(products[0].productId);
        this.iap.subscribe(products[0].productId).then((buydata: any) => {
            alert('buy Purchase' + JSON.stringify(buydata));
            // this.sub();
        }).catch((err) => {
            // this.navCtrl.push('subscribeDialogPage');
            alert('buyError' + JSON.stringify(err));
        });
    }

    sub() {
        this.platform.ready().then(() => {
            this.iap
                .subscribe(this.productId)
                .then((data) => {
                    console.log('subscribe Purchase' + JSON.stringify(data));
                    alert('subscribe Purchase' + JSON.stringify(data));
                    this.getReceipt();
                }).catch((err) => {
                    this.getReceipt();
                    alert('subscribeError' + JSON.stringify(err));
                    console.log(err);
                });
        })
    }


文章来源: Android in app purchase: Signature verification failed