Converting a md5 hash byte array to a string - c#

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

Related

Hash algorithm returning abnormal string length

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.

"Specified value has invalid Control characters" when converting SHA512 output to string

I am attempting to create an Hash for an API.
my input is something like this:
FBN|Web|3QTC0001|RS1|260214133217|000000131127897656
And my expected output is like :
17361DU87HT56F0O9967E34FDFFDFG7UO334665324308667FDGJKD66F9888766DFKKJJR466634HH6566734JHJH34766734NMBBN463499876554234343432456
I tried the bellow but I keep getting
"Specified value has invalid Control characters. Parameter name: value"
I am actually doing this in a REST service.
public static string GetHash(string text)
{
string hash = "";
SHA512 alg = SHA512.Create();
byte[] result = alg.ComputeHash(Encoding.UTF8.GetBytes(text));
hash = Encoding.UTF8.GetString(result);
return hash;
}
What am I missing?
The problem is Encoding.UTF8.GetString(result) as the data in result is invalid UTF-8 (it's just binary goo!) so trying to convert it to text is invalid - in general, and specifically for this input - which results in the Exception being thrown.
Instead, convert the byte[] to the hex representation of said byte sequence; don't treat it as UTF-8 encoded text.
See the questions How do you convert Byte Array to Hexadecimal String, and vice versa? and How can I convert a hex string to a byte array?, which discuss several different methods of achieving this task.
In order to make this work you need to convert the individual byte elements into a hex representation
var builder = new StringBuilder();
foreach(var b in result) {
builder.AppendFormat("{0:X2}", b);
}
return builder.ToString();
You might want to consider using Base64 encoding (AKA UUEncode):
public static string GetHash(string text)
{
SHA512 alg = SHA512.Create();
byte[] result = alg.ComputeHash(Encoding.UTF8.GetBytes(text));
return Convert.ToBase64String(result);
}
For your example string, the result is
OJgzW5JdC1IMdVfC0dH98J8tIIlbUgkNtZLmOZsjg9H0wRmwd02tT0Bh/uTOw/Zs+sgaImQD3hh0MlzVbqWXZg==
It has an advantage of being more compact than encoding each byte into two characters: three bytes takes four characters with Base64 encoding or six characters the other way.

Hashing Query String containing Special Characters not working

I have posted few questions about Tokens and Password reset and have managed to finally figure this all out. Thanks everyone!
So before reading that certain characters will not work in a query string, I decided to hash the query string but as you've guessed, the plus signs are stripped out.
How do you secure or hash a query string?
This is a sample from a company email I received and the string looks like this:
AweVZe-LujIAuh8i9HiXMCNDIRXfSZYv14o4KX0KywJAGlLklGC1hSw-bJWCYfia-pkBbessPNKtQQ&t=pr&ifl
In my setup, I am simply using a GUID. But does it matter?
In my scenario the user cannot access the password page, even without a GIUD. That's because the page is set to redirect onload if the query string don't match the session variable?
Are there ways to handle query string to give the result like above?
This question is more about acquiring knowledge.
UPDATE:
Here is the Hash Code:
public static string QueryStringHash(string input)
{
byte[] inputBytes = Encoding.UTF8.GetBytes();
SHA512Managed sha512 = new SHA512Managed();
byte[] outputBytes = sha512.ComputeHash(inputBytes);
return Convert.ToBase64String(outputBytes);
}
Then I pass the HASH (UserID) to a SESSION before sending it as a query string:
On the next page, the Session HASH is not the same as the Query which cause the values not to match and rendered the query string invalid.
Note: I created a Class called Encryption that handles all the Hash and Encryption.
Session["QueryString"] = Encryption.QueryStringHash(UserID);
Response.Redirect("~/public/reset-password.aspx?uprl=" +
HttpUtility.UrlEncode(Session["QueryString"].ToString()));
I also tried everything mentioned on this page but no luck:
How do I replace all the spaces with %20 in C#
Thanks for reading.
The problem is that base64 encoding uses the '+' and '/' characters, which have special meaning in URLs. If you want to base64 encode query parameters, you have to change those characters. Typically, that's done by replacing the '+' and '/' with '-' and '_' (dash and underscore), respectively, as specified in RFC 4648.
In your code, then, you'd do this:
public static string QueryStringHash(string input)
{
byte[] inputBytes = Encoding.UTF8.GetBytes();
SHA512Managed sha512 = new SHA512Managed();
byte[] outputBytes = sha512.ComputeHash(inputBytes);
string b64 = Convert.ToBase64String(outputBytes);
b64 = b64.Replace('+', '-');
return b64.Replace('/', '_');
}
On the receiving end, of course, you'll need to replace the '-' and '_' with the corresponding '+' and '/' before calling the method to convert from base 64.
They recommend not using the pad character ('='), but if you do, it should be URL encoded. There's no need to communicate the pad character if you always know how long your encoded strings are. You can add the required pad characters on the receiving end. But if you can have variable length strings, then you'll need the pad character.
Any time you see base 64 encoding used in query parameters, this is how it's done. It's all over the place, perhaps most commonly in YouTube video IDs.
I did something before where I had to pass a hash in a query string. As you've experienced Base 64 can be pretty nasty when mixed with URLs so I decided to pass it as a hex string instead. Its a little longer, but much easier to deal with. Here is how I did it:
First a method to transform binary into a hex string.
private static string GetHexFromData(byte[] bytes)
{
var output = new StringBuilder();
foreach (var b in bytes)
{
output.Append(b.ToString("X2"));
}
return output.ToString();
}
Then a reverse to convert a hex string back to binary.
private static byte[] GetDataFromHex(string hex)
{
var bytes = new List<byte>();
for (int i = 0; i < hex.Length; i += 2)
{
bytes.Add((byte)int.Parse(hex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber));
}
return bytes.ToArray();
}
Alternatively if you just need to verify the hashes are the same, just convert both to hex strings and compare the strings (case-insensitive). hope this helps.

Why am i getting gibberish instead of hash value?

Here is my little helper method to get the hash value from a string. But i am getting some sort of strange text instead of hash value :
public static string GetHashValue(string sourceString)
{
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
return Encoding.UTF8.GetString(provider.ComputeHash(Encoding.UTF8.GetBytes(sourceString)));
}
I don't think the value i get from this function is really a hash value.
Thanks in advance :)
Using UTF8.GetString() doesn't make sense here, the hashing function doesn't return a string encoded in utf-8. It's just a byte[] with arbitrary byte values. Use Convert.ToBase64String() instead. It is still gobbledygook but it is supposed to be. It's hashed. You can roundtrip it back to byte[] with Convert.FromBase64String().
You may be expecting this to be displayed as a Hexidecimal string. That's a common representation, and would differ from what you're displaying now. For details, see the MSDN sample.
Try the following changes to your code (adapted from the above sample):
public static string GetHashValue(string sourceString)
{
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider()
byte[] hashData = provider.ComputeHash(Encoding.UTF8.GetBytes(sourceString));
int i;
StringBuilder sOutput = new StringBuilder(hashData.Length);
for (i=0; i < hashData.Length; ++i)
{
sOutput.Append(hashData[i].ToString("X2"));
}
return sOutput.ToString();
}

Odd way of implementing the MD5 function in C#

I was looking for extension methods and stumbled in to a function that claims to mimic how PHP does the MD5 functions. Here's an exact copy(I don't get why the declaration and initialization of the Crypto were done separately...)
public static string MD5(this string s) {
MD5CryptoServiceProvider provider;
provider = new MD5CryptoServiceProvider();
byte[] bytes = Encoding.UTF8.GetBytes(s);
StringBuilder builder = new StringBuilder();
bytes = provider.ComputeHash(bytes);
foreach(byte b in bytes) {
builder.Append(b.ToString("x2").ToLower());
}
return builder.ToString();
}
The MSDN library shows this as being the way to do it:
byte[] MD5hash (byte[] data) {
// This is one implementation of the abstract class MD5.
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(data);
return result;
}
Except for the argument being a string, what's the need for the stringbuilder and appending each byte? If I just do Encoding.UTF8.GetBytes on the string, I should have a byte array, usable for the method found in the MSDN library.
Can anyone point to a use for the extension method?
The first method returns the hexadecimal encoding of the result. The latter returns it has a byte[] without encoding it as hex in a string.
The former is more of a convenience method when returning the result as a string (perhaps in a URL, form, etc).
The whole stringbuilder thing is a way of converting the bytes into a hex string (e.g. convert 16 bytes into 32 chars). See How do you convert Byte Array to Hexadecimal String, and vice versa? for a variety of other ways to accomplish the same thing.

Categories