Хеширование строки с помощью Sha256

146

Я пытаюсь хешировать строку с помощью 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 моих друзей, а также с онлайн-генераторами (такими как This generator )

Кто-нибудь знает, в чем ошибка? Разные базы?

Наттфростен
источник
18
Не по теме, но имейте в виду, что создание StringBuilder и использование AppendFormat вместо String.Format в вашем цикле foreach предотвратит ненужное создание в вашем коде большого количества строковых объектов.
Марсель Ламот

Ответы:

157

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

Quuxplusone
источник
3
@Elmue, вам может быть приятно узнать, что «сортировка по байтам в кодировке UTF8» и «сортировка по кодовым точкам Unicode» эквивалентны! (Как и «сортировка по кодировке UTF16 short», но не «сортировка по байтам в кодировке UTF16», если только вы не используете систему с прямым порядком байтов, которой нет в Windows.) Однако «сортировка» в Юникоде действительно сложная тема, которую стоит отложить на другой день.
Quuxplusone
2
@Elmue, не будь настолько уверенным в своих неправильных ответах. Попробуйте сами; вы будете удивлены. Вам решать, будет ли сюрприз приятным или неприятным. :)
Quuxplusone 03
2
@Elmue, « Что если вы хотите провести сравнение без учета регистра? «Вам также необходимо преобразовать байты в UTF-16, если вы хотите делать такие вещи. Тот факт, что это фиксированная длина, ни на что не помогает.
Артуро Торрес Санчес
2
«Не используется никем другим» - довольно интересное утверждение, поскольку Java внутренне обрабатывает строки также как UTF-16 ...
Сами Кухмонен
4
@Elmue "Ваши комментарии неверны: UTF16 - это Unicode." Вы неправы. «Юникод» - это стандарт, который присваивает глифам числа (кодовые точки). За исключением суррогатных пар, в нем не указано, как представлять эти числа в байтах. UTF16 определяет кодовые точки <--> байтов. Unicode определяет символы <--> кодовых точек.
antiduh
107

У меня также была эта проблема с другим стилем реализации, но я забыл, где я ее взял, так как это было 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-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/

Авто
источник
5
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));
            }
        }
ARC
источник
1
Мне нравится, что вы добавили немного соли ^^
Фабиан
1
Это самый лаконичный ответ.
Брендан Слук
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
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 preview 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. Пожалуйста, объясните, почему, по вашему мнению, предложенное вами решение может решить проблему OP.
Питер Чала,
1
Пожалуйста, постарайтесь избежать повторной генерации исключения. При этом вы теряете исходный StackTrace.
Питер Чала,