wake-up-neo.com

Android im App-Kauf: Signaturprüfung fehlgeschlagen

Ich habe mehrere Tage versucht, dieses Problem zu lösen, indem ich den Dungeons-Democode verwendet, der mit dem SDK geliefert wird. Ich habe eine Antwort bei Google gesucht, kann aber keine finden.

  • In der Dungeons-Demo habe ich meinen öffentlichen Schlüssel von der Dev-Konsole übergeben.
  • Die apk wurde signiert und ohne Veröffentlichung auf die Konsole hochgeladen.
  • Testen für Android.test.purchased und Produktliste, die auf der Konsole erstellt wurde und für Abonnement veröffentlicht wurde (die Hauptfunktion, die ich für meine App verwenden möchte).

Trotzdem bekomme ich einen Fehler von Signature verification failed und dann stimmt die Signatur nicht mit den Daten überein. Wie kann ich das lösen?

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;
}
63
user1926092

Dieses Problem tritt in der aktuellen Google-Abrechnungsversion immer noch auf. Grundsätzlich ist das Android.test.purchased kaputt; Nach dem Kauf von Android.test.purchased wird die verifyPurchase - Funktion in Security.Java immer fehlschlagen und der QueryInventoryFinishedListener wird an der Zeile if (result.isFailure ()) ; Dies liegt daran, dass das Element Android.test.purchased immer TextUtils.isEmpty (Signatur) nicht in Security.Java überprüft, da es kein echtes Element ist und keine vom Server zurückgegebene Signatur vorliegt. 

Mein Ratschlag (aus Mangel an anderen Lösungen) ist, NIEMALS "Android.test.purchased" zu verwenden. Es gibt verschiedene Code-Tweaks im Netz, aber keiner von ihnen funktioniert zu 100%.

Wenn Sie Android.test.purchased verwendet haben, können Sie den Fehler folgendermaßen beheben:

  1. Bearbeiten Sie Security.Java, und ändern Sie die Zeile "return false" in "verifyPurchase" in "return true". Dies ist temporär. Wir werden es in einer Minute zurücksetzen. 
  2. Fügen Sie in Ihrem QueryInventoryFinishedListener nach den Zeilen "if (result.isFailure ()) {...}" Folgendes hinzu, um zu verbrauchen und Ihr nie endendes Android.test.purchased-Element zu entfernen:

    if (inventory.hasPurchase(SKU_Android_TEST_PURCHASE_GOOD)) {  
       mHelper.consumeAsync(inventory.getPurchase(SKU_Android_TEST_PURCHASE_GOOD),null);
       }
    
  3. Führen Sie Ihre App aus, damit consunmeAsync ausgeführt wird. Dadurch wird das Element "Android.test.purchased" auf dem Server entfernt.

  4. Entfernen Sie den consumeAsync-Code (oder kommentieren Sie ihn aus).
  5. Zurück in der Security.Java, ändern Sie "return true" zurück in "return false".

Ihr QueryInventoryFinishedListener wird bei der Überprüfung nicht mehr fehlerfrei sein, alles ist wieder "normal" (wenn Sie es so nennen können). Denken Sie daran, dass Sie Android.test.purchased nicht noch einmal verwenden sollten, da es diesen Fehler erneut verursacht ... es ist pleite! Der einzige Weg, um zu testen, ob Sie eine APK erwerben, warten Sie, bis sie erscheint, und testen Sie sie (dieselbe APK) auf Ihrem Gerät mit aktivierter Protokollierung. 

145
Gavin Thornton

Ja, das Problem tritt immer noch auf. Nachdem ich Android.test.purchased gekauft habe, bekomme ich den Fehler beim Abfragen des Inventars. Es ist möglich, das Telefon zu reparieren, indem Sie einfach die Daten der Google Play Store-Anwendung löschen und einmal Google Play ausführen. Wenn Sie Daten von Google Play löschen, wird vergessen, dass Sie Android.test.purchased gekauft haben

46
Robert

Bitte überprüfen Sie, ob base64EncodedPublicKey und der aus der Play Developer Console gleich sind . Wenn Sie das APK erneut in die Developer Console hochladen, kann sich der öffentliche Schlüssel ändern, wenn Sie Ihren base64EncodedPublicKey aktualisieren.

18
user2032617

Sie können den Überprüfungsprozess für diese Produkt-IDs "Android.test. *" Überspringen. Wenn Sie den Beispielcode aus dem TrivialDrive-Beispiel verwenden, öffnen Sie IabHelper.Java, suchen Sie den folgenden Zeilencode, und ändern Sie ihn ab

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

in

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

Es ist harmlos, auch wenn Sie vergessen haben, den Code zurückzusetzen. Sie können also den weiteren Workflow-Schritt weiter testen.

9
douyw

Basierend auf der Antwort von GMTDev mache ich dies, um die Testprobleme beim Konsumieren von Produkten auf die einfachste Weise zu beheben. Ersetzen Sie in Security.Java die Methode verifyPurchase () durch Folgendes:

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

Ich habe nur eine Zeile geändert (siehe Kommentar). Auf diese Weise können Sie den Code so beibehalten, dass er Debugging ausführt, und dennoch Ihre Release-Versionen sicher veröffentlichen.

7
cprcrack

Der Fehler wurde aufgrund eines falschen Lizenzschlüssels verursacht. Möglicherweise stammt der Lizenzschlüssel möglicherweise von einer anderen App.

Die Lösung ist die Verwendung des richtigen Lizenzschlüssels von:

Play Console> App> Entwicklungstools> Lizenzierung und In-App-Abrechnung

3
Nabin Bhandari

Diese Lösung funktionierte für mich. Ich habe die neue verifyPurchase-Methode in der Kaufklasse durch die alte ersetzt.

2
Khayam Gondal

Was bei der Verwendung von In-App Billing v3 und den enthaltenen Dienstprogrammklassen für mich funktionierte, kostete den Testkauf innerhalb des zurückgegebenen onActivityResult-Aufrufs. 

Es sind keine Änderungen an IabHelper, Security oder einer der In-App-Abrechnungsgruppen erforderlich, um dies bei zukünftigen Testkäufen zu vermeiden. 

Wenn Sie bereits versucht haben, das Testprodukt zu kaufen, und der Fehler beim Bestätigungsfehler der Kaufsignatur jetzt hängen bleibt. Dies ist wahrscheinlich der Fall, da Sie nach Antworten für diesen Fehler suchen. Dann sollten Sie: 

  1. nehmen Sie die von GMTDev empfohlenen Änderungen vor
  2. führen Sie die App aus, um sicherzustellen, dass das Produkt verbraucht wird
  3. gMTDevs Änderungen löschen/rückgängig machen 
  4. implementieren Sie den folgenden Code in onActivityResult.

Dies ermöglicht nicht nur einen fließenden Kaufprozess, sondern sollte auch Konflikte mit iab vermeiden, die den Fehler "Item Already Owned" zurückgeben, wenn versucht wird, das Testprodukt erneut zu erwerben.

Wenn dies aus einem Fragment heraus aufgerufen wird und onActivityResult Ihres Fragments nicht aufgerufen wird, rufen Sie YourFragmentName.onActivityResult (requestCode, resultCode, data) ggf. über Ihr übergeordnetes ActivityFragment auf. Dies wird ausführlicher in Aufruf von interintSenderForResult aus Fragment (Android Billing v3) beschrieben.

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

Es wird den Kauf nur dann entfernen, wenn es sich um "Android.test.purchased" handelt. Die Verwendung sollte daher sicher sein. 

2
lodlock

Ich habe heute (30. Oktober 2018) dasselbe Problem (Signaturüberprüfung und Abschaffung des Testkaufs).

Das Problem der Signatur wird wahrscheinlich durch die Tatsache verursacht, dass diese Test-Skus nicht wirklich Teil Ihrer App sind und daher keine Signatur Ihrer App haben. Ich habe ein Ticket bei Google eröffnet, aber ich bin mir nicht sicher, ob sie das Problem beheben können. Wie bereits erwähnt, besteht die Problemumgehung darin, den Code zu ersetzen

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

mit 

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

In Bezug auf "Wie kann ich den Kauf von Android.test.purchased SKU" loswerden? Ich habe festgestellt, dass ein einfacher Neustart des Geräts, gefolgt von etwa einer Minute Wartezeit und/oder einem Neustart Ihrer App ein paar Mal behoben wurde für mich (dh ich musste den Kauf nicht per Code "verbrauchen"). Ich vermute, dass das Warten erforderlich ist, damit der Play Store die Synchronisierung mit den Servern von Google abschließt. (Nicht sicher, ob dies auch in Zukunft so funktionieren wird, aber wenn es jetzt für Sie funktioniert, könnte dies Ihnen helfen, voranzukommen.)

1
Venu G.

Die Signaturüberprüfung schlägt nur für das Standardtestprodukt .. __ fehl.

  • Gehe zur IabHelper-Klasse.
  • Invertieren Sie die if-Bedingungen von Security.verifyPurchase

Das ist es!

Denken Sie daran, die Änderungen rückgängig zu machen, wenn das Testprodukt durch das tatsächliche Produkt ersetzt wird

1
Shree Harsha S

Überprüfen Sie diese Antwort :

Ist das Hauptkonto auf Ihrem Testgerät das gleiche wie Ihr Google Entwicklerkonto abspielen

Andernfalls erhalten Sie keine Signaturen in Android.test. * Statische Antworten es sei denn, die App wurde zuvor auf Play veröffentlicht.

Siehe die Tabelle unter http://developer.Android.com/guide/market/billing/billing_testing.html#static-responses-table für alle Bedingungen.

Und es ist ein Kommentar:

Ich glaube nicht, dass die statischen IDs die Signatur zurückgeben. Sehen https://groups.google.com/d/topic/Android-developers/PCbCJdOl480/discussion

Bisher erlaubte der von vielen großen Apps verwendete Beispielcode aus der Google Play Billing Library eine leere Signatur. Deshalb haben dort statische Einkäufe stattgefunden.
Es war jedoch eine Sicherheitslücke. Wenn also veröffentlicht wurde, übermittelte Google ein update .

0
Luten

Ich habe das gleiche Problem und folge @Deadolus, basierend auf https://www.gaffga.de/implementing-in-app-billing-for-Android/

Der entscheidende Punkt ist, dass wir die SKU als Verbrauchsartikel verwenden müssen, selbst wenn das Ergebnis der Inventarabfrage nicht erfolgreich ist. Unten ist das Beispiel, wie ich das gemacht habe.

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().
             */                   
        }
    };

Ersetzen Sie PACKAGE_NAME im obigen Code durch den Paketnamen Ihrer App.

0
Plugie

Das hat bei mir funktioniert: 

  1. Rufen Sie BillingClient.querySkuDetailsAsync auf, um abzufragen, ob das Element verfügbar ist
  2. Warten Sie auf SkuDetailsResponseListener.onSkuDetailsResponse
  3. Warten Sie noch 500ms
  4. Starten Sie den Einkauf mit BillingClient.launchBillingFlow ... 

Der Schritt 3 sollte nicht notwendig sein, denn als ich onSkuDetailsResponse erhielt, sollte es OK sein, aber es ist nicht so, musste etwas warten. Nach diesem Kauf funktioniert kein Fehler mehr "Artikel nicht verfügbar". So habe ich es getestet:

  1. meine App-Daten löschen
  2. google Play-Daten löschen
  3. app ausführen
  4. kauf Android.test.purchased
  5. versuche meine Artikel zu kaufen (es schlägt fehl, wenn der Artikel nicht verfügbar ist)
  6. benutze meine Lösung oben, es funktioniert
0
ghostbanana34