Хеширование MD5 в Android


92

У меня есть простой клиент Android, которому нужно «разговаривать» с простым слушателем HTTP C #. Я хочу обеспечить базовый уровень аутентификации, передав имя пользователя / пароль в запросах POST.

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

РЕДАКТИРОВАТЬ: Просто чтобы устранить озабоченность по поводу слабости MD5 - сервер C # работает на ПК пользователей моего клиента Android. Во многих случаях они будут получать доступ к серверу с помощью Wi-Fi в своих собственных локальных сетях, но на свой страх и риск они могут выбрать доступ к нему из Интернета. Также служба на сервере должна использовать сквозную передачу MD5 для стороннего приложения, которое я не контролирую.


6
Не используйте MD5. Используйте SHA512.
SLaks

1
Зачем? SHA512 не сложнее MD5. Вы же не хотите через пять лет зацикливаться на устаревших клиентах, использующих MD5.
SLaks

2
Я надеюсь, что вы используете одноразовый номер в своем протоколе, поэтому вы можете отбросить атаки повторного воспроизведения.
sarnold

1
@NickJohnson: Чтобы ответить на ваш вопрос, почему вы намеренно выбрали более слабый вариант? с другим вопросом ... почему вы чувствуете необходимость комментировать вопрос, который я разместил 16 месяцев назад? Но если вы действительно хотите знать (если вы посмотрите комментарий к SLaks выше вашего), это был код альфа-стадии, а компьютерная часть (написанная не мной) использовала хеширование MD5. В основном это требовалось для сквозного сценария без дополнительных сложностей. В то время у меня было около 10 тестировщиков альфа-стадии, которые знали о рисках. С тех пор, как я задал вопрос, была включена более сложная безопасность.
Squonk

1
...какие? Нет, это не просто неправильно, это опасно неправильно.
Ник Джонсон

Ответы:


232

Вот реализация, которую вы можете использовать (обновленная для использования более современных соглашений Java - for:eachцикл StringBuilderвместо 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 "";
}

Хотя это не рекомендуется для систем, которые включают даже базовый уровень безопасности (MD5 считается сломанным и может быть легко взломан ), иногда его достаточно для основных задач.


Спасибо, это поможет. См. Мою правку, почему на данном этапе будет достаточно MD5.
Squonk

7
Проголосуйте за это ИМО, чтобы ниже были представлены более правильные ответы.
Адам

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 на месте
Иной

29

Код 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 в отдельных байтах, я думаю, что он все равно может удалить начальные 0 на всем хэше 32 символов. Вместо toString (16) я думаю, что String.Format ("% 032X", bigInt) работает по назначению. Кроме того, вы можете выбрать, хотите ли вы использовать шестнадцатеричный регистр в верхнем или нижнем регистре («% 032x» для нижнего).
rsimp

1
Эта версия ошибочна. Если ваш MD5 начинается с «0», сгенерированный MD5 не будет иметь ведущего 0. Пожалуйста, не используйте это решение.
elcuco 07

21

Если возможно использование кодека 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 commons (последней на 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;

Важно знать, что id, хеш MD5 имеет длину 32 символа или меньше, спасибо!
Pelanes

3
Последние несколько строк, вероятно, можно просто заменить на: return String.Format ("% 032X", number); В остальном мне очень нравится этот ответ.
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

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

Добавьте в корень 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


3

Вот версия Котлина из ответа @Andranik. Нам нужно изменить getBytesна toByteArray(не нужно добавлять кодировку UTF-8, потому что кодировка по умолчанию toByteArray- UTF-8) и преобразовать array [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 (затемк помогает Андранику)

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 месяцев назад), и я не уверен, почему вы почувствовали необходимость ответить на мой вопрос именно здесь.
Squonk

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

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

3
@ Адам, это не ответ, это комментарий.
Antzi

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 (затем преобразовать его в объект 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;


}

0

Пример полезной функции расширения 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

Предоставленные решения для языка 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.