messagedigest - Método no encontrado usando DigestUtils en Android
sha256 java (6)
Estoy tratando de usar la biblioteca DigestUtils en Android 2.3.1 usando JDK 1.6, sin embargo recibo el siguiente error al ejecutar la aplicación:
Could not find method org.apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.apache.commons.codec.digest.DigestUtils.shaHex
Aquí tienes 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 línea de código que causa la excepción es:
String hash = DigestUtils.shaHex("textToHash");
¡He ejecutado el mismo código en una clase de Java fuera de Android y funciona! Por lo tanto, no sé por qué cuando se trabaja con Android no funciona ... Puse el libraty dentro de una nueva carpeta libs / en mi aplicación y actualicé el BuildPath para usarlo. Si trato de usar md5 en lugar de sha1, obtengo la misma excepción. ¡Cualquier ayuda sería apreciada! Gracias.
ACTUALIZAR:
Como esta es una pregunta muy activa, he cambiado la respuesta aceptada a favor de @ DA25, ya que su solución es sencilla y el elevado número de votos positivos demuestra que funciona.
Agregar método
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);
}
Como no hay una respuesta clara para la causa raíz de este problema, me gustaría aclarar lo que está sucediendo aquí.
¿Por qué se lanza el NoSuchMethodError en primer lugar?
Según el seguimiento de pila de excepción, la línea que causa el error es 226 en DigestUtils#md5hex
método DigestUtils#md5hex
. Veamos qué tenemos there (supongo que ha usado la versión 1.4, ya que esta es la única versión en la que se Hex#encodeHexString
método Hex#encodeHexString
en la línea 226):
public static String md5Hex(String data) {
return Hex.encodeHexString(md5(data));
}
La excepción dice java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString
. Vamos a entender por qué.
En primer lugar, Android Framework ya incluye la biblioteca de Commons Codec
(excepto la clase DigestUtils
). Sí, no está expuesto como parte del Android SDK
y no puede usarlo directamente. Pero aún quieres usarlo. ¿Entonces, que haces? Usted agrega la biblioteca Commons Codec
como parte de su aplicación. El compilador no se queja, desde su punto de vista todo estaba bien.
Pero, ¿qué sucede en tiempo de ejecución? Sigamos su rastro de pila de excepción:
En primer lugar, llama a DigestUtils#md5Hex
desde el método DigestUtils#md5Hex
de su actividad. Como escribí anteriormente, el framework no incluye esa clase, por lo que DigestUtils
(del Commons Codec
versión 1.4) se carga desde tu dex.
A continuación, el método md5hex
intenta invocar Hex#encodeHexString
método Hex#encodeHexString
. Hex
clase Hex
es parte de la biblioteca de Commons Codec
que se incluye en el marco. Lo que sucede es que su versión es 1.3 (versión antigua de julio de 2004). Hex
clase Hex
existe en el classpath de arranque, lo que significa que el tiempo de ejecución siempre lo favorecerá en lugar de la clase Hex
que se incluye dentro de su dex. Puede ver advertencias al respecto en los registros de su aplicación cuando inicie su aplicación (con el tiempo de ejecución 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 método Hex#encodeHexString se introdujo en la versión 1.4 de la librería Commons Codec
y, por lo tanto, no existe en la clase Hex
de framework. El tiempo de ejecución no puede encontrar este método y arroja la excepción NoSuchMethodError
.
¿Por qué funciona la solución de la respuesta aceptada?
String s = new String(Hex.encodeHex(DigestUtils.md5(data)));
Primero, se DigestUtils#md5
método DigestUtils#md5
. Como ya DigestUtils
, la clase DigestUtils
que se usará es la que se empaqueta en su DigestUtils
. Este método no utiliza ninguna otra clase de Commons Codec
, por lo que no hay problema.
A continuación, se Hex#encodeHex
. La clase Hex
que se utilizará es la del framework (versión 1.3). El método encodeHex
(que toma un único parámetro - matriz de bytes) existe en la versión 1.3 de la biblioteca de Commons Codec
, y por lo tanto este código funcionará bien.
¿Qué te sugiero?
Mi solución sugerida es cambiar el nombre del espacio de nombres / paquete de las clases. Al hacerlo, especifico explícitamente qué código se va a ejecutar y evito el comportamiento extraño que puede ocurrir debido a problemas de control de versiones.
Puedes hacerlo manualmente (como escribió Caumons en su respuesta), o automáticamente con la herramienta jarjar .
Vea este resumen y consejos para usar jarjar
en mi blogpost .
Finalmente recibo la respuesta y funciona bien. Como se describe en No hay tal error de método en el códec Apache para otro tipo de encriptación (Base64) intenté reproducir el mismo problema y recibo exactamente el mismo error. Así que estaba en el caso de la pregunta adjunta. Como dicen, parece ser una colisión de nombre interno con el nombre del paquete org.apache.commons.codec
y según lo indicado por @Don lo cambié a com.apache.commons.codec
y funcionó bien! ¿Cómo lo hice?
Descargué el código fuente y cambié los 3 directorios org
a com
. También reemplacé todas las apariciones del nombre del paquete en los archivos donde aparecen y también cambié las referencias en los documentos a com/apache/commons/codec/
. (No intente rehacerlos manualmente o pasará el día del agujero). Luego compilé la biblioteca y generé el contenedor con Ant, que llamé commons-codec-1.6-android.jar
. Puse el frasco en la carpeta libs/
de mi aplicación de Android y lo agregué al buildpath. Además, adjunté las fuentes como la carpeta que contiene todos los archivos. ¡Así que ahora tengo la biblioteca lista para usar con Android!
Espero que ayude a alguien más!
Gracias @ DA25
Esto está funcionando bien para mí
Tengo dependencia de agregar
compile ''commons-codec:commons-codec:1.9''
ref: http://mvnrepository.com/artifact/commons-codec/commons-codec/1.9
mi función
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;
}
Me encontré con el mismo problema al tratar de usar DigestUtils en mi aplicación de Android. Esta fue la mejor respuesta que pude encontrar buscando, pero me resistí a reconstruir el archivo .jar con el espacio de nombres modificado. Después de pasar algún tiempo en este tema, encontré una manera más fácil de resolver el problema en mi caso. La declaración del problema para mi código era
String s = DigestUtils.md5Hex(data);
Reemplace esta declaración con lo siguiente y funcionará:
String s = new String(Hex.encodeHex(DigestUtils.md5(data)));
Del mismo modo, para shaHex ejempl, puede cambiarlo a
String hash = new String(Hex.encodeHex(DigestUtils.sha("textToHash")));
Esto funciona porque aunque Android no tiene encodeHexString (), sí tiene encodeHex (). Espero que esto ayude a otros que se encuentran con el mismo problema.
Para mí, proguard eliminó la clase durante la ofuscación. Añada esto a sus reglas de Proguard.
-keep class org.apache.commons.** { *; }
Este es el método que estaba usando del paquete apache.
Hex.encodeHex(digest)