Hachage MD5 sous Android

91

J'ai un client Android simple qui a besoin de «parler» à un simple écouteur HTTP C #. Je souhaite fournir un niveau d'authentification de base en passant un nom d'utilisateur / mot de passe dans les demandes POST.

Le hachage MD5 est trivial en C # et offre suffisamment de sécurité pour mes besoins, mais je n'arrive pas à trouver comment faire cela du côté Android.

EDIT: Juste pour répondre aux préoccupations soulevées au sujet de la faiblesse MD5 - le serveur C # fonctionne sur les PC des utilisateurs de mon client Android. Dans de nombreux cas, ils accèderont au serveur en utilisant le Wi-Fi sur leurs propres réseaux locaux, mais, à leurs propres risques, ils peuvent choisir d'y accéder depuis Internet. De plus, le service sur le serveur doit utiliser le relais du MD5 vers une application tierce sur laquelle je n'ai aucun contrôle.

Squonk
la source
6
N'utilisez pas MD5. Utilisez SHA512.
SLaks
1
Pourquoi? SHA512 n'est pas plus dur que MD5. Vous ne voulez pas être coincé dans cinq ans avec des clients hérités utilisant MD5.
SLaks
2
J'espère que vous utilisez un nonce dans votre protocole, afin que vous puissiez rejeter les attaques de relecture.
sarnold
1
@NickJohnson: Pour répondre à votre question, pourquoi choisiriez -vous délibérément l'option la plus faible? avec une autre question ... pourquoi ressentiriez-vous le besoin de commenter une question que j'ai postée il y a 16 mois? Mais si vous voulez vraiment savoir (si vous regardez le commentaire sur SLaks au-dessus du vôtre), c'était du code de scène alpha et l'extrémité PC (non écrite par moi) utilisait le hachage MD5. L'exigence était essentiellement un scénario de transmission sans impliquer de complexité supplémentaire. J'avais à l'époque environ 10 testeurs au stade alpha qui connaissaient les risques. Une sécurité plus complexe a été intégrée depuis que j'ai posé la question.
Squonk
1
...quelle? Non, ce n'est pas simplement faux, c'est dangereusement faux.
Nick Johnson

Réponses:

231

Voici une implémentation que vous pouvez utiliser (mise à jour pour utiliser des conventions Java plus à jour - for:eachboucle, StringBuilderau lieu 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 "";
}

Bien que cela ne soit pas recommandé pour les systèmes qui impliquent même le niveau de sécurité de base (MD5 est considéré comme cassé et peut être facilement exploité ), il est parfois suffisant pour les tâches de base.

Den Delimarsky
la source
Merci qui fera l'affaire. Voir mon édition pour savoir pourquoi MD5 suffira à ce stade.
Squonk
6
Votez pour IMO afin que les réponses les plus correctes ci-dessous soient présentées.
Adam
4
0 n'est pas pris en charge, comme répondu ci-dessous.
SohailAziz
Mise à jour pour utiliser les nouvelles normes Java (pour: each, StringBuilder)
loeschg
3
Existe-t-il un système d'exploitation Android qui n'a pas implémenté MD5 (cause du rejet de NoSuchAlgorithmException)?
VSB
50

La réponse acceptée n'a pas fonctionné pour moi dans Android 2.2. Je ne sais pas pourquoi, mais il "mangeait" certains de mes zéros (0). Apache commons ne fonctionnait pas non plus sur Android 2.2, car il utilise des méthodes qui ne sont prises en charge qu'à partir d'Android 2.3.x. De plus, si vous voulez juste MD5 une chaîne, Apache commons est trop complexe pour cela. Pourquoi faut-il garder une bibliothèque entière pour n'en utiliser qu'une petite fonction ...

Enfin, j'ai trouvé l'extrait de code suivant ici qui a parfaitement fonctionné pour moi. J'espère que ce sera utile pour quelqu'un ...

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
la source
4
A travaillé pour moi. J'ai changé md5.getBytes ("UTF-8") . Je l'ai vérifié avec: q4m'x68n6_YDB4ty8VC4 &} wqBtn ^ 68W avec le code ci-dessus, le résultat est 0c70bb931f03b75af1591f261eb77d0b , PAS le c70bb931f03b75af1591f261eb77d0b . 0 est en place
Inoy
28

Le code androidsnippets.com ne fonctionne pas de manière fiable car les 0 semblent être supprimés du hachage résultant.

Une meilleure implémentation est ici .

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;
}
Christian
la source
"M" peut-il jamais être nul sur Android, dans ce cas?
développeur android
3
@androiddeveloper Vous avez tout à fait raison. Le bloc catch est mal implémenté ici, il doit y avoir une instruction return ou il y aura une erreur de référence nulle lorsque m.update est appelé. De plus, je ne suis pas sûr, mais bien que cela ne supprime pas les 0 sur les octets individuels, je pense que cela peut toujours supprimer les 0 de début sur l'ensemble du hachage de 32 caractères. Au lieu de toString (16), je pense que String.Format ("% 032X", bigInt) fonctionne comme prévu. De plus, vous pouvez choisir si vous voulez que l'hexagone soit en majuscules ou en minuscules ("% 032x" pour le bas).
rsimp
1
Cette version est imparfaite. Si votre MD5 commence par "0", le MD5 généré n'aura pas de 0 en tête. Veuillez ne pas utiliser cette solution.
elcuco
20

Si l'utilisation du codec Apache Commons est une option, alors ce serait une implémentation plus courte:

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

Ou SHA:

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

Source ci-dessus.

Veuillez suivre le lien et voter pour sa solution pour attribuer la bonne personne.


Lien de dépôt Maven: https://mvnrepository.com/artifact/commons-codec/commons-codec

Dépendance actuelle de Maven (au 6 juillet 2016):

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>
tbraun
la source
1
Une raison pour laquelle vous utiliseriez une bibliothèque externe pour une API qui existe déjà dans la bibliothèque standard?
m0skit0
12

Une solution ci-dessus en utilisant DigestUtils n'a pas fonctionné pour moi. Dans ma version d'Apache commons (la dernière en 2013), une telle classe n'existe pas.

J'ai trouvé une autre solution ici dans un blog . Cela fonctionne parfaitement et n'a pas besoin des communs Apache. Il semble un peu plus court que le code de la réponse acceptée ci-dessus.

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

Vous aurez besoin de ces importations:

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Denis Kutlubaev
la source
Important à savoir si le hachage MD5 a une longueur de 32 caractères ou moins, merci!
Pelanes
3
Les dernières lignes peuvent probablement être simplement remplacées par: return String.Format ("% 032X", nombre); Sinon, j'aime vraiment cette réponse.
rsimp
10

Ceci est une légère variation des réponses Andranik et Den Delimarsky ci-dessus, mais c'est un peu plus concis et ne nécessite aucune logique au niveau du bit. Au lieu de cela, il utilise la String.formatméthode intégrée pour convertir les octets en chaînes hexadécimales à deux caractères (ne supprime pas les 0). Normalement, je ne ferais que commenter leurs réponses, mais je n'ai pas la réputation de le faire.

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 vous souhaitez renvoyer une chaîne en minuscules à la place, changez simplement %02Xen %02x.

Edit: En utilisant BigInteger comme avec la réponse de wzbozon, vous pouvez rendre la réponse encore plus concise:

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
la source
4

J'ai fait une simple bibliothèque à Kotlin.

Ajouter à la racine build.gradle

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

sur App build.gradle

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

Usage

À Kotlin

val ob = Hasher()

Ensuite, utilisez la méthode hash ()

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

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

Il renverra respectivement MD5 et SHA-1.

En savoir plus sur la bibliothèque

https://github.com/ihimanshurawat/Hasher

Himanshu Rawat
la source
2

Voici la version Kotlin de la réponse @Andranik. Nous devons changer getBytesen toByteArray(pas besoin d'ajouter le jeu de caractères UTF-8 car le jeu de caractères par défaut de toByteArrayest UTF-8) et transtyper le tableau [i] en entier

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
}

J'espère que ça aide

Phan Van Linh
la source
1

Dans notre application MVC, nous générons des paramètres longs

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

et même dans l'application Android (thenk aide 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
la source
1

J'ai utilisé la méthode ci-dessous pour me donner md5 en passant la chaîne pour laquelle vous voulez obtenir 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
la source
1

Veuillez utiliser SHA-512, MD5 n'est pas sécurisé

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 Jesus Arancibia Taborga
la source
0

MD5 est un peu vieux, SHA-1 est un meilleur algorithme, il y a un exemple ici .

( De plus, comme ils le notent dans cet article, Java gère cela seul, pas de code spécifique à Android. )

Adam
la source
4
Non - Je ne voulais pas d'alternatives à MD5 lorsque j'ai posé cette question en janvier 2011 (il y a 19 mois) et je ne sais pas pourquoi vous avez ressenti le besoin de répondre à ma question à ce stade.
Squonk
21
@Squonk J'ai répondu parce que c'est l'idée générale derrière stackoverflow. Peu importe combien de temps après les faits, essayez toujours d'obtenir de meilleures réponses pour les personnes qui pourraient rencontrer la question plus tard. Quant à suggérer SHA-1, il n'y avait aucun moyen pour moi de savoir que vous étiez spécifiquement contre SHA-1, mais beaucoup d'autres ne le seront pas, donc encore une fois, cela peut aider d'autres personnes à l'avenir qui rencontrent cela et les dirigent. à un algorithme plus moderne.
Adam
3
Je ne peux pas penser à une seule situation où MD5 est un mauvais choix mais SHA1 un bon choix. Si vous avez besoin d'une résistance aux collisions, vous avez besoin de SHA2 et non de SHA1. Si vous hachez les mots de passe, vous avez besoin de bcrypt ou PBKDF2. Ou dans le cas de l'OP, la solution appropriée est probablement SSL.
CodesInChaos
3
@Adam ce n'est pas une réponse c'est un commentaire.
Antzi
0

La conversion toHex () beaucoup trop inutile prévaut dans d'autres suggestions, vraiment.

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
la source
La réponse concerne un toHex "meilleur" lorsque la quête concerne MD5, en tant que telle, elle ne répond pas. Remarque: Donald Knuth Le vrai problème est que les programmeurs ont passé beaucoup trop de temps à s'inquiéter de l'efficacité aux mauvais endroits et aux mauvais moments; l'optimisation prématurée est la racine de tous les maux (ou du moins de la plupart d'entre eux) dans la programmation.
zaph
0

cela fonctionne parfaitement pour moi, je l'ai utilisé pour obtenir MD5 sur LIST Array (puis le convertir en objet JSON), mais si vous avez seulement besoin de l'appliquer sur vos données. type format, remplacez JsonObject par le vôtre.

Surtout si vous avez une incompatibilité avec l'implémentation python MD5, utilisez ceci!

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 loin
la source
-1

Les solutions fournies pour le langage Scala (un peu plus court):

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
la source