Come verificare se l'APK è firmato o "build di debug"?


121

Per quanto ne so, in Android "release build" è firmato APK. Come controllarlo dal codice o Eclipse ha una sorta di definizione segreta?

Ho bisogno di questo per eseguire il debug degli elementi ListView che popolano i dati del servizio Web (no, logcat non è un'opzione).

I miei pensieri:

  • L'applicazione è android:debuggable, ma per qualche motivo non sembra affidabile.
  • L'ID dispositivo hardcoded non è una buona idea, perché utilizzo lo stesso dispositivo per testare gli APK firmati.
  • Usando il flag manuale da qualche parte nel codice? Plausibile, ma prima o poi dimenticherò di cambiare, inoltre tutti i programmatori sono pigri.

Modifica di Phil ripristinata. Non si tratta di una questione se il programma sia legalmente distribuito sul mercato o meno. Si tratta di un programma ancora in "modalità debug".
Im0rtality

Questo è il modo più semplice per farlo: stackoverflow.com/a/23844716/2296787
MBH

Risposte:


80

Esistono diversi modi per verificare se l'applicazione è stata compilata utilizzando il debug o il certificato di rilascio, ma il modo seguente mi sembra il migliore.

Secondo le informazioni nella documentazione di Android Firma dell'applicazione , la chiave di debug contiene il seguente nome distinto del soggetto: " CN = Android Debug, O = Android, C = US ". Possiamo usare queste informazioni per verificare se il pacchetto è firmato con la chiave di debug senza codificare la firma della chiave di debug nel nostro codice.

Dato:

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

Puoi implementare un metodo isDebuggable in questo modo:

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

6
Per aiutare a risolvere più corrispondenze di importazione, le classi utilizzate qui sono java.security.cert.X509Certificate, java.security.cert.CertificateExceptione android.content.pm.Signature. Tutte le altre classi non presentano più partite per me
Christian García

1
Risposta modificata con queste importazioni. Grazie!
Cory Petosky

è abbastanza efficiente da essere eseguito sul metodo onCreate della classe dell'applicazione?
sviluppatore Android

Non ho notato il tempo di esecuzione, ma l'ho usato nella mia app e non ho problemi di efficienza.
Omar Rehman

Il risultato può essere memorizzato nella cache per maggiore efficienza.
ftvs

138

Per controllare il flag debuggabile, puoi usare questo codice:

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

Kotlin:

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

Per ulteriori informazioni, vedere Protezione delle applicazioni LVL Android .

In alternativa, se stai usando Gradle correttamente, puoi verificare se BuildConfig.DEBUGè vero o falso.


sembra che questo controlli ancora Android di manifest: debuggable
xster

2
Il primo test per il debuggable Manifest, questo è deprecato. Il secondo non è possibile per le librerie, la lib avrà il proprio BuildConfig - non è in grado di importare il BuildConfig dell'app, che sta usando Lib. Pertanto la risposta contrassegnata è "ok"
Christoph

Questa risposta funzionerà in tutti i casi indipendentemente dal progetto della libreria o dal progetto dell'applicazione.
Lavekush Agrawal

131

Risposta di Mark Murphy

La soluzione più semplice e migliore a lungo termine è quella di utilizzare BuildConfig.DEBUG. Questo è un booleanvalore che sarà trueper una build di debug, falsealtrimenti:

if (BuildConfig.DEBUG) {
  // do something for a debug build
}

8
L'unico svantaggio di questo approccio è che non funzionerà nei progetti di biblioteca (aar). Quando le librerie vengono create, questo risulterà falso, quindi anche se un'applicazione che utilizza la libreria è in modalità di debug, questo controllo risulterà falso all'interno del codice della libreria.
Vito Andolini

24

Se vuoi controllare APKstaticamente, puoi usare

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

Questo genera 0se APKnon è possibile eseguire il debug e 1se lo è.


3
questa è l'unica soluzione, da verificare sull'apk finale. Le altre risposte presuppongono che tu abbia la fonte.
Guillermo Tobar

1
aaptvive qui/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt
Casey il


10

Per prima cosa aggiungilo al tuo file build.gradle, questo consentirà anche l'esecuzione fianco a fianco di build di debug e di rilascio:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Aggiungi questo metodo:

public static boolean isDebug(Context context) {
    String pName = context.getPackageName();
    if (pName != null && pName.endsWith(".debug")) {
        return true;
    } else {
        return false;
    }
}

1
Preferisco questa risposta, perché è affidabile. Tuttavia, avevo bisogno di aggiungere una nuova voce "applicazione Android consentita" alla mia chiave API di Google Maps (poiché l'ID dell'applicazione è diverso).
Baz

5

Anche una build di debug viene firmata, solo con una chiave diversa. Viene generato automaticamente da Eclipse e il suo certificato è valido solo per un anno. Qual è il problema android:debuggable? Puoi ottenere questo valore dal codice usando PackageManager.


3

Un'altra opzione, degna di nota. Se è necessario eseguire del codice solo quando è collegato il debugger, utilizzare questo codice:

if (Debug.isDebuggerConnected() || Debug.waitingForDebugger()) { 
    //code to be executed 
}

0

Risolto con android:debuggable. Era un bug nella lettura dell'elemento in cui in alcuni casi il flag di debug sull'elemento non veniva memorizzato nel record e veniva if (m.debug && !App.isDebuggable(getContext()))sempre valutato false. Colpa mia.


13
Mi rendo conto che ha più di un anno, tuttavia avresti dovuto accettare la risposta di @Omar Rehman, non questa. Mentre quello che hai pubblicato è quello che hai fatto alla fine, non risponde davvero alla domanda che hai posto, mentre la soluzione di Omar sembra farlo, il che significa che merita il credito accettato.
mah

7
@Mah - è assolutamente inappropriato intimidire qualcuno per non aver accettato una risposta che è stata pubblicata quasi un anno dopo aver risolto il problema da soli! E questo ignora anche quanto sia più complicata la risposta che sceglieresti di quella con cui sono andati - il che, in effetti, risponde alla domanda posta, poiché la domanda è stata suggerita da un bug che porta a un'impressione errata che la bandiera non sia affidabile .
Chris Stratton

4
@ChrisStratton se pensi che la mia risposta sia stata il bullismo, penso che non leggi molto su Internet. Sostengo il tuo diritto di pubblicare il tuo punto di vista opposto in un commento come hai fatto tu, e di conseguenza ho rivisto il mio commento e gli altri post in questa domanda, e mi attengo al mio commento originale: il poster ha posto una domanda che era legittimamente e risposto correttamente da qualcuno. Basandosi sulla sua "risposta", la sua domanda originale non è ciò che voleva chiedere in primo luogo ... la firma di un APK (rilascio o debug) ha esattamente zero rilevanza sul manifest.
mah

3
@mah - spetta al richiedente, non a te, decidere cosa soddisfa le loro effettive esigenze applicative, incluso se la distinzione che sollevi è importante o, come apparentemente in questo caso, no. Ancora più importante, trascuri completamente che la risposta che hai nominato è stata pubblicata quasi un anno dopo che il loro effettivo bisogno è stato soddisfatto . È punire qualcuno per non essere tornato a cambiare la propria accettazione quasi un anno dopo, il che costituisce bullismo.
Chris Stratton

3
@ChrisStratton spetta anche al richiedente fare la domanda effettiva a cui vuole una risposta, qualcosa che non è stato fatto in questo caso. Hai ragione che ho trascurato quando è stata data la risposta che effettivamente risponde a ciò che è stato pubblicato, tuttavia non c'è nessuna punizione, c'è solo una ragionevole espressione della mia opinione. Se pensi che stia agendo da bullo qui, ti chiedo vivamente di contrassegnare il mio commento come abuso. Prima di farlo, tuttavia, potresti voler esaminare i tuoi messaggi qui.
mah

0

Soluzione in Kotlin che sto usando al momento:

@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
}

in questo modo posso ancora ACCEDERE al debug e quelli verranno segnalati a Crashlytics (esempio, per il processo di QA)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.