Hachage d'une chaîne avec Sha256

141

J'essaye de hacher une chaîne en utilisant SHA256, j'utilise le code suivant:

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

Cependant, ce code donne des résultats significativement différents par rapport à mes amis php, ainsi que des générateurs en ligne (tels que Ce générateur )

Quelqu'un sait-il quelle est l'erreur? Différentes bases?

Nattfrosten
la source
17
Hors sujet, mais gardez à l'esprit que créer un StringBuilder et utiliser AppendFormat au lieu de String.Format dans votre boucle foreach empêchera votre code de créer inutilement de nombreux objets chaîne.
Marcel Lamothe

Réponses:

155

Encoding.Unicodeest le nom trompeur de Microsoft pour UTF-16 (un encodage double largeur, utilisé dans le monde Windows pour des raisons historiques mais non utilisé par personne d'autre). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx

Si vous inspectez votre bytestableau, vous verrez que chaque octet sur deux est 0x00(à cause du codage double largeur).

Vous devriez utiliser à la Encoding.UTF8.GetBytesplace.

Mais aussi, vous verrez des résultats différents selon que vous considérez ou non l' '\0'octet de fin comme faisant partie des données que vous hachez. Le hachage des deux octets "Hi"donnera un résultat différent du hachage des trois octets "Hi". Vous devrez décider ce que vous voulez faire. (Vous voulez probablement faire ce que fait le code PHP de votre ami.)

Pour le texte ASCII, Encoding.UTF8conviendra certainement. Si vous visez une compatibilité parfaite avec le code de votre ami, même sur des entrées non ASCII, vous feriez mieux d'essayer quelques cas de test avec des caractères non ASCII tels que éet et voir si vos résultats correspondent toujours. Sinon, vous devrez comprendre quel encodage votre ami utilise réellement; il peut s'agir de l'une des «pages de codes» 8 bits qui étaient populaires avant l'invention d'Unicode. (Encore une fois, je pense que Windows est la principale raison pour laquelle quiconque doit encore s'inquiéter des "pages de codes".)

Quuxplusone
la source
3
@Elmue, vous serez peut-être heureux d'apprendre que le "tri par octets encodés en UTF8" et "le tri par points de code Unicode" sont équivalents! (De même que le "tri par shorts encodés en UTF16 ", mais pas le "tri par octets encodés en UTF16", sauf si vous êtes sur un système big-endian, ce que Windows n'est pas.) Cependant, le "tri" en Unicode est en fait un sujet compliqué qui devrait être conservé pour un autre jour.
Quuxplusone
2
@Elmue n'est pas si confiant dans vos mauvaises réponses. Essaye le; vous serez surpris. Que la surprise soit agréable ou désagréable dépend entièrement de vous. :)
Quuxplusone
2
@Elmue, « Et si vous voulez faire une comparaison insensible à la casse? «Vous devez également convertir des octets en UTF-16 si vous voulez faire ce genre de choses. Le fait que sa longueur soit fixe n'aide pas du tout.
Arturo Torres Sánchez
2
Le "non utilisé par quelqu'un d'autre" est une affirmation assez intéressante, puisque Java gère en interne les chaînes comme UTF-16 également ...
Sami Kuhmonen
4
@Elmue "Vos commentaires sont incorrects: UTF16 est Unicode." Vous avez tort. "Unicode" est une norme qui attribue des nombres (points de code) aux glyphes. À l'exception des paires de substitution, il n'indique pas comment représenter ces nombres sous forme d'octets. UTF16 spécifie les points de code <--> octets. Unicode spécifie les glyphes <--> points de code.
antiduh
103

J'ai aussi eu ce problème avec un autre style d'implémentation mais j'ai oublié d'où je l'ai eu puisqu'il y a 2 ans.

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

Lorsque j'entre quelque chose comme abcdefghi2013pour une raison quelconque, cela donne des résultats différents et entraîne des erreurs dans mon module de connexion. Ensuite, j'ai essayé de modifier le code de la même manière que suggéré par Quuxplusone et j'ai changé l'encodage de ASCIIà UTF8puis cela a finalement fonctionné!

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

Merci encore Quuxplusone pour la réponse merveilleuse et détaillée! :)

Nico Dumdum
la source
votre solution a fonctionné pour moi. mais j'ai un cas différent. c'est avec sha512 et la ligne de code qui a résolu mon problème est hash += bit.ToString("x2");que j'ai une question ici: j'utilisais Convert.ToBase64String(byte[] encryptedBytes)pour convertir des octets en chaîne. cela me donnait un résultat différent. alors quelle est la différence entre ces deux méthodes de conversion d'octets en chaîne ..?
Keval Langalia
Est-il possible d'utiliser une certaine personnalisation ici (comme mon propre vecteur d'initialisation) ou est-ce que l'ajout / le préfixe d'une chaîne aléatoire est uniquement une option?
FrenkyB
Je ne sais pas vraiment ce que tu veux dire. Il s'agit simplement d'une fonction de hachage très simple et vous pouvez toujours l'ajouter / la personnaliser comme vous le souhaitez. En ajoutant / en ajoutant une chaîne aléatoire, voulez-vous dire salage? Eh bien, c'est un bon moyen de le personnaliser pour plus de sécurité.
Nico Dumdum
Il n'est pas recommandé d'utiliser uniquement le hachage SHA sans facteur de travail pour stocker les mots de passe. En d'autres termes, le processus de hachage du mot de passe doit être considérablement lent, pour empêcher les pirates de deviner rapidement. Utilisez Bcrypt ou Scrypt pour une meilleure sécurité.
Ton Snoei
@TonSnoei Oui, c'est vrai. Cependant, il s'agit d'un ancien code d'une ancienne application système interne à l'université que personne n'utilise plus et je ne le recommanderais vraiment pas moi-même. De plus, ce fil concerne spécifiquement l'encodage SHA256 et non directement les mots de passe. Cependant, cela ne me dérangerait pas de le modifier pour supprimer les références aux mots de passe si cela vous chatouille.
Nico Dumdum
6
public static string ComputeSHA256Hash(string text)
{
    using (var sha256 = new SHA256Managed())
    {
        return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
    }                
}

La raison pour laquelle vous obtenez des résultats différents est que vous n'utilisez pas le même codage de chaîne. Le lien que vous avez mis pour le site Web en ligne qui calcule SHA256 utilise le codage UTF8, alors que dans votre exemple vous avez utilisé le codage Unicode. Ce sont deux encodages différents, vous n'obtenez donc pas le même résultat. Avec l'exemple ci-dessus, vous obtenez le même hachage SHA256 du site Web lié. Vous devez utiliser le même encodage également en PHP.

Le minimum absolu que tout développeur de logiciel doit absolument et positivement savoir sur l'Unicode et les jeux de caractères (sans excuses!)

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

Auto
la source
4

Dans la version PHP, vous pouvez envoyer «true» dans le dernier paramètre, mais la valeur par défaut est «false». L'algorithme suivant est équivalent à la fonction de hachage PHP par défaut lors du passage de 'sha256' comme premier paramètre:

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
la source
4
Je n'utiliserais pas ASCIIet ne le ferais pas à la byte[] arrBytes = System.Text.Encoding.UTF8.GetBytes(strData)place.
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));
            }
        }
ARC
la source
1
j'aime le fait que tu aies ajouté du sel ^^
Fabian
1

Le moyen le plus court et le plus rapide jamais conçu. Seulement 1 ligne!

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
la source
-2

Cela fonctionne pour moi dans .NET Core 3.1.
Mais pas dans l'aperçu de .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);
            }
        }  
    }
}
 
Alberto T. Payero Mota
la source
1
Bienvenue dans StackOverflow. Veuillez expliquer pourquoi la solution que vous proposez pourrait résoudre le problème du PO.
Peter Csala le
1
Veuillez éviter de relancer l'exception. Avec cela, vous perdez le StackTrace d'origine.
Peter Csala le