Hash MD5 en Android

91

Tengo un cliente de Android simple que necesita 'hablar' con un oyente HTTP de C # simple. Quiero proporcionar un nivel básico de autenticación pasando el nombre de usuario / contraseña en las solicitudes POST.

El hash MD5 es trivial en C # y proporciona suficiente seguridad para mis necesidades, pero parece que no puedo encontrar cómo hacerlo en el extremo de Android.

EDITAR: Solo para abordar las preocupaciones planteadas sobre la debilidad de MD5: el servidor C # se ejecuta en las PC de los usuarios de mi cliente Android. En muchos casos, accederán al servidor usando wi-fi en sus propias LAN pero, bajo su propio riesgo, pueden optar por acceder a él desde Internet. Además, el servicio en el servidor debe utilizar el paso a través del MD5 a una aplicación de terceros sobre la que no tengo control.

Squonk
fuente
6
No utilice MD5. Utilice SHA512.
SLaks
1
¿Por qué? SHA512 no es más difícil que MD5. No querrá quedarse atascado dentro de cinco años con clientes heredados que usan MD5.
SLaks
2
Espero que estés usando un nonce en tu protocolo, para que puedas descartar los ataques de repetición.
sarnold
1
@NickJohnson: Para responder a su pregunta, ¿por qué seleccionaría deliberadamente la opción más débil? con otra pregunta ... ¿por qué sentirías la necesidad de comentar una pregunta que publiqué hace 16 meses? Pero si realmente quieres saber (si miras el comentario de SLaks arriba del tuyo), era un código de etapa alfa y el extremo de la PC (no escrito por mí) usó hash MD5. El requisito era básicamente un escenario de transferencia sin implicar una complejidad adicional. En ese momento, tenía alrededor de 10 probadores en etapa alfa que conocían los riesgos. Se ha incorporado una seguridad más compleja desde que hice la pregunta.
Squonk
1
...¿qué? No, eso no solo está mal, está peligrosamente mal.
Nick Johnson

Respuestas:

231

Aquí hay una implementación que puede usar (actualizada para usar convenciones Java más actualizadas - for:eachbucle, en StringBuilderlugar de StringBuffer):

public static String md5(final String s) {
    final String MD5 = "MD5";
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest
                .getInstance(MD5);
        digest.update(s.getBytes());
        byte messageDigest[] = digest.digest();

        // Create Hex String
        StringBuilder hexString = new StringBuilder();
        for (byte aMessageDigest : messageDigest) {
            String h = Integer.toHexString(0xFF & aMessageDigest);
            while (h.length() < 2)
                h = "0" + h;
            hexString.append(h);
        }
        return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

Aunque no se recomienda para sistemas que involucran incluso el nivel básico de seguridad (MD5 se considera roto y se puede explotar fácilmente ), a veces es suficiente para tareas básicas.

Den Delimarsky
fuente
Gracias, eso hará el truco. Vea mi edición sobre por qué MD5 será suficiente en esta etapa.
Squonk
6
Vote esto en contra de la OMI para que aparezcan las respuestas más correctas a continuación.
Adam
4
0 no atendidos, como se responde a continuación.
SohailAziz
Actualizado para usar estándares java más nuevos (para: cada uno, StringBuilder)
loeschg
3
¿Hay algún sistema operativo Android que no haya implementado MD5 (causa el lanzamiento NoSuchAlgorithmException)?
VSB
50

La respuesta aceptada no funcionó para mí en Android 2.2. No sé por qué, pero se estaba "comiendo" algunos de mis ceros (0). Apache commons tampoco funcionaba en Android 2.2, porque utiliza métodos que solo son compatibles a partir de Android 2.3.x. Además, si solo desea MD5 una cadena, Apache commons es demasiado complejo para eso. Por qué uno debería mantener una biblioteca completa para usar solo una pequeña función de ella ...

Finalmente encontré el siguiente fragmento de código aquí que funcionó perfectamente para mí. Espero que sea de utilidad para alguien ...

public String MD5(String md5) {
   try {
        java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        byte[] array = md.digest(md5.getBytes("UTF-8"));
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < array.length; ++i) {
          sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
       }
        return sb.toString();
    } catch (java.security.NoSuchAlgorithmException e) {
    } catch(UnsupportedEncodingException ex){
    }
    return null;
}
Andranik
fuente
4
Trabajó para mi. He cambiado md5.getBytes ("UTF-8") . Lo he comprobado con: q4m'x68n6_YDB4ty8VC4 &} wqBtn ^ 68W con el código anterior, el resultado es 0c70bb931f03b75af1591f261eb77d0b , NO el c70bb931f03b75af1591f261eb77d0b . 0 está en su lugar
Inoy
28

El código de androidsnippets.com no funciona de manera confiable porque los ceros parecen cortarse del hash resultante.

Una mejor implementación está aquí .

public static String MD5_Hash(String s) {
    MessageDigest m = null;

    try {
            m = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
    }

    m.update(s.getBytes(),0,s.length());
    String hash = new BigInteger(1, m.digest()).toString(16);
    return hash;
}
cristiano
fuente
¿Puede "m" ser nulo en Android, en este caso?
desarrollador de Android
3
@androiddeveloper Tienes toda la razón. El bloque de captura está mal implementado aquí, es necesario que haya una declaración de retorno o habrá un error de referencia nula cuando se llame a m.update. Además, no estoy seguro, pero si bien esto puede no eliminar los 0 en los bytes individuales, creo que aún puede eliminar los 0 iniciales en todo el hash de 32 caracteres. En lugar de toString (16), creo que String.Format ("% 032X", bigInt) funciona según lo previsto. Además, puede elegir si desea el hexadecimal en mayúsculas o minúsculas ("% 032x" para minúsculas).
rsimp
1
Esta versión es defectuosa. Si su MD5 comienza con "0", el MD5 generado no tendrá un 0 a la izquierda. No utilice esta solución.
elcuco
20

Si usar Apache Commons Codec es una opción, entonces esta sería una implementación más corta:

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

O SHA:

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

Fuente de arriba.

Siga el enlace y vote a favor de su solución para adjudicar a la persona correcta.


Enlace de repositorio de Maven: https://mvnrepository.com/artifact/commons-codec/commons-codec

Dependencia actual de Maven (a partir del 6 de julio de 2016):

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>
tbraun
fuente
1
¿Alguna razón por la que usaría una biblioteca externa para una API que ya existe en la biblioteca estándar?
m0skit0
12

Una solución anterior usando DigestUtils no funcionó para mí. En mi versión de Apache commons (la última de 2013) no existe tal clase.

Encontré otra solución aquí en un blog . Funciona perfectamente y no necesita Apache commons. Parece un poco más corto que el código en la respuesta aceptada anterior.

public static String getMd5Hash(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] messageDigest = md.digest(input.getBytes());
        BigInteger number = new BigInteger(1, messageDigest);
        String md5 = number.toString(16);

        while (md5.length() < 32)
            md5 = "0" + md5;

        return md5;
    } catch (NoSuchAlgorithmException e) {
        Log.e("MD5", e.getLocalizedMessage());
        return null;
    }
}

Necesitará estas importaciones:

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Denis Kutlubaev
fuente
Es importante saber si el hash MD5 tiene una longitud de 32 caracteres o menos, ¡gracias!
Pelanes
3
Las últimas líneas probablemente se pueden reemplazar con: return String.Format ("% 032X", número); De lo contrario, me gusta mucho esta respuesta.
rsimp
10

Esta es una ligera variación de las respuestas de Andranik y Den Delimarsky anteriores, pero es un poco más concisa y no requiere ninguna lógica bit a bit. En su lugar, utiliza el String.formatmétodo incorporado para convertir los bytes en cadenas hexadecimales de dos caracteres (no elimina los ceros). Normalmente, solo comentaría sus respuestas, pero no tengo la reputación de hacerlo.

public static String md5(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");

        StringBuilder hexString = new StringBuilder();
        for (byte digestByte : md.digest(input.getBytes()))
            hexString.append(String.format("%02X", digestByte));

        return hexString.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }
}

Si en su lugar desea devolver una cadena en minúsculas, simplemente cambie %02Xa %02x.

Editar: usando BigInteger como con la respuesta de wzbozon, puede hacer que la respuesta sea aún más concisa:

public static String md5(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        BigInteger md5Data = new BigInteger(1, md.digest(input.getBytes()));
        return String.Format("%032X", md5Data);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }
}
rsimp
fuente
4

Hice una biblioteca simple en Kotlin.

Agregar en Root build.gradle

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

en App build.gradle

implementation 'com.github.1AboveAll:Hasher:-SNAPSHOT'

Uso

En Kotlin

val ob = Hasher()

Luego use el método hash ()

ob.hash("String_You_Want_To_Encode",Hasher.MD5)

ob.hash("String_You_Want_To_Encode",Hasher.SHA_1)

Devolverá MD5 y SHA-1 respectivamente.

Más sobre la biblioteca

https://github.com/ihimanshurawat/Hasher

Himanshu Rawat
fuente
2

Aquí está la versión de Kotlin de la respuesta de @Andranik. Necesitamos cambiar getBytesa toByteArray(no es necesario agregar el juego de caracteres UTF-8 porque el toByteArrayjuego de caracteres predeterminado es UTF-8) y convertir la matriz [i] a un entero

fun String.md5(): String? {
    try {
        val md = MessageDigest.getInstance("MD5")
        val array = md.digest(this.toByteArray())
        val sb = StringBuffer()
        for (i in array.indices) {
            sb.append(Integer.toHexString(array[i].toInt() and 0xFF or 0x100).substring(1, 3))
        }
        return sb.toString()
    } catch (e: java.security.NoSuchAlgorithmException) {
    } catch (ex: UnsupportedEncodingException) {
    }
    return null
}

Espero que ayude

Phan Van Linh
fuente
1

En nuestra aplicación MVC generamos param largo

using System.Security.Cryptography;
using System.Text;
    ...
    public static string getMD5(long id)
    {
        // convert
        string result = (id ^ long.MaxValue).ToString("X") + "-ANY-TEXT";
        using (MD5 md5Hash = MD5.Create())
        {
            // Convert the input string to a byte array and compute the hash. 
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(result));

            // Create a new Stringbuilder to collect the bytes and create a string.
            StringBuilder sBuilder = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
                sBuilder.Append(data[i].ToString("x2"));

            // Return the hexadecimal string. 
            result = sBuilder.ToString().ToUpper();
        }

        return result;
    }

y lo mismo en la aplicación de Android (thenk ayuda a Andranik)

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
...
public String getIdHash(long id){
    String hash = null;
    long intId = id ^ Long.MAX_VALUE;
    String md5 = String.format("%X-ANY-TEXT", intId);
    try {
        MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        byte[] arr = md.digest(md5.getBytes());
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < arr.length; ++i)
            sb.append(Integer.toHexString((arr[i] & 0xFF) | 0x100).substring(1,3));

        hash = sb.toString();
    } catch (NoSuchAlgorithmException e) {
        Log.e("MD5", e.getMessage());
    }

    return hash.toUpperCase();
}
Oleg Grabets
fuente
1

He utilizado el método siguiente para darme md5 pasando la cadena para la que desea obtener md5

public static String getMd5Key(String password) {

//        String password = "12131123984335";

        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(password.getBytes());

            byte byteData[] = md.digest();

            //convert the byte to hex format method 1
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < byteData.length; i++) {
                sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
            }

            System.out.println("Digest(in hex format):: " + sb.toString());

            //convert the byte to hex format method 2
            StringBuffer hexString = new StringBuffer();
            for (int i = 0; i < byteData.length; i++) {
                String hex = Integer.toHexString(0xff & byteData[i]);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            System.out.println("Digest(in hex format):: " + hexString.toString());

            return hexString.toString();

        } catch (Exception e) {
            // TODO: handle exception
        }

        return "";
}
Ghanshyam Patidar
fuente
1

Utilice SHA-512, MD5 no es seguro

public static String getSHA512SecurePassword(String passwordToHash) {
    String generatedPassword = null;
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-512");
        md.update("everybreathyoutake".getBytes());
        byte[] bytes = md.digest(passwordToHash.getBytes());
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        generatedPassword = sb.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return generatedPassword;
}
Carlos Jesús Arancibia Taborga
fuente
0

MD5 es un poco antiguo, SHA-1 es un algoritmo mejor, aquí hay un ejemplo .

( Además, como señalan en esa publicación, Java maneja esto por sí solo, sin código específico de Android ) .

Adán
fuente
4
No, no quería alternativas a MD5 cuando hice esta pregunta en enero de 2011 (hace 19 meses) y no estoy seguro de por qué sintió la necesidad de responder a mi pregunta en este momento.
Squonk
21
@Squonk Respondí porque esa es la idea general detrás de stackoverflow. No importa cuánto tiempo después del hecho, siempre trate de obtener mejores respuestas para las personas que puedan encontrar la pregunta más tarde. En cuanto a sugerir SHA-1, no había forma de que yo supiera que estabas específicamente en contra de SHA-1, pero muchos otros no lo estarán, así que nuevamente, puede ayudar a otras personas en el futuro que se encuentren con esto y dirigirlos. a un algoritmo más moderno.
Adam
3
No puedo pensar en una sola situación en la que MD5 sea una mala elección pero SHA1 una buena. Si necesita resistencia a colisiones, necesita SHA2, no SHA1. Si utiliza hash en las contraseñas, necesita bcrypt o PBKDF2. O en el caso del OP, la solución adecuada probablemente sea SSL.
CodesInChaos
3
@Adam, esto no es una respuesta, es un comentario.
Antzi
0

La conversión de toHex () demasiado derrochadora prevalece en otras sugerencias, de verdad.

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

public static String md5string(String s) {
    return toHex(md5plain(s));
}

public static byte[] md5plain(String s) {
    final String MD5 = "MD5";
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest.getInstance(MD5);
        digest.update(s.getBytes());
        return digest.digest();
    } catch (NoSuchAlgorithmException e) {
        // never happens
        e.printStackTrace();
        return null;
    }
}

public static String toHex(byte[] buf) {
    char[] hexChars = new char[buf.length * 2];
    int v;
    for (int i = 0; i < buf.length; i++) {
        v = buf[i] & 0xFF;
        hexChars[i * 2] = HEX_ARRAY[v >>> 4];
        hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}
Gena Batsyan
fuente
La respuesta es acerca de un toHex "mejor" cuando la búsqueda es sobre MD5, como tal, no responde. Nota: Donald Knuth El verdadero problema es que los programadores han pasado demasiado tiempo preocupándose por la eficiencia en los lugares y momentos equivocados; La optimización prematura es la raíz de todos los males (o al menos la mayor parte) en la programación.
zaph
0

esto funciona perfectamente para mí, lo usé para obtener MD5 en LIST Array (luego convertirlo en un objeto JSON), pero si solo necesita aplicarlo en sus datos. escriba formato, reemplace JsonObject con el suyo.

¡Especialmente si tiene una discrepancia con la implementación de Python MD5, use esto!

private static String md5(List<AccelerationSensor> sensor) {

    Gson gson= new Gson();
    byte[] JsonObject = new byte[0];
    try {
        JsonObject = gson.toJson(sensor).getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    MessageDigest m = null;

    try {
        m = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    byte[] thedigest = m.digest(JsonObject);
    String hash = String.format("%032x", new BigInteger(1, thedigest));
    return hash;


}
mehran sahandi lejos
fuente
-1

Las soluciones proporcionadas para el lenguaje Scala (un poco más corto):

def getMd5(content: Array[Byte]) =
    try {
        val md = MessageDigest.getInstance("MD5")
        val bytes = md.digest(content)
        bytes.map(b => Integer.toHexString((b + 0x100) % 0x100)).mkString
    } catch {
        case ex: Throwable => null
    }
david.perez
fuente