Hashing a string with Sha256

294,800

Solution 1

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".)

Solution 2

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! :)

Solution 3

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/

Solution 4

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

Solution 5

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);
Share:
294,800

Related videos on Youtube

Nattfrosten
Author by

Nattfrosten

Updated on July 30, 2020

Comments

  • Nattfrosten
    Nattfrosten almost 4 years

    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?

    • Marcel Lamothe
      Marcel Lamothe over 9 years
      Off topic but keep in mind that creating a StringBuilder and using AppendFormat instead of String.Format in your foreach loop will prevent your code from needlessly creating lots of string objects.
    • zgabi
      zgabi over 2 years
      @MarcelLamothe Since the size of the hash is known, it is even better to allocate a char[] with the exact size then create a string from the char[]... I know that your comment is old, in the In the newer frameworks you can also use stackalloc for the Span of char so only a string ovject is created at the end from the span
  • Nattfrosten
    Nattfrosten almost 12 years
    This is part of a log-in system, just switching to UTF8.GetBytes() did the trick, thank you :D
  • Cole Tobin
    Cole Tobin almost 12 years
    It is to my understanding that PHP uses ASCII by default.
  • Quuxplusone
    Quuxplusone almost 12 years
    ASCII and UTF-8 are for all practical purposes the same, so, good. :) But I've added a paragraph pointing out that they will encode high-byte characters differently, so some tweaking may be required.
  • Elmue
    Elmue over 9 years
    Your comments are incorrect: UTF16 is Unicode. There is nothing misleading about it. Codepages are dead since decades. Windows is no reason to worry about codepages. Since Windows NT Microsoft made big efforts to convert the entire OS to unicode. On the other hand Linux and PHP did the cheap workaround: Instead of using Unicode they still use UTF8. This is not clever. Example: If you want a sorted directory listing in Linux you must convert the UTF8 file names to Unicode, sort them and convert back to UTF8. This is inefficient and awkward. Windows uses wide strings that you can sort directly
  • Quuxplusone
    Quuxplusone over 9 years
    @Elmue, you may be pleased to learn that "sorting by UTF8-encoded bytes" and "sorting by Unicode codepoints" are equivalent! (As is "sorting by UTF16-encoded shorts", but not "sorting by UTF16-encoded bytes" unless you're on a big-endian system, which Windows isn't.) However, "sorting" in Unicode is really a complicated topic that should be saved for another day.
  • Elmue
    Elmue over 9 years
    This is not correct. In UTF16 the greek letter 'beta' 0x3B2 is greater than the greek letter 'alpha' 0x3B1. They can be compared directly. On the other hand in UTF8 you have byte sequences with the length between 1 and 6 bytes. Without decoding UTF8 you do not even know where a letter starts and where it ends. You just have a long snail of bytes. UTF8 MUST first be decoded before you can do anything useful with it.
  • Quuxplusone
    Quuxplusone over 9 years
    @Elmue don't be so confident in your wrong answers. Try it out; you'll be surprised. Whether the surprise is pleasant or unpleasant is entirely up to you. :)
  • Elmue
    Elmue over 9 years
    Maybe you can do an ultra primitive binary comparison of strings directly in Utf8. But what if you want to do a case insensitive comparison? It is NOT possible. And what if you want a substring(0,8)? It is NOT possible because a character may be between 1 and 6 bytes long. Without decoding Utf8 you don't know where a char starts and where it ends. Any string operation like padding, searching case insensitive, removing or replacing chars, regex needs decoding the Utf8 string. So Microsoft clearly did the better work with UTF16 here than Linux. If this is pleasant or unpleasant is up to you.
  • Arturo Torres Sánchez
    Arturo Torres Sánchez over 9 years
    @Elmue, “What if you want to do a case insensitive comparison?” You also need to convert bytes in UTF-16 if you want to do this kind of stuff. The fact that it's fixed length does not help one bit.
  • Keval Langalia
    Keval Langalia over 9 years
    your solution worked for me. but i have different case. it's with sha512 and the code line that solved my problem is hash += bit.ToString("x2"); I've a question here: I was using Convert.ToBase64String(byte[] encryptedBytes) to convert back from bytes to string. that was giving me different result. so what is the different between these two methods of converting from bytes to string..?
  • Sami Kuhmonen
    Sami Kuhmonen over 9 years
    The "not used by anyone else" is quite interesting claim, since Java internally handles strings as UTF-16 also...
  • antiduh
    antiduh almost 9 years
    @Elmue "Your comments are incorrect: UTF16 is Unicode." You are wrong. "Unicode" is a standard that assigns numbers (code points) to glyphs. Excepting surrogate pairs, it does not state how to represent those numbers as bytes. UTF16 specifies code points <--> bytes. Unicode specifies glyphs <--> code points.
  • Logan Pickup
    Logan Pickup over 8 years
    @Elmue UTF-16 does not have fixed-width characters. Most characters are two bytes, but not all - some need 4 bytes to encode, for example, certain rare Chinese/Japanese/Korean characters. UCS-2 is truly fixed-width, but Windows has used UTF-16 since Windows 2000. Also, due to being split over more than one code plane, you might find surprises when sorting Chinese containing the rare characters; even without those characters, which sorting method to you want? Chinese has more than one logical ordering.
  • c00000fd
    c00000fd over 8 years
    I wouldn't be using ASCII and do byte[] arrBytes = System.Text.Encoding.UTF8.GetBytes(strData) instead.
  • FrenkyB
    FrenkyB almost 8 years
    Is it possible to use some customization here (like my own initialization vector) or is appending / prepending random string only option?
  • Nico Dumdum
    Nico Dumdum almost 8 years
    I'm not really sure what you mean. This just a very simple hashing function and you can always add/customize it however you want. By appending/prepending random string do you mean salting? Well that's one good way of customizing it for further security.
  • Ton Snoei
    Ton Snoei about 7 years
    It is not recommended to use just SHA hashing without a work factor for storing passwords. In other words, the hashing process of the password needs to be significantly slow, to prevent hackers from guessing fast. Use Bcrypt, or Scrypt for better security.
  • Nico Dumdum
    Nico Dumdum almost 7 years
    @TonSnoei Yes that is true. However, this is some old code from some ancient internal system application back in college that nobody uses anymore and I really wouldn't recommend this myself. Moreover, this thread is specifically about SHA256 encoding and not directly about passwords. Although, I wouldn't mind editing it to remove references to passwords if that tickles your fancy.
  • Jeson Martajaya
    Jeson Martajaya over 4 years
    SHA256Managed is IDisposable. I think you should dispose it after use.
  • Fabian
    Fabian over 4 years
    i like the fact that you added some salt ^^
  • Peter Csala
    Peter Csala almost 4 years
    Welcome to StackOverflow. Please provide some explanation why do you think your proposed solution might solve the OP's problem.
  • Peter Csala
    Peter Csala almost 4 years
    Please try to avoid re-throwing exception. With that you loose the original StackTrace.
  • Brendan Sluke
    Brendan Sluke over 3 years
    This is the most concise answer.
  • zgabi
    zgabi over 2 years
    I think the condition should be only text == null... since the hash of the empty string should not be an empty string.