Как хешировать строку в Android?
Я разрабатываю приложение для Android и у меня есть несколько строк, которые я хотел бы зашифровать перед отправкой в базу данных. Мне нужно что-то безопасное, простое в реализации, что будет генерировать один и тот же результат каждый раз, когда передаются одни и те же данные. Желательно, чтобы результат был строкой фиксированной длины, независимо от объема передаваемой информации. Возможно, я ищу функцию хеширования.
5 ответ(ов)
Этот фрагмент кода рассчитывает MD5 хеш для заданной строки:
public String md5(String s) {
try {
// Создаем MD5 хеш
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
// Создаем шестнадцатеричную строку
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
Этот метод md5
принимает строку s
, генерирует для неё MD5 хеш и возвращает его в виде шестнадцатеричной строки. Если возникает ошибка при получении экземпляра алгоритма, она будет выведена в консоль.
Надеюсь, это будет полезно для вас!
Проблема в вашем методе md5
заключается в неверной конвертации байтов в шестнадцатеричную строку и в использовании недвойного формата вывода. Давайте разберем ваш код и сравним его с работающим примером.
В вашем неверном методе:
public static String md5(String s) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
Вы используете Integer.toHexString
, что может привести к тому, что одинокие байты (значения от 0 до 15) будут представлены в виде одной шестнадцатеричной цифры (например, 0x0, 0x1 и т.д.), а не в виде двух цифр, как требуется для корректного представления MD5, поэтому результирующая строка получается короче и неверной.
В рабочем методе:
public static final String md5(final String toEncrypt) {
try {
final MessageDigest digest = MessageDigest.getInstance("md5");
digest.update(toEncrypt.getBytes());
final byte[] bytes = digest.digest();
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(String.format("%02X", bytes[i]));
}
return sb.toString().toLowerCase();
} catch (Exception exc) {
return ""; // Impossibru!
}
}
Используется String.format("%02X", bytes[i])
, что гарантирует, что каждый байт будет представлен как две цифры в шестнадцатеричном формате (например, 0x0 будет представлено как 00). Дополнительно, toLowerCase()
делает все буквы строчными, что также является стандартом для представления строки MD5.
Чтобы исправить ваш метод, измените блок преобразования байтов в шестнадцатеричную строку так:
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
hexString.append(String.format("%02x", b));
}
Таким образом, исправленный метод будет выглядеть так:
public static String md5(String s) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
Теперь ваш метод должен генерировать корректную строку MD5 длиной 32 символа.
Ваш код предназначен для хеширования строки с использованием алгоритма MD5 и преобразования результата в шестнадцатеричную строку. Давайте разберем его шаги:
Hex-таблица: Создается массив символов
hextable
, который содержит шестнадцатеричные цифры от '0' до '9' и от 'a' до 'f'. Этот массив используется для преобразования байтов в их шестнадцатеричное представление.Метод
byteArrayToHex
: Этот метод принимает массив байтов и превращает его в шестнадцатеричную строку. Он проходит по каждому байту, делает его беззнаковым (используя побитовые операции) и получает соответствующие шестнадцатеричные символы изhextable
.int di = (array[i] + 256) & 0xFF; // Приводим байт к беззнаковому виду s = s + hextable[(di >> 4) & 0xF] + hextable[di & 0xF];
Метод
digest
: Этот метод принимает строку и алгоритм хеширования в виде строки. Он создает экземплярMessageDigest
для указанного алгоритма (например,MD5
). Если алгоритм не поддерживается, он выводит стек ошибки и возвращает исходную строку. После этого строка передается в месседж дайджест, который хешируется.m.update(s.getBytes(), 0, s.length());
Метод
md5
: Этот метод является удобным оберткой для вызова методаdigest
с конкретным алгоритмомMD5
. Он просто передает строкуs
в методdigest
и возвращает результат.
В результате, использование кода выглядит следующим образом:
String hashed = md5("your_string_here");
Таким образом, вы получите хеш MD5 вашей строки в шестнадцатеричном формате. Убедитесь, что ваш ввод корректен, и помните, что MD5 не рекомендуется для криптографических приложений из-за своих уязвимостей, но для простых задач хеширования его можно использовать.
Если вы используете решение от @Donut и работаете с символами, закодированными в UTF-8 (например, é), вам нужно использовать метод getBytes("UTF-8")
. Вот моя доработка метода digest
:
private static char[] hextable = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public static String byteArrayToHex(byte[] array) {
String s = "";
for (int i = 0; i < array.length; ++i) {
int di = (array[i] + 256) & 0xFF; // Делаем байт беззнаковым
s += hextable[(di >> 4) & 0xF] + hextable[di & 0xF];
}
return s;
}
public static String digest(String s, String algorithm) {
MessageDigest m = null;
try {
m = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return s;
}
try {
m.update(s.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
m.update(s.getBytes());
}
return byteArrayToHex(m.digest());
}
public static String md5(String s) {
return digest(s, "MD5");
}
Таким образом, при использовании этой реализации метод digest
будет корректно обрабатывать строки, содержащие UTF-8 символы. Не забывайте всегда обрабатывать возможные исключения, что и сделано в данном коде.
Ответ выше почти на 100% правильный, однако он может не обработать строки в кодировке Unicode корректно.
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
byte utf8_bytes[] = tag_xml.getBytes("UTF-8"); // Указываем явно кодировку UTF-8
digest.update(utf8_bytes, 0, utf8_bytes.length);
hash = new BigInteger(1, digest.digest()).toString(16);
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace(); // Обработка исключения для кодировки
}
Важно обратить внимание на то, что метод getBytes()
без параметров использует кодировку по умолчанию, что может вызвать проблемы с символами, попадающими в диапазон Unicode, и не всегда верно преобразовать строку в байтовый массив. Поэтому рекомендуем явно задавать кодировку, например, "UTF-8", что обеспечит правильный результат для большинства случаев.
Также настоящий вопрос касается длины байтового массива, а не строки. Как правило, для работы с длиной используется свойство length
массива байтов, как указано в коде выше.
Как сгенерировать уникальный хеш-код для строкового ввода в Android?
Использование контекста в фрагменте
Как проверить доступ к интернету на Android? InetAddress никогда не выдает таймаут
Room - Директория экспорта схемы не указана аннотационному процессору, не удается экспортировать схему
Регистрация нескольких хранилищ ключей в JVM