Haszowanie łańcucha za pomocą Sha256

141

Próbuję haszować ciąg przy użyciu SHA256, używam następującego kodu:

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;
    }
}

Jednak ten kod daje znacznie inne wyniki w porównaniu do php moich znajomych, a także generatorów online (takich jak Ten generator )

Czy ktoś wie na czym polega błąd? Różne bazy?

Nattfrosten
źródło
17
Poza tematem, ale pamiętaj, że utworzenie StringBuilder i użycie AppendFormat zamiast String.Format w pętli foreach zapobiegnie niepotrzebnemu tworzeniu wielu obiektów ciągu.
Marcel Lamothe

Odpowiedzi:

155

Encoding.Unicodeto myląca nazwa Microsoft dla UTF-16 (kodowanie o podwójnej szerokości, używane w świecie Windows ze względów historycznych, ale nie używane przez nikogo innego). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx

Jeśli sprawdzisz swoją bytestablicę, zobaczysz, że co drugi bajt to 0x00(z powodu kodowania o podwójnej szerokości).

Zamiast tego powinieneś używać Encoding.UTF8.GetBytes.

Ale również zobaczysz różne wyniki w zależności od tego, czy uważasz, że '\0'bajt kończący jest częścią haszowanych danych. Haszowanie dwóch bajtów "Hi"da inny wynik mieszania trzech bajtów "Hi". Musisz zdecydować, co chcesz zrobić. (Prawdopodobnie chcesz zrobić to, co robi kod PHP twojego przyjaciela).

Dla tekstu ASCII na Encoding.UTF8pewno będzie odpowiedni. Jeśli dążysz do idealnej zgodności z kodem przyjaciela, nawet na wejściach spoza ASCII, lepiej wypróbuj kilka przypadków testowych ze znakami spoza ASCII, takimi jak éi, i sprawdź, czy wyniki nadal pasują. Jeśli nie, musisz dowiedzieć się, jakiego kodowania naprawdę używa twój przyjaciel; może to być jedna z 8-bitowych „stron kodowych”, które były popularne przed wynalezieniem Unicode. (Ponownie, myślę, że Windows jest głównym powodem, dla którego każdy musi się martwić o „strony kodowe”).

Quuxplusone
źródło
3
@Elmue, możesz być zadowolony, gdy dowiesz się, że „sortowanie według bajtów zakodowanych w UTF8” i „sortowanie według punktów kodowych Unicode” są równoważne! (Podobnie jak „sortowanie według shorts zakodowanych w UTF16 ”, ale nie „sortowanie według bajtów zakodowanych w UTF16”, chyba że jesteś w systemie big-endian, którym nie jest Windows.) Jednak „sortowanie” w Unicode jest tak naprawdę skomplikowany temat, który warto zachować na inny dzień.
Quuxplusone
2
@Elmue nie bądź taki pewny swoich błędnych odpowiedzi. Wypróbuj to; będziesz zaskoczony. To, czy niespodzianka będzie przyjemna, czy nieprzyjemna, zależy wyłącznie od Ciebie. :)
Quuxplusone
2
@Elmue, „ A co, jeśli chcesz dokonać porównania bez uwzględniania wielkości liter? „Musisz także konwertować bajty w UTF-16, jeśli chcesz robić tego rodzaju rzeczy. Fakt, że ma stałą długość, nie pomaga ani trochę.
Arturo Torres Sánchez
2
Nieużywany
4
@Elmue "Twoje komentarze są niepoprawne: UTF16 to Unicode." Mylisz się. „Unicode” to standard przypisujący liczby (punkty kodowe) do glifów. Z wyjątkiem par zastępczych, nie określa, jak reprezentować te liczby jako bajty. UTF16 określa punkty kodowe <--> bajtów. Unicode określa glify <--> punkty kodowe.
antiduh
103

Miałem też ten problem z innym stylem realizacji, ale zapomniałem skąd go mam, bo to było 2 lata temu.

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;
}

Kiedy abcdefghi2013z jakiegoś powodu wprowadzam coś podobnego , daje to inne wyniki i powoduje błędy w moim module logowania. Potem spróbowałem zmodyfikować kod w taki sam sposób, jak sugerował Quuxplusone i zmieniłem kodowanie od ASCIIdo UTF8wtedy wreszcie zadziałało!

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();
}

Jeszcze raz dziękuję Quuxplusone za wspaniałą i szczegółową odpowiedź! :)

Nico Dumdum
źródło
Twoje rozwiązanie zadziałało dla mnie. ale mam inny przypadek. jest z sha512 i linią kodu, która rozwiązała mój problem hash += bit.ToString("x2");, mam tutaj pytanie: użyłem Convert.ToBase64String(byte[] encryptedBytes)do konwersji z powrotem z bajtów na ciąg. to dawało mi inny wynik. więc jaka jest różnica między tymi dwiema metodami konwersji z bajtów na ciąg znaków ..?
Keval Langalia
Czy w tym miejscu można zastosować jakieś dostosowanie (na przykład mój własny wektor inicjujący), czy też dołącza / poprzedza tylko losowy ciąg?
FrenkyB
Nie bardzo wiem, co masz na myśli. To tylko bardzo prosta funkcja mieszająca i zawsze możesz ją dodać / dostosować w dowolny sposób. Dodając / poprzedzając losowy ciąg, masz na myśli solenie? Cóż, to dobry sposób na dostosowanie go dla większego bezpieczeństwa.
Nico Dumdum
Nie zaleca się używania samego skrótu SHA bez czynnika roboczego do przechowywania haseł. Innymi słowy, proces haszowania hasła musi być znacznie powolny, aby hakerzy nie zgadywali go szybko. Użyj Bcrypt lub Scrypt dla większego bezpieczeństwa.
Ton Snoei
@TonSnoei Tak, to prawda. Jest to jednak stary kod z jakiejś starożytnej aplikacji systemu wewnętrznego z czasów studiów, którego nikt już nie używa i naprawdę nie polecałbym tego samemu. Co więcej, ten wątek dotyczy w szczególności kodowania SHA256, a nie bezpośrednio haseł. Chociaż nie miałbym nic przeciwko edycji, aby usunąć odniesienia do haseł, jeśli to łaskocze twoją fantazję.
Nico Dumdum
6
public static string ComputeSHA256Hash(string text)
{
    using (var sha256 = new SHA256Managed())
    {
        return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
    }                
}

Przyczyną różnych wyników jest to, że nie używasz tego samego kodowania ciągów. Łącze, które umieściłeś do witryny internetowej, która oblicza algorytm SHA256, używa kodowania UTF8, podczas gdy w Twoim przykładzie zastosowano kodowanie Unicode. Są to dwa różne kodowania, więc nie uzyskasz tego samego wyniku. W powyższym przykładzie otrzymujesz ten sam skrót SHA256 z połączonej witryny internetowej. Musisz użyć tego samego kodowania również w PHP.

Absolutne minimum każdy programista Absolutnie, pozytywnie musi wiedzieć o Unicode i zestawach znaków (bez wymówek!)

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

Automatyczny
źródło
4

W wersji PHP można wysłać „true” w ostatnim parametrze, ale domyślną wartością jest „false”. Poniższy algorytm jest odpowiednikiem domyślnej funkcji haszującej PHP podczas przekazywania „sha256” jako pierwszego parametru:

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;
    }
Rachel
źródło
4
Nie używałbym ASCIIi byte[] arrBytes = System.Text.Encoding.UTF8.GetBytes(strData)zamiast tego robiłbym.
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));
            }
        }
ŁUK
źródło
1
Podoba mi się fakt, że dodałeś trochę soli ^^
Fabian
1

Najkrótsza i najszybsza droga w historii. Tylko 1 linia!

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);
Erçin Dedeoğlu
źródło
-2

Ta praca dla mnie w .NET Core 3.1.
Ale nie w .NET 5 w wersji zapoznawczej 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);
            }
        }  
    }
}
 
Alberto T. Payero Mota
źródło
1
Witamy w StackOverflow. Proszę podać wyjaśnienie, dlaczego Twoim zdaniem proponowane rozwiązanie może rozwiązać problem PO.
Peter Csala
1
Spróbuj uniknąć ponownego zgłaszania wyjątku. W ten sposób tracisz oryginalny StackTrace.
Peter Csala