Eine Schnur mit Sha256 zerschlagen

141

Ich versuche, einen String mit SHA256 zu hashen. Ich verwende den folgenden Code:

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

Dieser Code liefert jedoch signifikant andere Ergebnisse als mein Freund PHP sowie Online-Generatoren (wie dieser Generator ).

Weiß jemand, was der Fehler ist? Verschiedene Basen?

Nattfrosten
quelle
17
Nicht zum Thema gehörend, aber denken Sie daran, dass das Erstellen eines StringBuilder und die Verwendung von AppendFormat anstelle von String.Format in Ihrer foreach-Schleife verhindert, dass Ihr Code unnötig viele String-Objekte erstellt.
Marcel Lamothe

Antworten:

154

Encoding.Unicodeist der irreführende Name von Microsoft für UTF-16 (eine doppelt breite Codierung, die in der Windows-Welt aus historischen Gründen verwendet wird, aber von niemand anderem verwendet wird). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx

Wenn Sie Ihr bytesArray untersuchen, werden Sie feststellen, dass jedes zweite Byte vorhanden ist 0x00(aufgrund der doppelt breiten Codierung).

Sie sollten Encoding.UTF8.GetBytesstattdessen verwenden.

Sie sehen jedoch auch unterschiedliche Ergebnisse, je nachdem, ob Sie das Abschlussbyte '\0'als Teil der Daten betrachten, die Sie hashen. Das Hashing der zwei Bytes "Hi"ergibt ein anderes Ergebnis als das Hashing der drei Bytes "Hi". Sie müssen sich entscheiden, was Sie tun möchten. (Vermutlich möchten Sie den PHP-Code Ihres Freundes ausführen.)

Für ASCII-Text Encoding.UTF8wird auf jeden Fall geeignet sein. Wenn Sie eine perfekte Kompatibilität mit dem Code Ihres Freundes anstreben , auch bei Nicht-ASCII-Eingaben, sollten Sie einige Testfälle mit Nicht-ASCII-Zeichen wie éund ausprobieren und prüfen, ob Ihre Ergebnisse noch übereinstimmen. Wenn nicht, müssen Sie herausfinden, welche Codierung Ihr Freund wirklich verwendet. Es könnte sich um eine der 8-Bit- "Codepages" handeln, die vor der Erfindung von Unicode populär waren. (Auch hier denke ich, dass Windows der Hauptgrund ist, warum sich noch jemand um "Codepages" sorgen muss.)

Quuxpluson
quelle
3
@Elmue, Sie können erfreut sein zu erfahren, dass "Sortieren nach UTF8-codierten Bytes" und "Sortieren nach Unicode-Codepunkten" gleichwertig sind! (Wie "Sortieren nach UTF16-codierten shorts", aber nicht "Sortieren nach UTF16-codierten Bytes", es sei denn, Sie befinden sich auf einem Big-Endian-System, was Windows nicht ist.) "Sortieren" in Unicode ist jedoch wirklich ein kompliziertes Thema, das für einen anderen Tag gespeichert werden sollte.
Quuxplusone
2
@ Elmue sei nicht so zuversichtlich in deine falschen Antworten. Versuch es; du wirst überrascht sein. Ob die Überraschung angenehm oder unangenehm ist, liegt ganz bei Ihnen. :)
Quuxplusone
2
@Elmue, „ Was ist, wenn Sie einen Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung durchführen möchten? ”Sie müssen auch Bytes in UTF-16 konvertieren, wenn Sie solche Dinge tun möchten. Die Tatsache, dass es eine feste Länge hat, hilft kein bisschen.
Arturo Torres Sánchez
2
Die "von niemand anderem verwendet" Behauptung ist sehr interessant, da Java Strings auch intern als UTF-16 behandelt ...
Sami Kuhmonen
4
@Elmue "Ihre Kommentare sind falsch: UTF16 ist Unicode." Sie liegen falsch. "Unicode" ist ein Standard, der Glyphen Zahlen (Codepunkte) zuweist. Mit Ausnahme von Ersatzpaaren wird nicht angegeben, wie diese Zahlen als Bytes dargestellt werden sollen. UTF16 gibt Codepunkte <--> Bytes an. Unicode gibt Glyphen <--> Codepunkte an.
Antiduh
103

Ich hatte auch dieses Problem mit einem anderen Implementierungsstil, aber ich habe vergessen, woher ich es habe, seit es vor 2 Jahren war.

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

Wenn ich abcdefghi2013aus irgendeinem Grund etwas eingebe, werden unterschiedliche Ergebnisse angezeigt und Fehler in meinem Anmeldemodul angezeigt. Dann habe ich versucht, den Code auf die gleiche Weise zu ändern, wie von Quuxplusone vorgeschlagen, und die Codierung von ASCIIbis geändert, bis UTF8es endlich funktioniert hat!

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

Nochmals vielen Dank Quuxplusone für die wunderbare und detaillierte Antwort! :) :)

Nico Dumdum
quelle
Ihre Lösung hat bei mir funktioniert. aber ich habe einen anderen Fall. Es ist mit sha512 und die Codezeile, die mein Problem gelöst hat, ist, dass hash += bit.ToString("x2");ich hier eine Frage habe: Ich habe verwendet Convert.ToBase64String(byte[] encryptedBytes), um von Bytes zurück in Zeichenfolgen zu konvertieren. das gab mir ein anderes Ergebnis. Was ist der Unterschied zwischen diesen beiden Konvertierungsmethoden von Bytes in Zeichenfolgen?
Keval Langalia
Ist es möglich, hier Anpassungen vorzunehmen (wie bei meinem eigenen Initialisierungsvektor), oder wird nur eine zufällige Zeichenfolge angehängt / vorangestellt?
FrenkyB
Ich bin mir nicht sicher, was du meinst. Dies ist nur eine sehr einfache Hashing-Funktion und Sie können sie jederzeit hinzufügen / anpassen, wie Sie möchten. Mit Anhängen / Voranstellen einer zufälligen Zeichenfolge meinen Sie das Salzen? Nun, das ist eine gute Möglichkeit, es für weitere Sicherheit anzupassen.
Nico Dumdum
Es wird nicht empfohlen, nur SHA-Hashing ohne Arbeitsfaktor zum Speichern von Kennwörtern zu verwenden. Mit anderen Worten, der Hashing-Prozess des Passworts muss erheblich langsam sein, damit Hacker nicht schnell raten können. Verwenden Sie Bcrypt oder Scrypt für eine bessere Sicherheit.
Ton Snoei
@ TonSnoei Ja das ist wahr. Dies ist jedoch ein alter Code aus einer alten internen Systemanwendung im College, den niemand mehr verwendet, und ich würde ihn selbst wirklich nicht empfehlen. Darüber hinaus geht es in diesem Thread speziell um die SHA256-Codierung und nicht direkt um Passwörter. Es würde mir allerdings nichts ausmachen, es zu bearbeiten, um Verweise auf Passwörter zu entfernen, wenn das Ihre Fantasie anregt.
Nico Dumdum
6
public static string ComputeSHA256Hash(string text)
{
    using (var sha256 = new SHA256Managed())
    {
        return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
    }                
}

Der Grund, warum Sie unterschiedliche Ergebnisse erhalten, liegt darin, dass Sie nicht dieselbe Zeichenfolgencodierung verwenden. Der Link, den Sie für die Online-Website setzen, die SHA256 berechnet, verwendet die UTF8-Codierung, während Sie in Ihrem Beispiel die Unicode-Codierung verwendet haben. Es handelt sich um zwei verschiedene Codierungen, sodass Sie nicht das gleiche Ergebnis erhalten. Mit dem obigen Beispiel erhalten Sie den gleichen SHA256-Hash der verlinkten Website. Sie müssen dieselbe Codierung auch in PHP verwenden.

Das absolute Minimum Jeder Softwareentwickler muss unbedingt über Unicode und Zeichensätze Bescheid wissen (keine Ausreden!)

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

Auto
quelle
4

In der PHP-Version können Sie im letzten Parameter 'true' senden, der Standardwert ist jedoch 'false'. Der folgende Algorithmus entspricht der Hash-Funktion des Standard-PHP, wenn 'sha256' als erster Parameter übergeben wird:

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
quelle
4
Ich würde nicht verwenden ASCIIund byte[] arrBytes = System.Text.Encoding.UTF8.GetBytes(strData)stattdessen tun .
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));
            }
        }
BOGEN
quelle
1
Ich mag die Tatsache, dass du etwas Salz hinzugefügt hast ^^
Fabian
1

Der kürzeste und schnellste Weg aller Zeiten. Nur 1 Zeile!

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
quelle