Зшивання струни з Sha256


141

Я намагаюся хеш-рядок використовувати SHA256, я використовую наступний код:

using System;
using System.Security.Cryptography;
using System.Text;
 public class Hash
    {
    public static string getHashSha256(string text)
    {
        byte[] bytes = Encoding.Unicode.GetBytes(text);
        SHA256Managed hashstring = new SHA256Managed();
        byte[] hash = hashstring.ComputeHash(bytes);
        string hashString = string.Empty;
        foreach (byte x in hash)
        {
            hashString += String.Format("{0:x2}", x);
        }
        return hashString;
    }
}

Однак цей код дає значно інші результати порівняно з моїми друзями php, а також онлайн-генераторами (такими як Цей генератор )

Хтось знає, в чому помилка? Різні основи?


17
Поза темою, але майте на увазі, що створення StringBuilder та використання AppendFormat замість String.Format у циклі foreach не дасть вашому коду зайвого створення безлічі рядкових об’єктів.
Марсель Ламоте

Відповіді:


155

Encoding.Unicodeє оманливою назвою Microsoft для UTF-16 (подвійне широке кодування, яке використовується в світі Windows з історичних причин, але не використовується ніхто іншим). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx

Якщо ви оглянете свій bytesмасив, ви побачите, що кожен другий байт є 0x00(через подвійне широке кодування).

Ви повинні використовувати це Encoding.UTF8.GetBytesзамість цього.

Крім того, ви побачите різні результати в залежності від того, чи вважаєте ви закінчуючий '\0'байт частиною даних, які ви хешуєте. Хешування двох байтів "Hi"дасть інший результат від хешування трьох байтів "Hi". Вам доведеться вирішити, що ви хочете зробити. (Імовірно, ви хочете зробити те, що робить PHP-код вашого друга.)

Для тексту ASCII, Encoding.UTF8безумовно, підійде. Якщо ви прагнете до ідеальної сумісності з кодом вашого друга, навіть на НЕ-ASCII входів, краще спробувати кілька тестових випадків з не-ASCII символи , такі як éі побачити ваші результати по- , як і раніше збігаються. Якщо ні, то вам доведеться з’ясувати, що кодує ваш друг справді; це може бути одна з 8-бітних "кодових сторінок", яка раніше була популярною до винаходу Unicode. (Знову ж таки, я думаю, що Windows є основною причиною того, що комусь все одно потрібно хвилюватися про "кодові сторінки".)


3
@Elmue, вам може бути приємно дізнатися, що "сортування за кодованими байтами UTF8" та "сортування за кодовими точками Unicode" рівнозначні! (Як і "сортування за кодуванням UTF16 shorts", але не "сортування за кодованими UTF16 байтами", якщо ви не перебуваєте в системі з великим ендіаном, що не в Windows.) Однак "сортування" в Unicode насправді є складна тема, яку слід зберегти на інший день.
Quuxplusone

2
@Elmue не будь так впевнений у своїх неправильних відповідях. Спробуй; ти здивуєшся. Чи буде сюрприз приємним чи неприємним, залежить тільки від вас. :)
Quuxplusone

2
@Elmue, " Що робити, якщо ви хочете зробити порівняння нечутливого до випадків? "Вам також потрібно перетворити байти в UTF-16, якщо ви хочете робити такі речі. Те, що фіксована довжина, не допомагає жодному шматочку.
Артуро Торрес Санчес

2
"Не використовується ніхто іншим" є досить цікавим твердженням, оскільки Java внутрішньо обробляє рядки як UTF-16 також ...
Самі Кухмонен

4
@Elmue "Ваші коментарі невірні: UTF16 - це Unicode." Ви неправі. "Unicode" - це стандарт, який присвоює числам (кодові точки) гліфам. За винятком сурогатних пар, він не визначає, як представити ці числа як байти. UTF16 визначає кодові точки <--> байт. Unicode вказує гліфи <--> кодові точки.
antiduh

103

У мене також була ця проблема з іншим стилем реалізації, але я забув, де я її взяв, оскільки це було 2 роки тому.

static string sha256(string randomString)
{
    var crypt = new SHA256Managed();
    string hash = String.Empty;
    byte[] crypto = crypt.ComputeHash(Encoding.ASCII.GetBytes(randomString));
    foreach (byte theByte in crypto)
    {
        hash += theByte.ToString("x2");
    }
    return hash;
}

Коли я ввожу щось подібне abcdefghi2013з якихось причин, це дає різні результати і призводить до помилок у моєму модулі входу. Потім я спробував змінити код так само, як запропонував Quuxplusone, і змінив кодування з того часу, ASCIIщоб UTF8він остаточно спрацював!

static string sha256(string randomString)
{
    var crypt = new System.Security.Cryptography.SHA256Managed();
    var hash = new System.Text.StringBuilder();
    byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
    foreach (byte theByte in crypto)
    {
        hash.Append(theByte.ToString("x2"));
    }
    return hash.ToString();
}

Ще раз дякую Quuxplusone за чудову і детальну відповідь! :)


ваше рішення працювало на мене. але у мене інший випадок. це з sha512, і рядок коду, який вирішив мою проблему, - у hash += bit.ToString("x2");мене тут питання: я використовував Convert.ToBase64String(byte[] encryptedBytes)для перетворення назад з байтів у рядок. це дало мені інший результат. так чим відрізняється ці два методи перетворення з байтів у рядок ..?
Кеваль Лангалія

Чи можливо тут скористатися якоюсь настройкою (наприклад, моїм власним вектором ініціалізації) або додається / додається лише варіант випадкового рядка?
FrenkyB

Я не дуже впевнений, що ти маєш на увазі. Це просто дуже проста функція хешування, і ви завжди можете її додавати / налаштовувати, як завгодно. Додаючи / додаючи випадковий рядок, ви маєте на увазі засолювання? Ну, це один хороший спосіб налаштувати його для подальшої безпеки.
Ніко Дамдум

Не рекомендується використовувати просто хешування SHA без фактора роботи для зберігання паролів. Іншими словами, процес хешування пароля повинен бути значно повільним, щоб хакери швидко не здогадувалися. Використовуйте Bcrypt або Scrypt для кращої безпеки.
Ton Snoei

@TonSnoei Так, це правда. Однак це старий код із старовинного додатка для внутрішньої системи ще в коледжі, який ніхто більше не використовує, і я дійсно не рекомендував би це сам. Більше того, цей потік стосується конкретно кодування SHA256, а не безпосередньо паролів. Хоча я б не проти редагувати його, щоб видалити посилання на паролі, якщо це лоскотить ваші фантазії.
Ніко Думдум

6
public static string ComputeSHA256Hash(string text)
{
    using (var sha256 = new SHA256Managed())
    {
        return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
    }                
}

Причина, чому ви отримуєте різні результати, полягає в тому, що ви не використовуєте одне і те ж кодування рядків. Посилання, яке ви розмістили для он-лайн веб-сайту, який обчислює SHA256, використовує кодування UTF8, тоді як у вашому прикладі ви використовували кодування Unicode. Це дві різні кодування, тому ви не отримаєте однакового результату. З наведеним вище прикладом ви отримуєте той самий хеш SHA256 пов’язаного веб-сайту. Вам потрібно використовувати те саме кодування також у PHP.

Абсолютний мінімум кожен розробник програмного забезпечення абсолютно, позитивно повинен знати про набори Unicode та символів (без виправдань!)

https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absgether-positively-must-know-about-unicode-and-character-sets-no-excuses/


4

У версії PHP ви можете надіслати "true" в останньому параметрі, але за замовчуванням "false". Наступний алгоритм еквівалентний хеш-функції PHP за замовчуванням при передачі "sha256" в якості першого параметра:

public static string GetSha256FromString(string strData)
    {
        var message = Encoding.ASCII.GetBytes(strData);
        SHA256Managed hashString = new SHA256Managed();
        string hex = "";

        var hashValue = hashString.ComputeHash(message);
        foreach (byte x in hashValue)
        {
            hex += String.Format("{0:x2}", x);
        }
        return hex;
    }

4
Я б не використовував ASCIIі робив byte[] arrBytes = System.Text.Encoding.UTF8.GetBytes(strData)натомість.
c00000fd

3
public string EncryptPassword(string password, string saltorusername)
        {
            using (var sha256 = SHA256.Create())
            {
                var saltedPassword = string.Format("{0}{1}", salt, password);
                byte[] saltedPasswordAsBytes = Encoding.UTF8.GetBytes(saltedPassword);
                return Convert.ToBase64String(sha256.ComputeHash(saltedPasswordAsBytes));
            }
        }

1
мені подобається те, що ти додав трохи солі ^^
Фабіан

1

Найкоротший і найшвидший спосіб. Лише 1 рядок!

public static string StringSha256Hash(string text) =>
    string.IsNullOrEmpty(text) ? string.Empty : BitConverter.ToString(new System.Security.Cryptography.SHA256Managed().ComputeHash(System.Text.Encoding.UTF8.GetBytes(text))).Replace("-", string.Empty);

-2

Ця робота для мене в .NET Core 3.1.
Але не в .NET 5 попередній перегляд 7.

using System;
using System.Security.Cryptography;
using System.Text;

namespace PortalAplicaciones.Shared.Models
{
    public class Encriptar
    {
        public static string EncriptaPassWord(string Password)
        {
            try
            {
                SHA256Managed hasher = new SHA256Managed();

                byte[] pwdBytes = new UTF8Encoding().GetBytes(Password);
                byte[] keyBytes = hasher.ComputeHash(pwdBytes);

                hasher.Dispose();
                return Convert.ToBase64String(keyBytes);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }  
    }
}
 

1
Ласкаво просимо до StackOverflow. Будь ласка, надайте пояснення, на вашу думку, запропоноване рішення може вирішити проблему ОП.
Пітер Цала

1
Будь ласка, намагайтеся уникати повторного викиду винятку. З цим ви втрачаєте оригінальний StackTrace.
Пітер Цала
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.