Wie überprüfe ich, ob APK signiert ist oder ob "Debug Build"?

121

Soweit ich weiß, ist in Android "Release Build" APK signiert. Wie kann man es anhand des Codes überprüfen oder hat Eclipse eine Art geheime Definition?

Ich benötige dies, um das Auffüllen von ListView-Elementen aus Webdienstdaten zu debuggen (nein, logcat ist keine Option).

Meine Gedanken:

  • Anwendungen android:debuggable, aber aus irgendeinem Grund sieht das nicht zuverlässig aus.
  • Die Hardcodierung der Geräte-ID ist keine gute Idee, da ich dasselbe Gerät zum Testen signierter APKs verwende.
  • Verwenden Sie die manuelle Flagge irgendwo im Code? Plausibel, aber ich werde definitiv vergessen, mich irgendwann zu ändern, und alle Programmierer sind faul.
Unverschämtheit
quelle
Roll-Backed Phils Bearbeitung. Dies ist keine Frage, ob das Programm legal über den Markt verteilt wird oder nicht. Die Frage ist, ob sich das Programm noch im "Debug-Modus" befindet.
Im0rtality
Dieser Weg ist der einfachste Weg, dies zu tun: stackoverflow.com/a/23844716/2296787
MBH

Antworten:

80

Es gibt verschiedene Möglichkeiten, um zu überprüfen, ob die Anwendung mithilfe eines Debug- oder Release-Zertifikats erstellt wurde. Die folgende Methode erscheint mir jedoch am besten.

Gemäß den Informationen in der Android-Dokumentation Signing Your Application enthält der Debug-Schlüssel den folgenden Betreffnamen: " CN = Android Debug, O = Android, C = US ". Wir können diese Informationen verwenden, um zu testen, ob das Paket mit dem Debug-Schlüssel signiert ist, ohne die Debug-Schlüsselsignatur in unserem Code fest zu codieren.

Gegeben:

import android.content.pm.Signature;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

Sie können eine isDebuggable-Methode folgendermaßen implementieren:

private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
private boolean isDebuggable(Context ctx)
{
    boolean debuggable = false;

    try
    {
        PackageInfo pinfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(),PackageManager.GET_SIGNATURES);
        Signature signatures[] = pinfo.signatures;

        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        for ( int i = 0; i < signatures.length;i++)
        {   
            ByteArrayInputStream stream = new ByteArrayInputStream(signatures[i].toByteArray());
            X509Certificate cert = (X509Certificate) cf.generateCertificate(stream);       
            debuggable = cert.getSubjectX500Principal().equals(DEBUG_DN);
            if (debuggable)
                break;
        }
    }
    catch (NameNotFoundException e)
    {
        //debuggable variable will remain false
    }
    catch (CertificateException e)
    {
        //debuggable variable will remain false
    }
    return debuggable;
}
Omar Rehman
quelle
6
Um Hilfe mehrerer Import passende Lösung, verwendet die Klassen hier sind java.security.cert.X509Certificate, java.security.cert.CertificateExceptionund android.content.pm.Signature. Alle anderen Klassen präsentieren nicht mehrere Spiele für mich
Christian García
1
Antwort mit diesen Importen bearbeitet. Vielen Dank!
Cory Petosky
Ist es effizient genug, um mit der onCreate-Methode der Anwendungsklasse ausgeführt zu werden?
Android-Entwickler
Ich habe die Ausführungszeit nicht notiert, aber ich habe sie in meiner App verwendet und habe keine Probleme mit der Effizienz.
Omar Rehman
Das Ergebnis kann aus Effizienzgründen zwischengespeichert werden.
ftvs
138

Um das debuggable Flag zu überprüfen, können Sie diesen Code verwenden:

boolean isDebuggable =  ( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );

Kotlin:

val isDebuggable = 0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE

Weitere Informationen finden Sie unter Sichern von Android LVL-Anwendungen .

Wenn Sie Gradle richtig verwenden, können Sie alternativ überprüfen, ob BuildConfig.DEBUGes wahr oder falsch ist.

Blundell
quelle
es scheint, als ob dies immer noch das Android des Manifests überprüft: debuggable
xster
2
Der erste Test für Manifest debuggable, dies ist veraltet. Die zweite ist für Bibliotheken nicht möglich. Die Bibliothek verfügt über eine eigene BuildConfig. Die BuildConfig der App, die die Bibliothek verwendet, kann nicht importiert werden. Daher ist die markierte Antwort "ok"
Christoph
Diese Antwort funktioniert in allen Fällen unabhängig vom Bibliotheksprojekt oder Anwendungsprojekt.
Lavekush Agrawal
131

Beantwortet von Mark Murphy

Die einfachste und beste langfristige Lösung ist die Verwendung BuildConfig.DEBUG. Dies ist ein booleanWert, der truefür einen Debug-Build verwendet wird, falseandernfalls:

if (BuildConfig.DEBUG) {
  // do something for a debug build
}
Zar E Ahmer
quelle
8
Der einzige Nachteil dieses Ansatzes ist, dass er in Bibliotheksprojekten (aar's) nicht funktioniert. Wenn Bibliotheken erstellt werden, führt dies zu false. Selbst wenn sich eine Anwendung, die die Bibliothek verwendet, im Debug-Modus befindet, führt diese Überprüfung im Bibliothekscode zu false.
Vito Andolini
24

Wenn Sie eine APKstatische überprüfen möchten , können Sie verwenden

aapt dump badging /path/to/apk | grep -c application-debuggable

Dies wird ausgegeben, 0wenn das APKnicht debuggbar ist und 1wenn es ist.

Heidegrenzen
quelle
3
Dies ist die einzige Lösung, um über die endgültige apk zu überprüfen. Die anderen Antworten setzen voraus, dass Sie die Quelle haben.
Guillermo Tobar
1
aaptlebt hier/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt
Casey
21

Vielleicht spät, aber iosched verwendet BuildConfig.DEBUG

urSus
quelle
ist es jetzt sicher zu benutzen? Es gibt einen Artikel, der besagt, dass es einige Probleme gibt: digipom.com/be-careful-with-buildconfig-debug
Android-Entwickler
Das ist die beste Antwort!
Peter Fortuin
Nicht, wenn Sie eine Bibliothek eines Drittanbieters schreiben und das BuildConfig-Paket zur Kompilierungszeit nicht kennen.
Sam Dozor
Sam, kannst du das näher erläutern?
Agamemnus
10

Fügen Sie dies zuerst zu Ihrer build.gradle-Datei hinzu. Dadurch können auch Debug- und Release-Builds nebeneinander ausgeführt werden:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Fügen Sie diese Methode hinzu:

public static boolean isDebug(Context context) {
    String pName = context.getPackageName();
    if (pName != null && pName.endsWith(".debug")) {
        return true;
    } else {
        return false;
    }
}
Gemeiner Mann
quelle
1
Ich bevorzuge diese Antwort, weil sie zuverlässig ist. Ich musste meinem Google Maps-API-Schlüssel jedoch einen neuen Eintrag "Zulässige Android-Anwendung" hinzufügen (da die Anwendungs-ID unterschiedlich ist).
Baz
5

Ein Debug-Build wird ebenfalls signiert, nur mit einem anderen Schlüssel. Es wird automatisch von Eclipse generiert und das Zertifikat ist nur ein Jahr gültig. Was ist das Problem mit android:debuggable? Sie können diesen Wert aus dem Code mit abrufen PackageManager.

Nikolay Elenkov
quelle
3

Eine weitere erwähnenswerte Option. Wenn Sie Code nur ausführen müssen, wenn der Debugger angehängt ist, verwenden Sie diesen Code:

if (Debug.isDebuggerConnected() || Debug.waitingForDebugger()) { 
    //code to be executed 
}
Igor Gorjanc
quelle
0

Gelöst mit android:debuggable. Es war ein Fehler beim Lesen eines Elements, bei dem in einigen Fällen das Debug-Flag für ein Element nicht im Datensatz gespeichert wurde und if (m.debug && !App.isDebuggable(getContext()))immer ausgewertet wurde false. Mein Fehler.

Unverschämtheit
quelle
13
Mir ist klar, dass dies über ein Jahr alt ist, aber Sie hätten die Antwort von @Omar Rehman akzeptieren sollen, nicht diese. Während das, was Sie gepostet haben, das ist, was Sie letztendlich getan haben, beantwortet es die Frage, die Sie gestellt haben, nicht wirklich, während Omars Lösung dies zu tun scheint, was bedeutet, dass er die akzeptierte Anerkennung verdient.
mah
7
@Mah - es ist absolut unangemessen, jemanden zu schikanieren, weil er eine Antwort nicht akzeptiert hat, die fast ein Jahr nach der Lösung des Problems veröffentlicht wurde! Und das ignoriert sogar, wie viel komplizierter die Antwort ist, die Sie nominieren würden, als die, mit der sie gegangen sind - was tatsächlich die gestellte Frage beantwortet, da die Frage durch einen Fehler ausgelöst wurde, der zu dem falschen Eindruck führte, dass die Flagge unzuverlässig ist .
Chris Stratton
4
@ChrisStratton Wenn Sie glauben, meine Antwort sei Mobbing, lesen Sie im Internet nicht viel. Ich unterstütze Ihr Recht, Ihren gegnerischen Standpunkt in einem Kommentar wie Sie zu veröffentlichen. Daher habe ich meinen Kommentar und die anderen Beiträge in dieser Frage überprüft und stehe zu meinem ursprünglichen Kommentar: Das Poster hat eine Frage gestellt, die legitim war und von jemandem richtig beantwortet. Basierend auf seiner eigenen "Antwort" ist seine ursprüngliche Frage nicht das, was er überhaupt stellen wollte ... Die Signatur eines APK (Release oder Debug) hat genau keinen Einfluss auf das Manifest.
Mah
3
@mah - es liegt an dem Fragesteller, nicht an Ihnen, zu entscheiden, was seinem tatsächlichen Anwendungsbedarf entspricht, einschließlich der Frage, ob die von Ihnen angesprochene Unterscheidung von Bedeutung ist - oder, wie anscheinend in diesem Fall, nicht. Noch wichtiger ist, dass Sie völlig übersehen, dass die Antwort, die Sie nominieren würden, fast ein Jahr nach Befriedigung ihres tatsächlichen Bedarfs veröffentlicht wurde . Es bestraft jemanden, der fast ein Jahr später nicht zurückgekommen ist, um seine Akzeptanz zu ändern, was Mobbing darstellt.
Chris Stratton
3
@ChrisStratton Es ist auch Sache des Fragestellers, die eigentliche Frage zu stellen, auf die er eine Antwort haben möchte - etwas, das in diesem Fall nicht getan wurde. Sie haben Recht, dass ich übersehen habe, als die Antwort, die tatsächlich auf das, was gepostet wurde, antwortet, gegeben wurde. Es gibt jedoch überhaupt keine Bestrafung, es gibt nur einen vernünftigen Ausdruck meiner Meinung. Wenn Sie glauben, ich spiele hier den Mobber , bitte ich Sie dringend , meinen Kommentar wegen Missbrauchs zu kennzeichnen. Zuvor möchten Sie jedoch möglicherweise Ihre eigenen Beiträge hier überprüfen.
Mah
0

Lösung in Kotlin, die ich gerade benutze:

@SuppressLint("PackageManagerGetSignatures")
@Suppress("DEPRECATION")
fun isSigned(context: Context?): Boolean {
    return (context?.packageManager?.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)?.signatures?.firstOrNull()?.toByteArray()
            ?.let {
                return@let CertificateFactory.getInstance("X.509").generateCertificate(ByteArrayInputStream(it))
            } as? X509Certificate)
            ?.issuerDN
            ?.name
            ?.contains("O=Android", ignoreCase = false) ?: true
}

Auf diese Weise kann ich mich immer noch beim Debuggen SIGNIEREN und diese werden an Crashlytics gemeldet (Beispiel für den QS-Prozess).

Rafael Ruiz Muñoz
quelle