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?
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".)
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! :)
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/
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));
}
}
New .NET 5+ solution:
If you're using .NET 5 or above, you can use 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.
The following method also uses the using block so that the SHA256 instance gets disposed.
It also turns the password (which is passed in as a string) into a byte array using UTF-8 encoding, which was recommended by the accepted answer. Furthermore, we're also using the new SHA256 class as opposed to the old (now obselete) SHA256Managed class.
public string QuickHash(string secret)
{
using var sha256 = SHA256.Create();
var secretBytes = Encoding.UTF8.GetBytes(secret);
var secretHash = sha256.ComputeHash(secretBytes);
return Convert.ToHexString(secretHash);
}
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)
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);
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;
}
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();
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);
}
}
}
}
Related
If I hash the string "password" in C# using SHA256 using the below method I get this as my output:
e201065d0554652615c320c00a1d5bc8edca469d72c2790e24152d0c1e2b6189
But this website(SHA-256 produces a 256-bit (32-byte) hash value) tells me the has is:
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
I'm clearly having a data format issue or something similar. Any ideas why this C# SHA256Managed method is returning something different? I'm sending the method "password" as the input parameter.
private static string CalculateSHA256Hash(string text)
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(text);
SHA256Managed hashString = new SHA256Managed();
string hex = "";
hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
UnicodeEncoding is UTF-16, guaranteed to inflate all your characters to two to four bytes to represent their Unicode code point (see also the Remarks section on MSDN).
Use (the static) Encoding.UTF8 instead, so most characters will fit in one byte. It depends on which system you need to mimic whether that will work for all strings.
After computing the hash using Encoding.UTF8 as stated on the answer above, I would also return a string as follows:
hex = BitConverter.ToString(hashValue).Replace("-", "");
return hex;
I am hashing a string 3C970E3BF535 using the below method:
internal static string Hash(string content)
{
System.Security.Cryptography.KeyedHashAlgorithm ha = System.Security.Cryptography.KeyedHashAlgorithm.Create();
string asmKey = "Some key";
byte[] hashkey = System.Text.Encoding.ASCII.GetBytes(asmKey);
byte[] data1 = System.Text.Encoding.ASCII.GetBytes(content);
byte[] data2 = System.Text.Encoding.ASCII.GetBytes(content.ToLower());
System.Security.Cryptography.HMACSHA512 hmac = new System.Security.Cryptography.HMACSHA512(hashkey);
byte[] hashmac1 = hmac.ComputeHash(data1);
byte[] hashmac2 = hmac.ComputeHash(data2);
string hashmacencoded1 = System.Text.Encoding.ASCII.GetString(hashmac1);
string hashmacencoded2 = System.Text.Encoding.ASCII.GetString(hashmac2);
return string.Format("{0:X}", hashmacencoded1.GetHashCode());
}
The output I get is 2BED23B1. The length is 8.
If I reverse the string, using this method:
private static string Reverse(string s)
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
And then re-hash it, I get 7 characters - 707D653 instead of 8 characters of the non-reversed hash, though the fact that it's reversed or non-reversed shouldn't matter at all, since hashing should return same length despite the input.
I have switched out the key that we actually use, so if you use the code above it would not reproduce the issue. The issue would ONLY pop up if we use our key. What is going on?
You can't use System.Text.Encoding.ASCII.GetString to represent the hash because the hash is just a (pseudo) random assortment of bytes, not a binary representation of an ASCII string.
Instead you could represent it as a base64 string.
Convert.ToBase64String(hashmac1); // ABCDEF123== for example
You also should not be testing the GetHashCode() of the result - this is a different code that is used internally in the .NET framework for key-based data structures such as Dictionary, HashSet, etc. Just output the variable, not the hash code of the variable - the variable is the hash code.
The result of string.GetHashCode is always a 32bit signed integer.
In this case, your result just happened to have a leading zero, which gets lopped off when you do string.Format("{0:X}", ... )
There are all sorts of other problems with this (why are you getting the hash code of the hash?) but that's the answer to the problem you actually asked about.
I have to replicate C# hash from the code below into PHP. I have been searching but didn't find a solution so far.
From this article on creating an md5 hash string:
using System;
using System.Text;
using System.Security.Cryptography;
// Create an md5 sum string of this string
static public string GetMd5Sum(string str)
{
// First we need to convert the string into bytes, which
// means using a text encoder.
Encoder enc = System.Text.Encoding.Unicode.GetEncoder();
// Create a buffer large enough to hold the string
byte[] unicodeText = new byte[str.Length * 2];
enc.GetBytes(str.ToCharArray(), 0, str.Length, unicodeText, 0, true);
// Now that we have a byte array we can ask the CSP to hash it
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(unicodeText);
// Build the final string by converting each byte
// into hex and appending it to a StringBuilder
StringBuilder sb = new StringBuilder();
for (int i=0;i<result.Length;i++)
{
sb.Append(result[i].ToString("X2"));
}
// And return it
return sb.ToString();
}
For input = "123", the above code gives me "5FA285E1BEBE0A6623E33AFC04A1FBD5"
I have tried the following PHP code but it does not give the same output.
From the SO question PHP MD5 not matching C# MD5:
$str = "123";
$strUtf32 = mb_convert_encoding($str, "UTF-32LE");
echo md5($strUtf32);
This code has the result = "a0d5c8a4d386f15284ec25fe1eeeb426". By the way, changing UTF-32LE to utf-8 or utf-16 still does not give me the same result.
Can anyone help?
Yep, as CodesInChaos suggests, you got the encodings wrong.
On php side try this:
$str = "123";
$strUtf32 = mb_convert_encoding($str, "UTF-16LE");
echo md5($strUtf32);
This will give you 5FA285E1BEBE0A6623E33AFC04A1FBD5. This will match System.Text.Encoding.Unicode on the c# side.
Otherwise change System.Text.Encoding.Unicode to System.Text.Encoding.UTF32 on the c# side. This will give you A0D5C8A4D386F15284EC25FE1EEEB426.
Uhh, the C# code creates a MD5 hash and the PHP mb_convert_encoding function just encodes the string...
Plus, this is NOT THE FULL CODE from the link you gave. You are missing the important MD5 function:
$str = "123";
$strUtf32 = mb_convert_encoding($str, "UTF-16");
echo md5($strUtf32); <=====
If that code matches there should be NO REASON why that shouldn't work, as the MD5 algorithm is still the same and does not vary from language to language.
I have looked and found code to take a PHP sha512 hash and match it inside C#. I am currently looking for a way to go from a hash that was made in C# and get the same result in PHP. We are slowly moving away from asp.net to PHP and need a way to check our passwords in the database. Here is the C# code used to make the hash.
// Create a hash from a pwd and salt using sha512
public static string CreatePasswordHash(string _password, string _salt)
{
string saltAndPwd = String.Concat(_password, _salt);
SHA512 sha512 = new System.Security.Cryptography.SHA512Managed();
byte[] sha512Bytes = System.Text.Encoding.Default.GetBytes(saltAndPwd);
byte[] cryString = sha512.ComputeHash(sha512Bytes);
string hashedPwd = string.Empty;
for (int i = 0; i < cryString.Length; i++)
{
hashedPwd += cryString[i].ToString("X");
}
return hashedPwd;
}
In PHP I have tried to get it to match but it is off by just a few bytes it seems.
function CreatePasswordHash($_password, $_salt)
{
$saltAndPwd = $_password . $_salt;
$hashedPwd = hash('sha512', $saltAndPwd);
return strtoupper($hashedPwd);
}
When using the above with the same salt and password here is the results I get.
The first result is from C#, and the second result is from PHP:
60BB73FDA3FF7A444870C6D0DBC7C6966F8D5AD632B0A02762E0283051D7C54A5F4B01571D1A5BC8C689DBC411FEB92158383A56AFC6AE6074696AF36E16
60BB73FDA3FF7A444870C6D0DBC7C609066F8D5AD632B0A02762E0283051D7C54A5F4B001571D1A5BC8C689DBC411FEB092158383A56AFC6AE6074696AF36E16
Any ideas on why these are not matching up? Does it have to do with endian byte order?
try
hashedPwd += cryString[i].ToString("X2");
Editing PHP:
function CreatePasswordHash($_password, $_salt)
{
$saltAndPwd = $_password . $_salt;
$hashedPwd = hash('sha512', $saltAndPwd);
$hex_strs = str_split($hashedPwd,2);
foreach($hex_strs as &$hex) {
$hex = preg_replace('/^0/', '', $hex);
}
$hashedPwd = implode('', $hex_strs);
return strtoupper($hashedPwd);
}
The C# print out is not including leading zeros.
Replace
hashedPwd += cryString[i].ToString("X");
with
hashedPwd += cryString[i].ToString("X2");
Double check that you use the same character encoding in C# and PHP. GetBytes returns different results, depending on the encoding. System.Text.Encoding.Default depends on the localization of the OS.
You can try open_ssl_digest
echo openssl_digest($saltAndPwd, 'sha512');
if you have PHP >= 5.3
You could also use the hash_algos function to see which algorithms are supported in your system.
HTH
How can I convert the hashed result, which is a byte array, to a string?
byte[] bytePassword = Encoding.UTF8.GetBytes(password);
using (MD5 md5 = MD5.Create())
{
byte[] byteHashedPassword = md5.ComputeHash(bytePassword);
}
I need to convert byteHashedPassword to a string.
public static string ToHex(this byte[] bytes, bool upperCase)
{
StringBuilder result = new StringBuilder(bytes.Length*2);
for (int i = 0; i < bytes.Length; i++)
result.Append(bytes[i].ToString(upperCase ? "X2" : "x2"));
return result.ToString();
}
You can then call it as an extension method:
string hexString = byteArray.ToHex(false);
I always found this to be the most convenient:
string hashPassword = BitConverter.ToString(byteHashedPassword).Replace("-","");
For some odd reason BitConverter likes to put dashes between bytes, so the replace just removes them.
Update:
If you prefer "lowercase" hex, just do a .ToLower() and boom.
Do note that if you are doing this as a tight loop and many ops this could be expensive since there are at least two implicit string casts and resizes going on.
You can use Convert.ToBase64String and Convert.FromBase64String to easily convert byte arrays into strings.
If you're in the 'Hex preference' camp you can do this. This is basically a minimal version of the answer by Philippe Leybaert.
string.Concat(hash.Select(x => x.ToString("X2")))
B1DB2CC0BAEE67EA47CFAEDBF2D747DF
Well as it is a hash, it has possibly values that cannot be shown in a normal string, so the best bet is to convert it to Base64 encoded string.
string s = Convert.ToBase64String(bytes);
and use
byte[] bytes = Convert.FromBase64(s);
to get the bytes back.
Well, you could use the string constructor that takes bytes and an encoding, but you'll likely get a difficult to manage string out of that since it could contain lots of fun characters (null bytes, newlines, control chars, etc)
The best way to do this would be to encode it with base 64 to get a nice string that's easy to work with:
string s = Convert.ToBase64String(bytes);
And to go from that string back to a byte array:
byte[] bytes = Convert.FromBase64String(s);
I know this is an old question, but I thought I would add you can use the built-in methods (I'm using .NET 6.0):
Convert.ToBase64String(hashBytes); (Others have already mentioned this)
Convert.ToHexString(hashBytes);
For anyone interested a Nuget package I created called CryptoStringify allows you to convert a string to a hashed string using a nice clean syntax, without having to play around with byte arrays:
using (MD5 md5 = MD5.Create())
{
string strHashedPassword = md5.Hash(password);
}
It's an extension method on HashAlgorithm and KeyedHashAlgorithm so works on SHA1, HMACSHA1, SHA256 etc too.
https://www.nuget.org/packages/cryptostringify