Hashing una stringa con Sha256


141

Provo a eseguire l'hashing di una stringa usando SHA256, sto usando il seguente codice:

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

Tuttavia, questo codice fornisce risultati significativamente diversi rispetto ai miei amici php, così come i generatori online (come questo generatore )

Qualcuno sa qual è l'errore? Basi diverse?


17
Fuori tema ma tieni presente che la creazione di StringBuilder e l'utilizzo di AppendFormat anziché String.Format nel tuo ciclo foreach impediranno al tuo codice di creare inutilmente molti oggetti stringa.
Marcel Lamothe,

Risposte:


154

Encoding.Unicodeè il nome fuorviante di Microsoft per UTF-16 (una codifica a doppia larghezza, utilizzata nel mondo Windows per motivi storici ma non utilizzata da nessun altro). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx

Se ispezioni l' bytesarray, vedrai che ogni secondo byte è 0x00(a causa della codifica a doppia larghezza).

Dovresti usare Encoding.UTF8.GetBytesinvece.

Inoltre, vedrai risultati diversi a seconda che tu consideri o meno il '\0'byte finale come parte dei dati che stai eseguendo l'hashing. L'hash dei due byte "Hi"darà un risultato diverso dall'hash dei tre byte "Hi". Dovrai decidere quale vuoi fare. (Presumibilmente vuoi fare qualunque sia il codice PHP del tuo amico.)

Per il testo ASCII, Encoding.UTF8sarà sicuramente adatto. Se stai cercando la perfetta compatibilità con il codice del tuo amico, anche su input non ASCII, è meglio provare alcuni casi di test con caratteri non ASCII come ée e vedere se i risultati continuano a corrispondere. In caso contrario, dovrai capire quale codifica sta realmente usando il tuo amico; potrebbe essere una delle "pagine di codice" a 8 bit che era popolare prima dell'invenzione di Unicode. (Ancora una volta, penso che Windows sia il motivo principale per cui qualcuno deve ancora preoccuparsi delle "pagine di codice".)


3
@Elmue, potresti essere contento di apprendere che "l'ordinamento per byte con codifica UTF8" e "l'ordinamento per punti di codice Unicode" sono equivalenti! (Come lo "ordinamento per shorts codificato UTF16 ", ma non "l'ordinamento per byte codificato UTF16" a meno che non ci si trovi su un sistema big-endian, che Windows non lo è.) Tuttavia, "ordinamento" in Unicode è davvero un argomento complicato che dovrebbe essere salvato per un altro giorno.
Quuxplusone,

2
@Elmue non essere così fiducioso nelle tue risposte sbagliate. Provalo; sarai sorpreso. Che la sorpresa sia piacevole o spiacevole dipende interamente da te. :)
Quuxplusone,

2
@Elmue, “ Cosa succede se si desidera eseguire un confronto senza distinzione tra maiuscole e minuscole? "Devi anche convertire i byte in UTF-16 se vuoi fare questo tipo di cose. Il fatto che abbia una lunghezza fissa non aiuta un po '.
Arturo Torres Sánchez,

2
Il "non utilizzato da nessun altro" è una affermazione piuttosto interessante, dal momento che Java gestisce internamente le stringhe come UTF-16 ...
Sami Kuhmonen,

4
@Elmue "I tuoi commenti sono errati: UTF16 è Unicode." Tui hai torto. "Unicode" è uno standard che assegna numeri (punti di codice) ai glifi. Tranne le coppie surrogate, non indica come rappresentare quei numeri come byte. UTF16 specifica i punti di codice <--> byte. Unicode specifica glifi <--> punti di codice.
antiduh,

103

Ho avuto anche questo problema con un altro stile di implementazione, ma ho dimenticato dove l'ho preso da quando era 2 anni fa.

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

Quando inserisco qualcosa di simile abcdefghi2013per qualche motivo, si ottengono risultati diversi e si verificano errori nel mio modulo di accesso. Quindi ho provato a modificare il codice nello stesso modo suggerito da Quuxplusone e ho cambiato la codifica da ASCIIa UTF8poi finalmente ha funzionato!

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

Grazie ancora Quuxplusone per la risposta meravigliosa e dettagliata! :)


la tua soluzione ha funzionato per me. ma ho un caso diverso. è con sha512 e la riga di codice che ha risolto il mio problema è hash += bit.ToString("x2");che ho una domanda qui: stavo usando Convert.ToBase64String(byte[] encryptedBytes)per riconvertire da byte a stringa. questo mi stava dando risultati diversi. quindi qual è la differenza tra questi due metodi di conversione da byte a stringa ..?
Keval Langalia,

È possibile utilizzare un po 'di personalizzazione qui (come il mio vettore di inizializzazione) o aggiungere o aggiungere solo l'opzione di stringa casuale?
FrenkyB,

Non sono proprio sicuro di cosa tu voglia dire. Questa è solo una funzione di hashing molto semplice e puoi sempre aggiungerla / personalizzarla come preferisci. Aggiungendo / anteponendo una stringa casuale intendi salare? Bene, questo è un buon modo di personalizzarlo per ulteriore sicurezza.
Nico Dumdum,

Non è consigliabile utilizzare solo l'hash SHA senza un fattore di lavoro per l'archiviazione delle password. In altre parole, il processo di hashing della password deve essere significativamente lento, per impedire agli hacker di indovinare rapidamente. Usa Bcrypt o Scrypt per una migliore sicurezza.
Ton Snoei,

@TonSnoei Sì, è vero. Tuttavia, questo è un vecchio codice di qualche antica applicazione di sistema interno al college che nessuno usa più e non lo consiglierei a me stesso. Inoltre, questo thread riguarda in particolare la codifica SHA256 e non direttamente le password. Tuttavia, non mi dispiacerebbe modificarlo per rimuovere i riferimenti alle password se ciò solletica la tua fantasia.
Nico Dumdum,

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

Il motivo per cui si ottengono risultati diversi è perché non si utilizza la stessa codifica stringa. Il collegamento inserito per il sito Web in linea che calcola SHA256 utilizza la codifica UTF8, mentre nell'esempio è stata utilizzata la codifica Unicode. Sono due codifiche diverse, quindi non ottieni lo stesso risultato. Con l'esempio sopra ottieni lo stesso hash SHA256 del sito web collegato. Devi usare la stessa codifica anche in PHP.

Il minimo assoluto Ogni sviluppatore di software deve assolutamente sapere positivamente sugli Unicode e sui set di caratteri (senza scuse!)

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


4

Nella versione PHP è possibile inviare "true" nell'ultimo parametro, ma l'impostazione predefinita è "false". Il seguente algoritmo è equivalente alla funzione hash del PHP predefinito quando si passa 'sha256' come primo parametro:

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
Non userei ASCIIe farei byte[] arrBytes = System.Text.Encoding.UTF8.GetBytes(strData)invece.
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
mi piace il fatto che tu abbia aggiunto del sale ^^
Fabian il

1

Il modo più breve e veloce di sempre. Solo 1 linea!

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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.