Java-рядок до SHA1


158

Я намагаюся зробити простий конвертер String to SHA1 на Java, і ось що у мене є ...

public static String toSHA1(byte[] convertme) {
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    } 
    return new String(md.digest(convertme));
}

Коли я передаю це toSHA1("password".getBytes()), я [�a�ɹ??�%l�3~��.знаю, що це, ймовірно, таке просте виправлення кодування, як UTF-8, але чи могла б хтось сказати мені, що я повинен зробити, щоб отримати те, що я хочу, що це 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? Або я роблю це зовсім неправильно?


Алгоритм SHA1без дефісу, не знаю, чи це змінить значення.
The Scrum Meister

Доброю практикою є getBytes()toSHA1("password".getBytes("UTF-8"))
вказівка

можливий дублікат Java обчислити sha1 рядка
Tulains Córdova

Відповіді:


183

ОНОВЛЕННЯ
Ви можете використовувати Apache Commons Codec (версія 1.7+), щоб зробити цю роботу за вас.

DigestUtils.sha1Hex (stringToConvertToSHexRepresentation)

Дякуємо @ Jon Onstott за цю пропозицію.


Стара відповідь
Перетворіть свій байтовий масив у шістнадцяткову рядок Real як розповідає, як .

return byteArrayToHexString(md.digest(convertme))

і (скопійовано з "Як це" Реала

public static String byteArrayToHexString(byte[] b) {
  String result = "";
  for (int i=0; i < b.length; i++) {
    result +=
          Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
  }
  return result;
}

До речі, ви можете отримати більш компактне представлення за допомогою Base64. Apache Commons Codec API 1.4 має цю приємну утиліту, щоб зняти всю біль. звертайтесь сюди


4
base64 і sha1 дуже різні - не пропонуйте їх як альтернативи.
Райан А.

13
@RyanA. Як я розумію, він пропонує base64 як альтернативу шестигранному кодуванню хеша SHA1 (не як альтернативу SHA1 повністю).
helmbert

Я ще не пробував цього, але ви хочете пояснити, як це працює?
Jivay

11
Чому б не використати бібліотеку на зразок DigestUtils.sha1Hex("my string")замість винаходити колесо (хоча цікаво знати, як перетворити на шістнадцятковий вручну)?
Джон Онстотт

3
Тому що, коли ця відповідь була написана, DigestUtils (1.7 вийшов у вересні 2012 року) не мала такої особливості. Дякуємо, що вказали на це. +1
Нішант

67

Це моє рішення перетворення рядка в sha1. Він добре працює в моєму додатку для Android:

private static String encryptPassword(String password)
{
    String sha1 = "";
    try
    {
        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
        crypt.reset();
        crypt.update(password.getBytes("UTF-8"));
        sha1 = byteToHex(crypt.digest());
    }
    catch(NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch(UnsupportedEncodingException e)
    {
        e.printStackTrace();
    }
    return sha1;
}

private static String byteToHex(final byte[] hash)
{
    Formatter formatter = new Formatter();
    for (byte b : hash)
    {
        formatter.format("%02x", b);
    }
    String result = formatter.toString();
    formatter.close();
    return result;
}

7
Ви можете вказати, що це java.util.Formatter і потрібен formatter.close () наприкінці, щоб уникнути попередження.
Ерік Чен

Чи не вийде, encryptPassword("test")і echo test|sha1sumв Linux-терміналі вивести один і той же результат? Вони ні.
Тулен Кордова

@ TulainsCórdova Що стосується виклику консолі: якщо ви використовуєте echo test, висновок, включаючи розрив рядка, буде прокладений до sha1sum. Якщо ви хочете зафіксувати просту рядок без розриву кінцевої лінії, можете скористатися echo -n test | sha1sum. -nПараметр дозволяє echoопустити розрив рядка.
MrSnrub

Менше питання, але загалом: Ваші encryptPassword()звуки схожі на те, що використовується для зберігання даних аутентифікації. Зауважте, що ваше кодування є вразливим до атак на словники, оскільки не застосовується висівання насіння. Перевірте ваше середовище безпеки, чи це проблема для вашої програми!
EagleRainbow


32

SHA-1 (та всі інші алгоритми хешування) повертають двійкові дані. Це означає, що (на Java) вони виробляють a byte[]. Цей byteмасив не представляє конкретних символів, це означає, що ви не можете просто перетворити його на Stringзразок, який ви робили.

Якщо вам потрібен а String, тоді ви повинні відформатувати byte[]його таким чином, щоб його можна було зобразити як String(інакше просто тримати byte[]навколо).

Два поширених способу подання довільних byte[]символів як друкованих символів - це BASE64 або прості шістнадцяткові рядки (тобто представлення кожної byteз двох шістнадцяткових цифр). Схоже, ви намагаєтеся створити шестигранну струну.

Також є ще одна помилка: якщо ви хочете отримати SHA-1 Java String, вам потрібно перетворити це Stringв byte[]перший (оскільки вхід SHA-1 також є byte[]). Якщо ви просто використовуєте, myString.getBytes()як ви показали, тоді він буде використовувати кодування платформи за замовчуванням і як така буде залежати від середовища, в якому ви її запускаєте (наприклад, він може повертати різні дані на основі налаштувань мови / локальності вашої ОС).

Краще рішення вказати кодування для використання для String-До- byte[]перетворення , як це: myString.getBytes("UTF-8"). Вибір UTF-8 (або іншого кодування, яке може представляти кожен символ Unicode) - це найбезпечніший вибір тут.


27

Це просте рішення, яке можна використовувати при перетворенні рядка в шістнадцятковий формат:

private static String encryptPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {

    MessageDigest crypt = MessageDigest.getInstance("SHA-1");
    crypt.reset();
    crypt.update(password.getBytes("UTF-8"));

    return new BigInteger(1, crypt.digest()).toString(16);
}

Попередження: Неможливо створити хеш для хешів, що починаються з "0". Ви отримаєте рядок з 39 символами.
фільм

@philn Ви можете запропонувати рішення?
Микита Кокшаров

1
Я здогадуюсь, що якщо створити велике ціле число з байту [] з достатньою кількістю ведучих нулів, ці 0 будуть втрачені. Таким чином, представлення шістнадцяткових рядків "0" там не буде, що веде до хешу з 39 або навіть меншими символами. Я використовував розчин petrnohejls вище, і він прекрасно працює ...
philn

25

Просто використовуйте бібліотеку кодеків apache commons. Вони мають клас корисності під назвою DigestUtils

Не потрібно вникати в деталі.


51
Я не погоджуюся,
вникаючи

12
Питання в тому, чи встигнете ви детально розібратися чи ні. Вся справа в тому, що зазвичай це робиться вчасно. Не кожен є студентом або має розкіш вивчити всі деталі.
DaTroop

DigestUtils повертає байтовий масив, щоб отримати рядкове представлення, яке потрібно запустити через Hex.encodeHexString. Java: це 2014 рік, і у нас досі немає методу
шагового шага

5
Один крок метод SHA-1 String result = DigestUtils.sha1Hex("An input string")
:;

18

Як згадувалося раніше, використовуйте кодек apache commons. Рекомендується також хлопцям Spring (див. DigestUtils у весняному документі). Наприклад:

DigestUtils.sha1Hex(b);

Однозначно не використовував би найкращу відповідь тут.


6

Неправильно друкується, оскільки потрібно використовувати кодування Base64. З Java 8 ви можете кодувати за допомогою класу кодерів Base64 .

public static String toSHA1(byte[] convertme) {
    md = MessageDigest.getInstance("SHA-1");
    return Base64.getEncoder().encodeToString((md.digest(convertme));
}

Результат

Це дасть вам очікуваний результат 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8


1
@Devenv це SHA-1 три крапки означає, що він буде зберігати свій початковий код, який перетворюється на sha1. Оригінальна проблема ОП була при правильному друкуванні рядка.
Едуардо Денніс

4

Дайджест повідомлень (хеш) - байт [] у байті []

Дайджест повідомлень визначається як функція, яка приймає необроблений байтовий масив і повертає необроблений масив байтів (ака byte[]). Наприклад, SHA-1 (алгоритм безпечного хешу 1) має дайджест розміром 160 біт або 20 байт. Сировинні байтові масиви зазвичай не можна інтерпретувати як кодування символів, як UTF-8 , тому що не кожен байт у кожному порядку є законним кодування. Тож перетворення їх у a за Stringдопомогою:

new String(md.digest(subject), StandardCharsets.UTF_8)

може створити кілька незаконних послідовностей або мати покажчики коду на невизначені відображення Unicode :

[�a�ɹ??�%l3~��.

Бінарне кодування тексту

Для цього використовується кодування бінарного тексту . З хешами найбільше використовується кодування HEX або Base16 . В основному байт може мати значення від 0до 255(або -128на 127підпис) , яке еквівалентно HEX поданні 0x00- 0xFF. Тому шістнадцятковий вдвічі перевищить необхідну довжину виводу, це означає, що 20-байтовий вихід створить шістнадцяткову шістнадцяткову рядок, наприклад:

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

Зауважте, що не потрібно використовувати шістнадцяткове кодування. Ви також можете використовувати щось на зразок base64 . Шестигранну часто надають перевагу, оскільки вона легше читається людиною і має визначену довжину виходу без необхідності прокладки.

Ви можете перетворити масив байтів у шістнадцятковий лише за допомогою функціональності JDK:

new BigInteger(1, token).toString(16)

Однак зауважте, що BigIntegerінтерпретувати заданий масив байтів буде як число, а не як рядок байтів. Це означає, що провідні нулі не виводяться, і результуюча рядок може бути коротшою, ніж 40 символів.

Використання бібліотек для кодування до HEX

Тепер ви можете скопіювати і вставити неперевірений байт-шістнадцятковий метод із переповнення стека або використовувати масивні залежності, такі як Guava .

Щоб мати рішення для більшості проблем, пов’язаних з байтами, я застосував утиліту для обробки таких випадків: bytes-java (Github)

Щоб перетворити масив байтів дайджестів повідомлень, ви могли просто зробити

String hex = Bytes.wrap(md.digest(subject)).encodeHex();

або ви можете просто використовувати вбудовану функцію хешу

String hex =  Bytes.from(subject).hashSha1().encodeHex();

2

База 64 Представництво SHA1хешу:

String hashedVal = Base64.getEncoder().encodeToString(DigestUtils.sha1(stringValue.getBytes(Charset.forName("UTF-8"))));

1

Причина цього не працює в тому, що під час дзвінка String(md.digest(convertme))ви говорите Java інтерпретувати послідовність зашифрованих байтів як String. Що ви хочете, це перетворити байти в шістнадцяткові символи.


0

Перетворити байтовий масив у шістнадцятковий рядок

public static String toSHA1(byte[] convertme) {
    final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    byte[] buf = md.digest(convertme);
    char[] chars = new char[2 * buf.length];
    for (int i = 0; i < buf.length; ++i) {
        chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
        chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
    }
    return new String(chars);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.