Android应用购买:签名validation失败

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

  • 在Dungeons演示中,我从开发控制台传递了我的公钥。
  • 签署apk并上传到控制台而不发布。
  • testingandroid.test.purchased &产品列表在控制台上发布的订阅(我想为我的应用程序的主要function)。

但是我仍然得到了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; } 

目前的Google结算版本仍然存在这个问题。 基本上android.test.purchased是坏的; 购买android.test.purchased后, Security.java中verifyPurchase函数将始终失败,并且QueryInventoryFinishedListener将停在行if(result.isFailure()) ; 这是因为android.test.purchased项目总是在Security.java中的TextUtils.isEmpty(签名)检查失败,因为它不是一个真实的项目,也没有服务器返回的签名。

我的build议(从缺乏任何其他解决scheme)是永远不要使用“android.test.purchased”。 网上有各种代码的调整,但没有一个100%的工作。

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

  1. 编辑Security.java并将verifyPurchase中的“返回false”行更改为“返回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. 去掉consumeAsync代码(或注释掉)。
  5. 回到Security.java,把“return true”改回“return false”。

你的QueryInventoryFinishedListener将不再在validation错误,一切都回到“正常”(如果你可以称之为)。 请记住 – 不要打扰再次使用android.test.purchased,因为它只会再次导致这个错误…它坏了! testing您购买上传APK唯一真实的方法,等待它出现,然后在启用日志logging的情况下在您的设备上testing它(相同的APK)。

是的,问题仍然存在。 在我买了android.test.purchased之后,我开始在查询库存时发生错误。 只需清除Google Play商店应用程序的数据并运行一次Google Play即可修复您的手机。 当您清除Google Play的数据时,会忘记您购买了android.test.purchased

请检查base64EncodedPublicKeyPlay开发者控制台的版本是否相同。 一旦您在开发者控制台中重新上传APK,公钥可能会更改,如果更新您的base64EncodedPublicKey

根据GMTDev的回答,这是我为了解决以最简单的方式使用产品时的testing问题所做的。 在Security.java中,将verifyPurchase()方法replace为:

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

我只修改了一行(见注释),这样你可以保持代码的debugging,并仍然安全地发布你的发布版本。

您可以跳过这些“android.test。*”产品ID的validation过程。 如果您使用的是TrivialDrive示例中的示例代码,请打开IabHelper.java,find以下代码行,将其更改为

  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)) { ... } 

这是无害的,即使你忘了回滚代码。 因此,您可以继续testing进一步的工作stream程步骤。

这个解决scheme为我工作。 我在旧购物类中更改了新的verifyPurchase方法。

在使用In-app Billing v3和附带的实用程序类的同时,在我返回的onActivityResult调用中消费了testing购买。

不需要对IabHelper,安全性或任何应用内结算util类别进行更改,以避免将来的testing购买。

如果您已经尝试购买testing产品,并且现在停留在购买签名validation失败错误(您可能正在查找此错误的答案),那么您应该:

  1. 进行GMTDev推荐的更改
  2. 运行应用程序以确保它消耗产品
  3. 删除/撤销GMTDev的更改
  4. 在onActivityResult中实现下面的代码。

这样做不仅可以使购买testingstream程变得stream畅,而且还可以避免在尝试重新购买testing产品时iab返回已有产品错误的任何冲突问题。

如果这是从一个片段中调用,并且片段的onActivityResult没有被调用,那么一定要从父ActivityFragment调用YourFragmentName.onActivityResult(requestCode,resultCode,data)(如有必要)。 这在从Fragment(Android Billing v3)调用startIntentSenderForResult中有更详细的解释

 @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”,所以它应该是安全的使用。

签名validation仅对默认testing产品失败。 快速修复:

  • 转到IabHelper类。
  • 反转Security.verifyPurchase条件。

而已!

请记住,当testing产品被实际产品replace时,要恢复更改

检查这个答案 :

您的testing设备上的主帐户是否与您的Google Play开发者帐户相同?

如果没有,你将不会得到android.test。*静态响应的签名,除非应用程序已经发布在Play上。

请参阅http://developer.android.com/guide/market/billing/billing_testing.html#static-responses-table中的表格以了解整套条件。;

这是评论:

我不认为静态ids返回签名了。 请参阅https://groups.google.com/d/topic/android-developers/PCbCJdOl480/discussion

此外,Google Play Billing Library之前的示例代码(由许多大型应用程序使用)允许使用空签名。 这就是为什么它在那里工作的静态购买。
但这是一个安全漏洞,所以在发布时 ,Google提交了更新 。

我有同样的问题,并遵循@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_NAMEreplace为您的应用的软件包名称。

错误是由于许可证密钥错误导致的。 也许许可证密钥可能来自您的另一个应用程序。

解决方法是使用以下适当的许可证密钥:

Play console> App>开发工具>授权和应用内结算

Interesting Posts