Nattfrosten
Nattfrosten

Reputation: 2119

Hashing a string with SHA256

I try to hash a string using SHA256, I'm using the following 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;
    }
}

However, this code gives significantly different results compared to my friends php, as well as online generators (such as This generator)

Does anyone know what the error is? Different bases?

Upvotes: 187

Views: 378581

Answers (9)

Arad
Arad

Reputation: 12695

New .NET 5+ solution:

If you're using .NET 5 or above, you can now achieve this in just 3 lines of code.

string QuickHash(string input)
{
    var inputBytes = Encoding.UTF8.GetBytes(input);
    var inputHash = SHA256.HashData(inputBytes);
    return Convert.ToHexString(inputHash);
}

string hash = QuickHash("...");

The code above:

  • Uses the new static HashData method on the SHA256 class to avoid instantiating and having to dispose a new instance each time.
  • Uses the new Convert.ToHexString method to convert the hash byte array into a hexadecimal string; eliminating the hassle of using string builders and so on.
  • Uses the new SHA256 class as opposed to the old (now obsolete) SHA256Managed class.
  • Uses UTF-8 encoding to convert the input string into a byte array, which was recommended by the accepted answer.

Note: You should NOT use this method for hashing user passwords. General-purpose hashing functions such as SHA-256 aren't suited for use for passwords anymore, even if you add salts. This is useful for hashing strings that you know have high entropy, such as long randomly generated session tokens and whatnot. For storing passwords, you must look into slower hashing functions that were specifically designed for this purpose, such as Bcrypt, Scrypt, or PBKDF2 (the latter is available natively in .NET — see this)

Upvotes: 39

Vítor Oliveira
Vítor Oliveira

Reputation: 2091

I was looking and testing theses answers, and Visual Studio showed me that SHA256Managed is now Obsolete (here)

So, I used the SHA256 class instead:

Encoding enc = Encoding.UTF8;
var hashBuilder = new StringBuilder();
using var hash = SHA256.Create();
byte[] result = hash.ComputeHash(enc.GetBytes(yourStringToHash));
foreach (var b in result)
    hashBuilder.Append(b.ToString("x2"));
string result = hashBuilder.ToString();

Upvotes: 7

This work for me in .NET Core 3.1.
But not in .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);
            }
        }  
    }
}
 

Upvotes: -4

Erçin Dedeoğlu
Erçin Dedeoğlu

Reputation: 5383

The shortest and fastest way ever. Only 1 line!

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

Upvotes: 7

Auto
Auto

Reputation: 676

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

The reason why you get different results is because you don't use the same string encoding. The link you put for the on-line web site that computes SHA256 uses UTF8 Encoding, while in your example you used Unicode Encoding. They are two different encodings, so you don't get the same result. With the example above you get the same SHA256 hash of the linked web site. You need to use the same encoding also in PHP.

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No 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/

Upvotes: 15

ARC
ARC

Reputation: 1081

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

Upvotes: 11

Nico Dumdum
Nico Dumdum

Reputation: 3273

I also had this problem with another style of implementation but I forgot where I got it since it was 2 years ago.

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

When I input something like abcdefghi2013 for some reason it gives different results and results in errors in my login module. Then I tried modifying the code the same way as suggested by Quuxplusone and changed the encoding from ASCII to UTF8 then it finally worked!

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

Thanks again Quuxplusone for the wonderful and detailed answer! :)

Upvotes: 138

Rachel
Rachel

Reputation: 137

In the PHP version you can send 'true' in the last parameter, but the default is 'false'. The following algorithm is equivalent to the default PHP's hash function when passing 'sha256' as the first parameter:

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

Upvotes: 5

Quuxplusone
Quuxplusone

Reputation: 26949

Encoding.Unicode is Microsoft's misleading name for UTF-16 (a double-wide encoding, used in the Windows world for historical reasons but not used by anyone else). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx

If you inspect your bytes array, you'll see that every second byte is 0x00 (because of the double-wide encoding).

You should be using Encoding.UTF8.GetBytes instead.

But also, you will see different results depending on whether or not you consider the terminating '\0' byte to be part of the data you're hashing. Hashing the two bytes "Hi" will give a different result from hashing the three bytes "Hi". You'll have to decide which you want to do. (Presumably you want to do whichever one your friend's PHP code is doing.)

For ASCII text, Encoding.UTF8 will definitely be suitable. If you're aiming for perfect compatibility with your friend's code, even on non-ASCII inputs, you'd better try a few test cases with non-ASCII characters such as é and and see whether your results still match up. If not, you'll have to figure out what encoding your friend is really using; it might be one of the 8-bit "code pages" that used to be popular before the invention of Unicode. (Again, I think Windows is the main reason that anyone still needs to worry about "code pages".)

Upvotes: 181

Related Questions