web-dev-qa-db-fra.com

Méthode introuvable à l'aide de DigestUtils dans Android

J'essaie d'utiliser la bibliothèque DigestUtils in Android 2.3.1 en utilisant JDK 1.6, cependant j'obtiens l'erreur suivante lors de l'exécution de l'application:

Could not find method org.Apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.Apache.commons.codec.digest.DigestUtils.shaHex

Voici la stacktrace:

02-03 10:25:45.153: I/dalvikvm(1230): Could not find method org.Apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.Apache.commons.codec.digest.DigestUtils.shaHex
02-03 10:25:45.153: W/dalvikvm(1230): VFY: unable to resolve static method 329: Lorg/Apache/commons/codec/binary/Hex;.encodeHexString ([B)Ljava/lang/String;
02-03 10:25:45.153: D/dalvikvm(1230): VFY: replacing opcode 0x71 at 0x0004
02-03 10:25:45.153: D/dalvikvm(1230): VFY: dead code 0x0007-0008 in Lorg/Apache/commons/codec/digest/DigestUtils;.shaHex ([B)Ljava/lang/String;
02-03 10:25:45.163: D/AndroidRuntime(1230): Shutting down VM
02-03 10:25:45.163: W/dalvikvm(1230): threadid=1: thread exiting with uncaught exception (group=0x40015560)
02-03 10:25:45.173: E/AndroidRuntime(1230): FATAL EXCEPTION: main
02-03 10:25:45.173: E/AndroidRuntime(1230): Java.lang.NoSuchMethodError: org.Apache.commons.codec.binary.Hex.encodeHexString
02-03 10:25:45.173: E/AndroidRuntime(1230):     at org.Apache.commons.codec.digest.DigestUtils.md5Hex(DigestUtils.Java:226)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at com.caumons.trainingdininghall.ConnectionProfileActivity.onCreate(ConnectionProfileActivity.Java:20)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Android.app.Instrumentation.callActivityOnCreate(Instrumentation.Java:1047)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Android.app.ActivityThread.performLaunchActivity(ActivityThread.Java:1586)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:1638)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Android.app.ActivityThread.access$1500(ActivityThread.Java:117)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:928)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Android.os.Handler.dispatchMessage(Handler.Java:99)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Android.os.Looper.loop(Looper.Java:123)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Android.app.ActivityThread.main(ActivityThread.Java:3647)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Java.lang.reflect.Method.invokeNative(Native Method)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at Java.lang.reflect.Method.invoke(Method.Java:507)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:839)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:597)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at dalvik.system.NativeStart.main(Native Method)

La ligne de code qui provoque l'exception est:

String hash = DigestUtils.shaHex("textToHash");

J'ai exécuté le même code dans une classe Java à l'extérieur Android et cela fonctionne! Donc, je ne sais pas pourquoi lorsque je travaille avec Android cela ne fonctionne pas ... J'ai mis la libraty dans un nouveau dossier libs/dans mon application et mis à jour le BuildPath pour l'utiliser. Si j'essaie d'utiliser md5 au lieu de sha1, je reçois la même exception. Toute aide serait être apprécié! Merci.

MISE À JOUR:

Comme il s'agit d'une question très active, j'ai changé la réponse acceptée en faveur de @ DA25, car sa solution est simple et le nombre élevé de votes positifs prouve que cela fonctionne.

68
Caumons

J'ai rencontré le même problème en essayant d'utiliser DigestUtils dans mon Android. C'était la meilleure réponse que j'ai pu trouver en recherchant, mais j'étais réticent à reconstruire le fichier .jar avec l'espace de noms modifié. Après avoir passé un peu de temps sur ce problème, j'ai trouvé un moyen plus simple de résoudre le problème pour mon cas. L'énoncé du problème pour mon code était

String s = DigestUtils.md5Hex(data);

Remplacez cette déclaration par ce qui suit et cela fonctionnera:

String s = new String(Hex.encodeHex(DigestUtils.md5(data)));

De même, pour l'exemple shaHex, vous pouvez le changer en

String hash = new String(Hex.encodeHex(DigestUtils.sha("textToHash")));

Cela fonctionne parce que même si Android n'a pas encodeHexString (), il a encodeHex (). J'espère que cela aiderait ceux qui rencontrent le même problème.

135
DA25

Puisqu'il n'y a pas de réponse claire à la cause première de ce problème, je voudrais clarifier ce qui se passe ici.

Pourquoi la NoSuchMethodError est levée en premier lieu?

Selon la trace de la pile d'exceptions, la ligne qui provoque l'erreur est 226 dans DigestUtils#md5hex méthode. Voyons ce que nous avons (je suppose que vous avez utilisé la version 1.4, car c'est la seule version où Hex#encodeHexString la méthode est appelée à la ligne 226):

public static String md5Hex(String data) {
    return Hex.encodeHexString(md5(data));
}

L'exception indique Java.lang.NoSuchMethodError: org.Apache.commons.codec.binary.Hex.encodeHexString. Comprenons pourquoi.

Tout d'abord, Android inclut déjà le Commons Codec bibliothèque (sauf la classe DigestUtils). Oui, il n'est pas exposé dans le cadre du Android SDK et vous ne pouvez pas l'utiliser directement. Mais vous voulez toujours l'utiliser. Alors qu'est-ce que tu fais? Vous ajoutez Commons Codec bibliothèque dans le cadre de votre application. Le compilateur ne se plaint pas - de son point de vue, tout allait bien.

Mais que se passe-t-il à l'exécution? Suivons votre trace de pile d'exceptions:
Tout d'abord, vous appelez DigestUtils#md5Hex à partir de la méthode onCreate de votre activité. Comme je l'ai écrit ci-dessus, le cadre n'inclut pas cette classe, donc DigestUtils (de Commons Codec version 1.4) est chargé depuis votre dex.
Suivant, md5hex la méthode tente d'appeler Hex#encodeHexString méthode. Hex classe fait partie de la Commons Codec bibliothèque incluse dans le framework. Le fait est que sa version est 1.3 (ancienne version de juillet 2004). La classe Hex existe dans le chemin d'accès aux classes de démarrage, ce qui signifie que le runtime la favorisera toujours à la place de la classe Hex qui est empaquetée dans votre dex. Vous pouvez voir des avertissements à ce sujet dans vos journaux d'application lorsque vous démarrez votre application (avec le runtime Dalvik):

D/dalvikvm? DexOpt: 'Lorg/Apache/commons/codec/binary/Hex;' has an earlier definition; blocking out
I/dalvikvm? DexOpt: not resolving ambiguous class 'Lorg/Apache/commons/codec/binary/Hex;'
D/dalvikvm? DexOpt: not verifying/optimizing 'Lorg/Apache/commons/codec/binary/Hex;': multiple definitions
I/dalvikvm? Could not find method org.Apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.Apache.commons.codec.digest.DigestUtils.md5Hex

Hex # encodeHexString La méthode a été introduite dans la version 1.4 de Commons Codec bibliothèque et donc elle n'existe pas dans la classe Hex du framework. Le runtime ne trouve pas cette méthode et lève donc une exception NoSuchMethodError.

Pourquoi la solution de réponse acceptée fonctionne-t-elle?

String s = new String(Hex.encodeHex(DigestUtils.md5(data)));

Première, DigestUtils#md5 la méthode est appelée. Comme je l'ai déjà dit, la classe DigestUtils qui sera utilisée est celle qui est empaquetée dans votre dex. Cette méthode n'utilise aucune autre Commons Codec classes, donc pas de problème avec ça.

Suivant, Hex#encodeHex sera appelé. La classe Hex qui sera utilisée est celle du framework (version 1.3). La méthode encodeHex (qui prend un seul paramètre - tableau d'octets) existe dans la version 1.3 de Commons Codec bibliothèque, et donc ce code fonctionnera bien.

Que suggérerais-je?

Ma solution suggérée consiste à renommer l'espace de noms/package des classes. Ce faisant, je spécifie explicitement le code à exécuter et évite les comportements bizarres pouvant survenir en raison de problèmes de version.

Vous pouvez le faire manuellement (comme l'a écrit Caumons dans sa réponse), ou automatiquement avec l'outil jarjar .

Consultez ce résumé du problème et des conseils pour utiliser jarjar dans mon blogpost .

31
Alex Lipov

Enfin j'ai la réponse et ça marche bien. Comme décrit dans Aucune erreur de méthode dans le codec Apache pour un autre type de cryptage (Base64), j'ai essayé de reproduire le même problème et j'obtiens exactement la même erreur. J'étais donc dans le cas de la question ci-jointe. Comme on dit, cela semble être une collision de nom interne avec le nom du package org.Apache.commons.codec et comme indiqué par @Don, je l'ai changé en com.Apache.commons.codec et a bien fonctionné! Comment je l'ai fait?

J'ai téléchargé le code source et changé les 3 répertoires org en com. J'ai également remplacé toutes les occurrences du nom du package dans les fichiers où elles apparaissent et j'ai également changé les références dans les documents en com/Apache/commons/codec/. (N'essayez pas de les remanier manuellement ou vous passerez la journée du trou). Ensuite, j'ai compilé la bibliothèque et généré le pot avec Ant, que j'ai appelé commons-codec-1.6-Android.jar. J'ai mis le pot dans le libs/ dossier de mon Android et l'ajoutée au buildpath. Aussi, j'ai attaché les sources en tant que dossier contenant tous les fichiers. Alors maintenant, j'ai la bibliothèque prête à l'emploi avec Android!

J'espère que cela aide quelqu'un d'autre!

19
Caumons

Merci @ DA25

Cela fonctionne bien pour moi

J'ai ajouter une dépendance

compile 'commons-codec:commons-codec:1.9'

réf: http://mvnrepository.com/artifact/commons-codec/commons-codec/1.9

ma fonction

public String encode(String key, String data) {
    try {

        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);

        return new String(Hex.encodeHex(sha256_HMAC.doFinal(data.getBytes("UTF-8"))));

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    return null;
}
2
Bikesh M

Pour moi, proguard a supprimé la classe pendant l'obfuscation. Ajoutez ceci à vos règles de Proguard.

-keep class org.Apache.commons.** { *; }

Voici la méthode que j'utilisais pour le package Apache.

Hex.encodeHex(digest)
2
pratham kesarkar

Nous avons utilisé le code ci-dessous et cela a fonctionné:

  HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString);
  String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));
0
Alok Gupta

Ajouter une méthode

public static String byteArrayToHexString(byte[] bytes) {
    final char[] toDigits = "0123456789abcdef".toCharArray();
    int l = bytes.length;
    char[] out = new char[l << 1];

    int i = 0; for (int j = 0; i < l; ++i) {
        out[(j++)] = toDigits[((0xF0 & bytes[i]) >>> 4)];
        out[(j++)] = toDigits[(0xF & bytes[i])];
    }
    return new String(out);
}
0
reznic