Хешування MD5 в Android


91

У мене є простий андроїд-клієнт, якому потрібно "поговорити" з простим слухачем C # HTTP. Я хочу надати базовий рівень автентифікації, передавши ім'я користувача / пароль у запитах POST.

Хешування MD5 є тривіальним у C # і забезпечує достатній захист для моїх потреб, але я, здається, не можу знайти, як це зробити в кінці Android.

EDIT: Лише для вирішення проблем, викликаних слабкістю MD5 - сервер C # працює на ПК користувачів мого андроїд-клієнта. У багатьох випадках вони отримуватимуть доступ до сервера за допомогою Wi-Fi у власних локальних мережах, але на власний ризик вони можуть обрати доступ до нього з Інтернету. Крім того, служба на сервері повинна використовувати передачу для MD5 сторонній програмі, над якою я не маю контролю.


6
Не використовуйте MD5. Використовуйте SHA512.
Слакс

1
Чому? SHA512 не складніше, ніж MD5. Ви не хочете, щоб через п’ять років застрягли у застарілих клієнтах, що використовують MD5.
Слакс

2
Сподіваюся, ви використовуєте nonce у своєму протоколі, щоб ви могли відкидати атаки відтворення.
sarnold

1
@NickJohnson: Щоб відповісти на ваше запитання, чому б ви навмисно вибрали слабший варіант? з іншим запитанням ... чому б ви відчували потребу коментувати питання, яке я розмістив 16 місяців тому? Але якщо ви дійсно хочете знати (якщо ви подивитесь на коментар до SLaks вище вашого), це був алфавітний код етапу, а кінець ПК (не я написав) використовував хешування MD5. Вимога, в основному, полягала в прохідному сценарії без залучення особливої ​​складності. У той час у мене було близько 10 тестерів альфа-фази, які знали про ризики. Більш складна безпека була включена з моменту мого запитання.
Squonk

1
...що? Ні, це не просто неправильно, це небезпечно неправильно.
Нік Джонсон,

Відповіді:


231

Ось реалізація, яку ви можете використовувати (оновлена ​​для використання більш сучасних конвенцій Java - замість for:eachциклу ):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 "";
}

Незважаючи на те, що це не рекомендується для систем, які передбачають навіть базовий рівень безпеки (MD5 вважається зламаним і може бути легко використаний ), іноді цього достатньо для базових завдань.


Спасибі, що зробить трюк. Перегляньте мою редакцію, чому на цьому етапі буде достатньо MD5.
Squonk

6
Голосуйте за IMO, щоб з’явилися більш правильні відповіді нижче.
Адам

4
0 не обслуговується, як відповіли нижче.
SohailAziz

Оновлено для використання новіших стандартів Java (для: кожен, StringBuilder)
loeschg,

3
Чи існує якась операційна система Android, у якій не було впроваджено MD5 (спричинити викидання NoSuchAlgorithmException)?
VSB

50

Прийнята відповідь не працювала для мене в Android 2.2. Не знаю чому, але це "з'їдало" деякі мої нулі (0). Apache commons також не працював на Android 2.2, оскільки він використовує методи, які підтримуються лише з Android 2.3.x. Крім того, якщо ви хочете просто MD5 рядок, Apache commons занадто складний для цього. Чому слід тримати цілу бібліотеку, щоб використовувати лише невелику функцію з неї ...

Нарешті -то я знайшов наступний фрагмент коду тут , який працював відмінно для мене. Сподіваюся, це комусь стане в нагоді ...

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
Працював у мене. Я змінив md5.getBytes ("UTF-8") . Я перевірив це за допомогою: q4m'x68n6_YDB4ty8VC4 &} wqBtn ^ 68W з наведеним вище кодом, результат 0c70bb931f03b75af1591f261eb77d0b , НЕ c70bb931f03b75af1591f261eb77d0b . 0 на місці
Іной

28

Код androidsnippets.com не працює надійно, оскільки 0 здається вирізаним з отриманого хешу.

Краще реалізація тут .

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" коли-небудь бути нульовим на Android, у цьому випадку?
розробник android

3
@androiddeveloper Ви абсолютно праві. Блок catch тут погано реалізований, повинен бути оператор return, або при виклику m.update буде помилка нульового посилання. Також я не впевнений, але, хоча це може не позбавляти нулі на окремих байтах, я думаю, що це все одно може позбавити провідних 0 на всьому 32 хеш-символі. Замість toString (16), я думаю, String.Format ("% 032X", bigInt) працює за призначенням. Крім того, ви можете вибрати, чи потрібно використовувати шістнадцяткові символи у верхньому чи нижньому регістрі ("% 032x" у нижньому регістрі).
rsimp

1
Ця версія недолікова. Якщо ваш MD5 починається з "0", згенерований MD5 не матиме провідних 0. Будь ласка, не використовуйте це рішення.
elcuco

20

Якщо використання кодека Apache Commons є варіантом, то це було б більш коротким впровадженням:

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

Або SHA:

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

Джерело для вище.

Будь ласка, перейдіть за посиланням і проголосуйте його рішення, щоб нагородити правильну особу.


Посилання на репо Maven: https://mvnrepository.com/artifact/commons-codec/commons-codec

Поточна залежність Maven (станом на 6 липня 2016 року):

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

1
Будь-яка причина, чому б ви використовували зовнішню бібліотеку для API, який вже існує у стандартній бібліотеці?
m0skit0

12

Рішення вище з використанням DigestUtils для мене не спрацювало. У моїй версії спільних версій Apache (останньої - 2013 р.) Такого класу немає.

Я знайшов інше рішення тут, в одному блозі . Він працює ідеально і не потребує спільних версій Apache. Це виглядає трохи коротшим за код у прийнятій відповіді вище.

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

Вам знадобляться такі імпорти:

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

Важливо знати ідентифікатор хеша MD5 довжиною не більше 32 символів, дякую!
Пеланес

3
Останні кілька рядків, можливо, можна просто замінити на: return String.Format ("% 032X", число); Інакше мені дуже подобається ця відповідь.
rsimp

10

Це невелика варіація відповідей Андраніка та Ден Делімарського вище, але вона дещо лаконічніша і не вимагає жодної побітової логіки. Натомість він використовує вбудований String.formatметод для перетворення байтів у два символьні шістнадцяткові рядки (не позбавляє 0). Зазвичай я просто коментував би їхні відповіді, але у мене немає такої репутації.

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

Якщо ви хочете замість цього повернути рядок з малої літери, просто змініть %02Xна %02x.

Редагувати: Використовуючи BigInteger, як у відповіді wzbozon, ви можете зробити відповідь ще більш стислою:

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

Я створив просту бібліотеку в Котліні.

Додайте на Root build.gradle

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

в App build.gradle

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

Використання

У Котліні

val ob = Hasher()

Потім використовуйте метод hash ()

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

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

Він поверне MD5 і SHA-1 відповідно.

Більше про бібліотеку

https://github.com/ihimanshurawat/Hasher


2

Ось версія Котліна від відповіді @Andranik. Нам потрібно змінити getBytesна toByteArray(не потрібно додавати набір символів UTF-8, тому що за замовчуванням набір символів toByteArrayUTF-8) і передати масив [i] на ціле число

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
}

Сподіваюся, це допоможе


1

У нашому додатку MVC ми створюємо довгі параметри

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

і те саме в додатку для Android (thenk допомагає Андраніку)

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

я використав наведений нижче спосіб, щоб надати мені md5, передавши рядок, для якого ви хочете отримати 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

Будь ласка, використовуйте SHA-512, MD5 є небезпечним

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 трохи старий, SHA-1 - кращий алгоритм, тут є приклад .

( Також, як вони зазначають у цьому дописі, Java обробляє це самостійно, без коду Android. )


4
Ні - я не хотів альтернативи MD5, коли задавав це запитання у січні 2011 року (19 місяців тому), і я не впевнений, чому ви відчули потребу відповісти на моє запитання на цьому етапі.
Сквонк

21
@Squonk Я відповів, тому що це загальна ідея stackoverflow. Незалежно від того, скільки часу після цього станеться, завжди намагайтеся отримати кращі відповіді для людей, які можуть зіткнутися з цим питанням згодом. Що стосується припущення SHA-1, то я не міг знати, що ви спеціально проти SHA-1, але багато інших цього не буде, тому знову ж таки, це може допомогти іншим людям, які зіткнуться з цим, і направити їх до більш сучасного алгоритму.
Адам

3
Я не можу подумати жодної ситуації, коли MD5 - це поганий вибір, але SHA1 - хороший. Якщо вам потрібна стійкість до зіткнення, вам потрібен SHA2, а не SHA1. Якщо у вас є хеш-паролі, вам потрібно bcrypt або PBKDF2. Або у випадку OP правильним рішенням є, мабуть, SSL.
CodesInChaos

3
@Adam це не відповідь, це коментар.
Анці

0

Насправді занадто марнотратне перетворення 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);
}

Відповідь полягає в "кращому" toHex, коли квест стосується MD5, тому він не реагує. Примітка: Дональд Кнут Справжня проблема полягає в тому, що програмісти витрачають занадто багато часу, турбуючись про ефективність в неналежних місцях і в невідповідний час; передчасна оптимізація - корінь усього зла (або принаймні більшої його частини) у програмуванні.
zaph

0

це ідеально працює для мене, я використав це, щоб отримати MD5 на LIST Array (потім перетворити його в об'єкт JSON), але якщо вам потрібно лише застосувати його до своїх даних. формат, замініть JsonObject на ваш.

Особливо, якщо у вас є невідповідність реалізації python MD5, використовуйте це!

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

Надані рішення для мови Scala (трохи коротші):

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
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.