Haszowanie MD5 w systemie Android


92

Mam prostego klienta Android, który musi „rozmawiać” z prostym odbiornikiem HTTP C #. Chcę zapewnić podstawowy poziom uwierzytelniania, przekazując nazwę użytkownika / hasło w żądaniach POST.

Haszowanie MD5 jest trywialne w C # i zapewnia wystarczające zabezpieczenia dla moich potrzeb, ale nie mogę znaleźć sposobu na zrobienie tego na końcu Androida.

EDYCJA: Aby rozwiązać obawy związane ze słabością MD5 - serwer C # działa na komputerach użytkowników mojego klienta Android. W wielu przypadkach będą uzyskiwać dostęp do serwera za pomocą Wi-Fi we własnych sieciach LAN, ale na własne ryzyko mogą uzyskać do niego dostęp przez Internet. Również usługa na serwerze musi korzystać z przejścia MD5 do aplikacji innej firmy, nad którą nie mam kontroli.


6
Nie używaj MD5. Użyj SHA512.
SLaks

1
Czemu? SHA512 nie jest twardszy niż MD5. Nie chcesz tkwić za pięć lat ze starszymi klientami korzystającymi z MD5.
SLaks

2
Mam nadzieję, że używasz w swoim protokole nonce, abyś mógł odrzucić ataki typu Replay.
sarnold

1
@NickJohnson: Aby odpowiedzieć na twoje pytanie, dlaczego celowo wybrałbyś słabszą opcję? z innym pytaniem ... dlaczego miałbyś odczuwać potrzebę skomentowania pytania, które opublikowałem 16 miesięcy temu? Ale jeśli naprawdę chcesz wiedzieć (jeśli spojrzysz na komentarz do SLaksów nad twoim), był to kod etapu alfa, a koniec komputera (nie napisany przeze mnie) użył hashowania MD5. Zasadniczo wymóg dotyczył scenariusza tranzytowego bez angażowania dodatkowej złożoności. W tamtym czasie miałem około 10 testerów etapu alfa, którzy znali ryzyko. Odkąd zadałem pytanie, wprowadzono bardziej złożone zabezpieczenia.
Squonk,

1
...co? Nie, to nie tylko złe, to niebezpiecznie złe.
Nick Johnson

Odpowiedzi:


232

Oto implementacja, której możesz użyć (zaktualizowana, aby używać bardziej aktualnych konwencji Java - zamiast for:eachpętli ):StringBuilderStringBuffer

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

Chociaż nie jest to zalecane dla systemów, które wymagają nawet podstawowego poziomu bezpieczeństwa (MD5 jest uważane za zepsute i można je łatwo wykorzystać ), czasami wystarcza do podstawowych zadań.


Dzięki, to załatwi sprawę. Zobacz moją edycję, aby dowiedzieć się, dlaczego MD5 wystarczy na tym etapie.
Squonk

7
Głosuj na to IMO, aby wyświetlić bardziej poprawne odpowiedzi poniżej.
Adam

4
0 nie jest obsługiwany, zgodnie z odpowiedzią poniżej.
SohailAziz

Zaktualizowany użyć nowszych standardów Java (dla: każdego, StringBuilder)
loeschg

3
Czy istnieje system operacyjny Android, który nie ma zaimplementowanego MD5 (powoduje wyrzucanie NoSuchAlgorithmException)?
VSB

50

Zaakceptowana odpowiedź nie działa dla mnie w systemie Android 2.2. Nie wiem dlaczego, ale to „zjadało” moje zera (0). Apache commons również nie działało na Androidzie 2.2, ponieważ używa metod, które są obsługiwane tylko od Androida 2.3.x. Ponadto, jeśli chcesz tylko MD5 jako ciąg, Apache commons jest na to zbyt skomplikowane. Dlaczego należy zachować całą bibliotekę, aby używać z niej tylko małej funkcji ...

Wreszcie znalazłem tutaj następujący fragment kodu , który działał idealnie dla mnie. Mam nadzieję, że komuś się przyda ...

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
Pracował dla mnie. Zmieniłem md5.getBytes („UTF-8”) . Sprawdziłem to z: q4m'x68n6_YDB4ty8VC4 &} wqBtn ^ 68W z powyższym kodem, wynik to 0c70bb931f03b75af1591f261eb77d0b , NIE c70bb931f03b75af1591f261eb77d0b . 0 jest na miejscu
Inoy

29

Kod androidsnippets.com nie działa niezawodnie, ponieważ wydaje się, że z wynikowego skrótu zostały wycięte cyfry zerowe.

Lepsze wdrożenie jest tutaj .

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

Czy w tym przypadku „m” może mieć wartość NULL na Androidzie?
programista Androida

3
@androiddeveloper Masz całkowitą rację. Blok catch jest tutaj słabo zaimplementowany, musi istnieć instrukcja return lub wystąpi błąd odwołania zerowego po wywołaniu m.update. Nie jestem też pewien, ale chociaż może to nie usuwać zer na poszczególnych bajtach, myślę, że nadal może usuwać początkowe 0 z całego 32-znakowego skrótu. Zamiast toString (16) myślę, że String.Format ("% 032X", bigInt) działa zgodnie z przeznaczeniem. Dodatkowo możesz wybrać, czy chcesz, aby szesnastkowy zapis był pisany dużymi czy małymi literami („% 032x” dla małych).
rsimp

1
Ta wersja jest wadliwa. Jeśli MD5 zaczyna się od „0”, wygenerowana MD5 nie będzie miała początkowego 0. Nie używaj tego rozwiązania.
elcuco

21

Jeśli użycie kodeka Apache Commons jest opcją, byłaby to krótsza implementacja:

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

Lub SHA:

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

Źródło powyżej.

Kliknij link i zagłosuj na jego rozwiązanie, aby nagrodzić właściwą osobę.


Link do repozytorium Maven: https://mvnrepository.com/artifact/commons-codec/commons-codec

Obecna zależność Maven (stan na 6 lipca 2016 r.):

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

1
Czy jest jakiś powód, dla którego miałbyś używać zewnętrznej biblioteki dla interfejsu API, który już istnieje w bibliotece standardowej?
m0skit0

12

Powyższe rozwiązanie przy użyciu DigestUtils nie działa dla mnie. W mojej wersji Apache commons (najnowsza na 2013 rok) nie ma takiej klasy.

Znalazłem tutaj inne rozwiązanie na jednym blogu . Działa idealnie i nie potrzebuje wspólnych zasobów Apache. Wygląda na trochę krótszą niż kod w zaakceptowanej odpowiedzi powyżej.

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

Będziesz potrzebować tych importów:

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

Ważne, aby wiedzieć, czy hash MD5 ma długość 32 znaków lub mniej, dziękuję!
Pelanes

3
Kilka ostatnich wierszy można prawdopodobnie po prostu zastąpić przez: return String.Format ("% 032X", liczba); W przeciwnym razie bardzo podoba mi się ta odpowiedź.
rsimp

10

Jest to niewielka odmiana odpowiedzi Andranika i Dena Delimarskiego powyżej, ale jest nieco bardziej zwięzła i nie wymaga żadnej logiki bitowej. Zamiast tego używa wbudowanej String.formatmetody do konwersji bajtów na dwuznakowe ciągi szesnastkowe (nie usuwa zer). Zwykle tylko skomentowałbym ich odpowiedzi, ale nie mam reputacji, aby to zrobić.

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

Jeśli zamiast tego chcesz zwrócić ciąg z małych liter, po prostu zmień %02Xna %02x.

Edycja: używając BigInteger, podobnie jak w przypadku odpowiedzi wzbozon, możesz uczynić odpowiedź jeszcze bardziej zwięzłą:

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

Zrobiłem prostą bibliotekę w Kotlinie.

Dodaj w katalogu głównym build.gradle

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

w aplikacji build.gradle

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

Stosowanie

W Kotlinie

val ob = Hasher()

Następnie użyj metody hash ()

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

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

Zwróci odpowiednio MD5 i SHA-1.

Więcej o Bibliotece

https://github.com/ihimanshurawat/Hasher


3

Oto wersja Kotlina z odpowiedzi @Andranik. Musimy zmienić getBytesna toByteArray(nie musimy dodawać zestawu znaków UTF-8, ponieważ domyślny toByteArrayzestaw znaków to UTF-8) i rzutować tablicę [i] na liczbę całkowitą

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
}

Mam nadzieję, że to pomoże


1

W naszej aplikacji MVC generujemy dla long param

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

i to samo w aplikacji na Androida (wtedyk pomaga Andranikowi)

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

Użyłem poniższej metody, aby dać mi md5, przekazując ciąg, dla którego chcesz uzyskać 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

Użyj SHA-512, MD5 jest niezabezpieczone

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 jest trochę stary, SHA-1 to lepszy algorytm, tutaj jest przykład .

( Również, jak zauważają w tym poście, Java obsługuje to samodzielnie, bez kodu specyficznego dla Androida ).


4
Nie - nie chciałem mieć alternatywy dla MD5, kiedy zadałem to pytanie w styczniu 2011 roku (19 miesięcy temu) i nie jestem pewien, dlaczego poczułeś potrzebę odpowiedzi na moje pytanie w tym momencie.
Squonk,

21
@Squonk Odpowiedziałem, ponieważ taka jest ogólna idea stackoverflow. Bez względu na to, jak długo po fakcie, zawsze staraj się uzyskać lepsze odpowiedzi dla osób, które mogą później zetknąć się z pytaniem. Jeśli chodzi o sugerowanie SHA-1, nie było sposobu, abym wiedział, że byłeś konkretnie przeciwko SHA-1, ale wielu innych nie będzie, więc znowu może to pomóc innym ludziom w przyszłości, którzy się z tym spotkają i pokierować nimi do bardziej nowoczesnego algorytmu.
Adam

3
Nie przychodzi mi do głowy ani jedna sytuacja, w której MD5 jest złym wyborem, a SHA1 dobrym. Jeśli potrzebujesz odporności na kolizje, potrzebujesz SHA2, a nie SHA1. Jeśli haszujesz hasła, potrzebujesz bcrypt lub PBKDF2. Lub w przypadku OP prawdopodobnie właściwym rozwiązaniem jest SSL.
CodesInChaos

3
@Adam to nie jest odpowiedź to jest komentarz.
Antzi

0

Tak naprawdę w innych sugestiach przeważa zbyt marnotrawna konwersja metody toHex ().

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

Odpowiedź dotyczy „lepszego” szesnastkowania, gdy zadanie dotyczy MD5, jako takie nie odpowiada. Uwaga: Donald Knuth Prawdziwym problemem jest to, że programiści spędzili zbyt wiele czasu na martwieniu się o wydajność w niewłaściwych miejscach i w niewłaściwych momentach; przedwczesna optymalizacja jest źródłem wszelkiego zła (a przynajmniej jego większości) w programowaniu.
zaph

0

to działa idealnie dla mnie, użyłem tego, aby uzyskać MD5 na LIST Array (następnie przekonwertować go na obiekt JSON), ale jeśli potrzebujesz go tylko zastosować do swoich danych. wpisz format, zamień JsonObject na swój.

Zwłaszcza jeśli masz niezgodność z implementacją Pythona MD5, użyj tego!

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;


}

0

Przydatny przykład funkcji rozszerzenia Kotlin

fun String.toMD5(): String {
    val bytes = MessageDigest.getInstance("MD5").digest(this.toByteArray())
    return bytes.toHex()
}

fun ByteArray.toHex(): String {
    return joinToString("") { "%02x".format(it) }
}

-1

Dostarczone rozwiązania dla języka Scala (trochę krótsze):

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
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.