Hash MD5 in Android


91

Ho un semplice client Android che deve "parlare" con un semplice listener HTTP C #. Voglio fornire un livello base di autenticazione passando nome utente / password nelle richieste POST.

L'hashing MD5 è banale in C # e fornisce una sicurezza sufficiente per le mie esigenze, ma non riesco a trovare come farlo alla fine di Android.

EDIT: Giusto per affrontare le preoccupazioni sollevate sulla debolezza dell'MD5: il server C # funziona sui PC degli utenti del mio client Android. In molti casi, accederanno al server utilizzando il wi-fi sulla propria LAN ma, a proprio rischio, potrebbero scegliere di accedervi da Internet. Inoltre, il servizio sul server deve utilizzare il pass-through per l'MD5 a un'applicazione di terze parti su cui non ho alcun controllo.


6
Non utilizzare MD5. Usa SHA512.
SLaks

1
Perché? SHA512 non è più difficile di MD5. Non vuoi rimanere bloccato tra cinque anni con client legacy che utilizzano MD5.
SLaks

2
Spero che tu stia usando un nonce nel tuo protocollo, così puoi buttare via gli attacchi di replay.
sarnold

1
@ NickJohnson: Per rispondere alla tua domanda, perché dovresti selezionare deliberatamente l'opzione più debole? con un'altra domanda ... perché sentiresti il ​​bisogno di commentare una domanda che ho postato 16 mesi fa? Ma se vuoi davvero sapere (se guardi il commento a SLaks sopra il tuo), era il codice della fase alpha e il PC (non scritto da me) utilizzava l'hashing MD5. Il requisito era fondamentalmente per uno scenario pass-through senza coinvolgere ulteriore complessità. All'epoca avevo circa 10 tester della fase alfa che conoscevano i rischi. Da quando ho posto la domanda è stata incorporata una sicurezza più complessa.
Squonk

1
...che cosa? No, non è solo sbagliato, è pericolosamente sbagliato.
Nick Johnson,

Risposte:


231

Ecco un'implementazione che puoi utilizzare (aggiornata per utilizzare convenzioni Java più aggiornate - for:eachloop, StringBuilderinvece di 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 "";
}

Sebbene non sia consigliato per sistemi che implicano anche il livello di sicurezza di base (MD5 è considerato guasto e può essere facilmente sfruttato ), a volte è sufficiente per attività di base.


Grazie che farà il trucco. Vedi la mia modifica per sapere perché MD5 sarà sufficiente in questa fase.
Squonk

6
Vota questo IMO in modo che vengano visualizzate le risposte più corrette di seguito.
Adam

4
0 non è soddisfatto, come risposto di seguito.
SohailAziz

Aggiornato per utilizzare gli standard java più recenti (per: each, StringBuilder)
loeschg

3
Esistono sistemi operativi Android che non hanno implementato MD5 (causa lancio di NoSuchAlgorithmException)?
VSB

50

La risposta accettata non ha funzionato per me in Android 2.2. Non so perché, ma "mangiava" alcuni dei miei zeri (0). Anche Apache commons non funzionava su Android 2.2, perché utilizza metodi supportati solo a partire da Android 2.3.x. Inoltre, se vuoi solo MD5 una stringa, Apache commons è troppo complesso per quello. Perché si dovrebbe mantenere un'intera libreria per utilizzare solo una piccola funzione da essa ...

Finalmente ho trovato il seguente frammento di codice qui che ha funzionato perfettamente per me. Spero possa essere utile per qualcuno ...

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

4
Ha funzionato per me. Ho modificato md5.getBytes ("UTF-8") . L'ho controllato con: q4m'x68n6_YDB4ty8VC4 e} wqBtn ^ 68W con il codice sopra, il risultato è 0c70bb931f03b75af1591f261eb77d0b , NON il c70bb931f03b75af1591f261eb77d0b . 0 è a posto
Inoy

28

Il codice androidsnippets.com non funziona in modo affidabile perché gli 0 sembrano essere tagliati fuori dall'hash risultante.

Una migliore implementazione è qui .

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

"M" può mai essere nullo su Android, in questo caso?
sviluppatore Android

3
@androiddeveloper Hai perfettamente ragione. Il blocco catch è implementato male qui, ci deve essere un'istruzione return o ci sarà un errore di riferimento nullo quando viene chiamato m.update. Inoltre non ne sono sicuro, ma sebbene questo potrebbe non rimuovere gli 0 sui singoli byte, penso che possa ancora rimuovere gli 0 iniziali sull'intero hash a 32 caratteri. Invece di toString (16) penso che String.Format ("% 032X", bigInt) funzioni come previsto. Inoltre puoi scegliere se desideri che l'esadecimale sia in maiuscolo o minuscolo ("% 032x" in minuscolo).
rsimp

1
Questa versione è difettosa. Se il tuo MD5 inizia con "0", l'MD5 generato non avrà uno zero iniziale. Non utilizzare questa soluzione.
elcuco

20

Se l'utilizzo di Apache Commons Codec è un'opzione, questa sarebbe un'implementazione più breve:

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

Oppure SHA:

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

Fonte per sopra.

Segui il link e vota la sua soluzione per premiare la persona giusta.


Collegamento repo Maven: https://mvnrepository.com/artifact/commons-codec/commons-codec

Attuale dipendenza da Maven (al 6 luglio 2016):

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>

1
Qualche motivo per cui dovresti utilizzare una libreria esterna per un'API che esiste già nella libreria standard?
m0skit0

12

Una soluzione sopra usando DigestUtils non ha funzionato per me. Nella mia versione di Apache commons (l'ultima del 2013) non esiste una classe del genere.

Ho trovato un'altra soluzione qui in un blog . Funziona perfettamente e non necessita di comuni Apache. Sembra un po 'più corto del codice nella risposta accettata sopra.

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

Avrai bisogno di queste importazioni:

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

Importante sapere che l'hash MD5 ha una lunghezza di 32 caratteri o meno, grazie!
Pelanes

3
Le ultime righe possono probabilmente essere semplicemente sostituite con: return String.Format ("% 032X", number); Altrimenti questa risposta mi piace molto.
rsimp

10

Questa è una leggera variazione delle risposte di Andranik e Den Delimarsky sopra, ma è un po 'più concisa e non richiede alcuna logica bit per bit. Utilizza invece il String.formatmetodo integrato per convertire i byte in stringhe esadecimali di due caratteri (non rimuove gli 0). Normalmente vorrei solo commentare le loro risposte, ma non ho la reputazione di farlo.

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

Se invece desideri restituire una stringa minuscola, cambia semplicemente %02Xin %02x.

Modifica: utilizzando BigInteger come con la risposta di wzbozon, puoi rendere la risposta ancora più 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;
    }
}

4

Ho creato una semplice libreria in Kotlin.

Aggiungi a Root build.gradle

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

su App build.gradle

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

Utilizzo

A Kotlin

val ob = Hasher()

Quindi usa il metodo hash ()

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

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

Restituirà rispettivamente MD5 e SHA-1.

Maggiori informazioni sulla Biblioteca

https://github.com/ihimanshurawat/Hasher


2

Ecco la versione di Kotlin dalla risposta di @Andranik. Dobbiamo passare getBytesa toByteArray(non è necessario aggiungere il set di caratteri UTF-8 perché il set di caratteri predefinito di toByteArrayè UTF-8) e trasmettere array [i] a intero

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
}

Spero che aiuti


1

Nella nostra applicazione MVC generiamo per parametri lunghi

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

e lo stesso nell'applicazione Android (thenk aiuta 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();
}

1

ho usato il seguente metodo per darmi md5 passando la stringa per la quale vuoi ottenere 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 "";
}

1

Si prega di utilizzare SHA-512, MD5 non è sicuro

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

0

MD5 è un po 'vecchio, SHA-1 è un algoritmo migliore, c'è un esempio qui .

( Inoltre, come notano in quel post, Java lo gestisce da solo, nessun codice specifico per Android. )


4
No, non volevo alternative all'MD5 quando ho posto questa domanda nel gennaio 2011 (19 mesi fa) e non sono sicuro del motivo per cui hai sentito il bisogno di rispondere alla mia domanda a questo punto.
Squonk

21
@Squonk ho risposto perché questa è l'idea generale alla base di stackoverflow. Non importa quanto tempo dopo il fatto, cerca sempre di ottenere risposte migliori per le persone che potrebbero incontrare la domanda in seguito. Per quanto riguarda il suggerimento di SHA-1, non c'era modo per me di sapere che eri specificamente contro SHA-1, ma molti altri non lo saranno, quindi di nuovo, potrebbe aiutare altre persone in futuro che si imbatteranno in questo e dirigerli a un algoritmo più moderno.
Adam

3
Non riesco a pensare a una singola situazione in cui MD5 sia una cattiva scelta ma SHA1 una buona. Se hai bisogno di resistenza alle collisioni, hai bisogno di SHA2 non SHA1. Se hai password hash, hai bisogno di bcrypt o PBKDF2. O nel caso dell'OP la soluzione corretta è probabilmente SSL.
CodesInChaos

3
@Adam questa non è una risposta questo è un commento.
Antzi

0

Troppo dispendioso nella conversione in Hex () prevale in altri suggerimenti, davvero.

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

La risposta riguarda un toHex "migliore" quando la ricerca riguarda l'MD5, in quanto tale non risponde. Nota: Donald Knuth Il vero problema è che i programmatori hanno speso troppo tempo a preoccuparsi dell'efficienza nei posti sbagliati e nei momenti sbagliati; l'ottimizzazione prematura è la radice di tutti i mali (o almeno della maggior parte) nella programmazione.
Zaf

0

questo funziona perfettamente per me, l'ho usato per ottenere MD5 su LIST Array (quindi convertirlo in oggetto JSON), ma se hai solo bisogno di applicarlo ai tuoi dati. digita format, sostituisci JsonObject con il tuo.

Soprattutto se hai una mancata corrispondenza con l'implementazione di python MD5, usa questo!

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;


}

-1

Le soluzioni fornite per il linguaggio Scala (un po 'più breve):

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