Android应用内购买:签名验证失败

use*_*092 66 android signing in-app-billing

我已经尝试了几天来解决这个问题,使用SDK附带的Dungeons演示代码.我试过谷歌寻求答案,但找不到答案.

  • 在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;
}
Run Code Online (Sandbox Code Playgroud)

小智 148

此问题仍在当前的Google结算版本中进行.基本上android.test.purchased被打破了; 购买android.test.purchased后,Security.java中verifyPurchase函数将始终失败,QueryInventoryFinishedListener将停在if(result.isFailure()) ; 这是因为android.test.purchased项始终未通过Security.java中的TextUtils.isEmpty(签名)检查,因为它不是真实项目,并且没有服务器返回的签名.

我的建议(缺乏任何其他解决方案)是永远不要使用"android.test.purchased".网上有各种代码调整,但它们都不能100%运行.

如果您使用了android.test.purchased,那么摆脱错误的一种方法是执行以下操作: -

  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);
       }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 运行你的应用程序,以便consunmeAsync发生,这摆脱了服务器上的"android.test.purchased"项目.

  4. 删除consumeAsync代码(或将其注释掉).
  5. 回到Security.java,将"return true"改回"return false".

您的QueryInventoryFinishedListener将不再在验证时出错,一切都恢复到"正常"(如果您可以调用它).记住 - 不要再费心使用android.test.purchased,因为它只会再次导致此错误...它已经破了!唯一真正的方法是测试您购买它以上传APK,等待它出现,然后在启用了日志记录的设备上测试它(相同的APK).

  • 谷歌令人窒息的无能为力从未让我感到惊讶. (52认同)
  • 是的,问题仍然存在.在我买了android.test.purchased之后,我开始在查询库存时遇到错误.我只想补充一点,只需清除Google Play商店应用程序的数据并运行一次即可修复手机.当您清除Google Play的数据时,它会忘记您购买了android.test.purchased. (16认同)
  • Srsly是谁写的这个API?太可怕了 (10认同)
  • 谢谢@GTMDev.这在2015年仍然被打破,你的回答帮助我恢复正常.此外,对于未来的读者,上面的SKU_ANDROID_TEST_PURCHASE_GOOD常量的值应为"android.test.purchased". (6认同)
  • 我有同样的问题,但找到了一个更简单的解决方案来消费[this site](https://www.gaffga.de/implementing-in-app-billing-for-android/)上的android.test.purchase,他静态创建购买以后再使用它:`pp = 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 \"}","");` (5认同)
  • 等等,在2016年,这仍然是一个问题.人们如何测试应用程序内购买? (2认同)

Rob*_*ert 47

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


小智 21

请检查一下,Play Developer Console中的base64EncodedPublicKey那个是相同的.在开发者控制台中重新上传APK后,公钥可能会发生变化,如果是这样,请更新您的.base64EncodedPublicKey

  • 我有同样的问题,确实与公钥不匹配.但是,每次重新上传APK时公钥都不会改变**(谢天谢地!). (4认同)

dou*_*uyw 9

您可以跳过那些"android.test.*"产品ID的验证过程.如果您正在使用TrivialDrive示例中的示例代码,请打开IabHelper.java,找到以下行代码,将其更改为

   if (Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... }
Run Code Online (Sandbox Code Playgroud)

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

即使您忘记回滚代码,它也是无害的.因此,您可以继续测试进一步的工作流程步骤.


cpr*_*ack 7

基于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);
}
Run Code Online (Sandbox Code Playgroud)

我只修改了一行(请参阅注释),这样你就可以保留代码来进行调试,并且仍然安全地发布你的发行版本.


Nab*_*ari 5

该错误是由于错误的许可证密钥引起的。许可证密钥可能来自另一个应用程序。

解决方案是使用来自以下位置的正确许可证密钥:

播放控制台>应用>开发工具>许可和应用内结算