Comment vérifier si l'APK est signé ou "debug build"?

121

Autant que je sache, dans Android "release build" est signé APK. Comment le vérifier à partir du code ou Eclipse a-t-il une sorte de définition secrète?

J'en ai besoin pour déboguer le remplissage des éléments ListView à partir des données du service Web (non, logcat pas une option).

Mes pensées:

  • Application android:debuggable, mais pour une raison qui ne semble pas fiable.
  • Le codage en dur de l'identifiant d'appareil n'est pas une bonne idée, car j'utilise le même appareil pour tester les APK signés.
  • Vous utilisez un indicateur manuel quelque part dans le code? Plausible, mais je vais certainement oublier de changer à un moment donné, et tous les programmeurs sont paresseux.
Im0rtalité
la source
Édition de Phil à dos roulé. Il ne s'agit pas de savoir si le programme est légalement distribué sur le marché ou non. Il s'agit de savoir si le programme est toujours en "mode débogage".
Im0rtality
Cette méthode est la plus simple pour le faire: stackoverflow.com/a/23844716/2296787
MBH

Réponses:

80

Il existe différentes manières de vérifier si l'application est construite à l'aide du débogage ou du certificat de version, mais la méthode suivante me semble la meilleure.

Selon les informations de la documentation Android Signing Your Application , la clé de débogage contient le nom distinctif du sujet suivant: " CN = Android Debug, O = Android, C = US ". Nous pouvons utiliser ces informations pour tester si le package est signé avec une clé de débogage sans codage en dur de la signature de clé de débogage dans notre code.

Donné:

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

Vous pouvez implémenter une méthode isDebuggable de cette façon:

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
la source
6
Pour aider à résoudre plusieurs correspondances d'importation, les classes utilisées ici sont java.security.cert.X509Certificate, java.security.cert.CertificateExceptionet android.content.pm.Signature. Toutes les autres classes ne présentent pas plusieurs matchs pour moi
Christian García
1
Réponse modifiée avec ces importations. Merci!
Cory Petosky
est-il suffisamment efficace pour être exécuté sur la méthode onCreate de la classe d'application?
développeur android
Je n'ai pas noté le temps d'exécution, mais je l'utilise dans mon application et je n'ai aucun problème d'efficacité.
Omar Rehman
Le résultat peut être mis en cache pour plus d'efficacité.
ftvs
138

Pour vérifier l'indicateur déboguable, vous pouvez utiliser ce code:

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

Kotlin:

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

Pour plus d'informations, consultez Sécurisation des applications LVL Android .

Sinon, si vous utilisez correctement Gradle, vous pouvez vérifier si BuildConfig.DEBUGest vrai ou faux.

Blundell
la source
il semble que cela vérifie toujours l'android du manifeste: debuggable
xster
2
Le premier teste Manifest debuggable, c'est obsolète. Le second n'est pas possible pour les bibliothèques, la lib aura son propre BuildConfig - pas capable d'importer le BuildConfig de l'application, qui utilise le Lib. Par conséquent, la réponse marquée est "ok"
Christoph
Cette réponse fonctionnera dans tous les cas quel que soit le projet de bibliothèque ou le projet d'application.
Lavekush Agrawal
131

Répondue par Mark Murphy

La solution la plus simple et la meilleure à long terme consiste à utiliser BuildConfig.DEBUG. C'est une booleanvaleur qui sera truepour une compilation de débogage, falsesinon:

if (BuildConfig.DEBUG) {
  // do something for a debug build
}
Zar E Ahmer
la source
8
Le seul inconvénient de cette approche est qu'elle ne fonctionnera pas dans les projets de bibliothèque (aar). Lorsque les bibliothèques sont construites, cela se traduira par false, donc même si une application qui utilise la bibliothèque est en mode débogage, cette vérification aboutira à false dans le code de la bibliothèque.
Vito Andolini
24

Si vous souhaitez vérifier un APKstatiquement, vous pouvez utiliser

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

Cela génère 0si le APKn'est pas déboguable et 1s'il l'est.

Frontières de la santé
la source
3
c'est la seule solution, à vérifier sur l'apk final. Les autres réponses supposent que vous avez la source.
Guillermo Tobar
1
aaptvit ici/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt
Casey
21

Peut-être en retard, mais des utilisations iosched BuildConfig.DEBUG

urSus
la source
est-il maintenant sûr à utiliser? il y a un article disant qu'il a des problèmes: digipom.com/be-careful-with-buildconfig-debug
développeur android
C'est la meilleure réponse!
Peter Fortuin
pas si vous écrivez une bibliothèque tierce et ne connaissez pas le package de BuildConfig au moment de la compilation.
Sam Dozor du
Sam, pouvez-vous nous en dire plus?
Agamemnus
10

Ajoutez d'abord ceci à votre fichier build.gradle, cela permettra également d'exécuter côte à côte les versions de débogage et de publication:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Ajoutez cette méthode:

public static boolean isDebug(Context context) {
    String pName = context.getPackageName();
    if (pName != null && pName.endsWith(".debug")) {
        return true;
    } else {
        return false;
    }
}
Méchant homme
la source
1
Je préfère cette réponse, car elle est fiable. J'ai cependant besoin d'ajouter une nouvelle entrée "Application Android autorisée" à ma clé API Google Maps (car l'ID de l'application est différent).
Baz
5

Une version de débogage est également signée, juste avec une clé différente. Il est généré automatiquement par Eclipse et son certificat n'est valable qu'un an. Quel est le problème android:debuggable? Vous pouvez obtenir cette valeur à partir du code en utilisant PackageManager.

Nikolay Elenkov
la source
3

Une autre option, à mentionner. Si vous devez exécuter du code uniquement lorsque le débogueur est attaché, utilisez ce code:

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

Résolu avec android:debuggable. C'était un bogue dans la lecture de l'élément où, dans certains cas, l'indicateur de débogage sur l'élément n'était pas stocké dans l'enregistrement, if (m.debug && !App.isDebuggable(getContext()))toujours évalué false. Ma faute.

Im0rtalité
la source
13
Je me rends compte que cela remonte à plus d'un an, mais vous auriez dû accepter la réponse de @Omar Rehman, pas celle-ci. Bien que ce que vous avez publié soit ce que vous avez finalement fait, cela ne répond pas vraiment à la question que vous avez posée, alors que la solution d'Omar semble le faire, ce qui signifie qu'il mérite le crédit accepté.
mah
7
@Mah - il est tout à fait inapproprié d'intimider quelqu'un pour ne pas avoir accepté une réponse qui a été publiée près d'un an après avoir résolu son problème lui-même! Et cela ignore même à quel point la réponse que vous nommez est beaucoup plus compliquée que celle avec laquelle ils sont allés - qui répond en fait à la question posée, puisque la question a été provoquée par un bug conduisant à une impression erronée que le drapeau n'est pas fiable .
Chris Stratton
4
@ChrisStratton si vous pensez que ma réponse était de l'intimidation, je pense que vous ne lisez pas beaucoup Internet. Je soutiens votre droit d'afficher votre point de vue opposé dans un commentaire comme vous l'avez fait, et par conséquent, j'ai examiné mon commentaire et les autres messages de cette question, et je maintiens mon commentaire d'origine: l'affiche a posé une question qui était légitimement et correctement répondu par quelqu'un. Sur la base de sa propre «réponse», sa question initiale n'est pas ce qu'il voulait demander en premier lieu ... la signature d'un APK (version ou débogage) n'a aucun rapport avec le manifeste.
mah
3
@mah - c'est au demandeur, et non à vous de décider de ce qui satisfait son besoin d'application réel, y compris si la distinction que vous soulevez est importante - ou, comme apparemment dans ce cas, non. Plus important encore, vous oubliez complètement que la réponse que vous proposez a été publiée près d'un an après que leur besoin réel ait été satisfait . C'est punir quelqu'un de ne pas revenir pour changer son acceptation presque un an plus tard, ce qui constitue de l'intimidation.
Chris Stratton
3
@ChrisStratton c'est aussi au demandeur de poser la question réelle à laquelle il veut une réponse - quelque chose qui n'a pas été fait dans ce cas. Vous avez raison de dire que j'ai oublié quand la réponse qui répond réellement à ce qui a été affiché a été faite, mais il n'y a aucune punition du tout, il n'y a qu'une expression raisonnable de mon opinion. Si vous pensez que je suis l'intimidateur ici, je vous demande fortement de signaler mon commentaire pour abus. Avant de faire cela cependant, vous voudrez peut-être examiner vos propres publications ici.
mah du
0

Solution dans Kotlin que j'utilise en ce moment:

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

de cette façon, je peux toujours SIGNER en débogage et ceux-ci seront signalés à Crashlytics (exemple, pour le processus d'assurance qualité)

Rafael Ruiz Muñoz
la source