HELP -- ASCIIEncoding Problem

D

Dino Buljubasic

Hi,

I am using a function to hash a string value:

public string generateMD5Hash(string input)
{
MD5 md5Provider; // MD5 provider instance

// generate byte code for input
byte[] inputData = ASCIIEncoding.ASCII.GetBytes(input);

// compute MD5 hash
md5Provider = new MD5CryptoServiceProvider();
byte[] hashResult = md5Provider.ComputeHash(inputData);

return ASCIIEncoding.ASCII.GetString(hashResult);
}

The last return statement (ASCIIEncoding...) returns different values
if used in .NET Framework 1.1 (or 1.0) and .NET Framework 2.0. That
means that my .NET Framework 2.0 version of application (written in
C#) will not execute sam as writen in .NET Framework 1.1.

Did something changed? How to fix this?

any help will be appreciated,
_dino_
 
J

Jon Skeet [C# MVP]

Dino Buljubasic said:
I am using a function to hash a string value:

public string generateMD5Hash(string input)
{
MD5 md5Provider; // MD5 provider instance

// generate byte code for input
byte[] inputData = ASCIIEncoding.ASCII.GetBytes(input);

// compute MD5 hash
md5Provider = new MD5CryptoServiceProvider();
byte[] hashResult = md5Provider.ComputeHash(inputData);

return ASCIIEncoding.ASCII.GetString(hashResult);
}

The last return statement (ASCIIEncoding...) returns different values
if used in .NET Framework 1.1 (or 1.0) and .NET Framework 2.0. That
means that my .NET Framework 2.0 version of application (written in
C#) will not execute sam as writen in .NET Framework 1.1.

Did something changed? How to fix this?

I suspect it is only returning a different value when the byte is
greater than 127. To be honest, I think that's reasonable, as the
behaviour isn't well-defined in that situation, as ASCII doesn't
contain any values greater than 127.

For those interested, here's a sample which demonstrates the "problem":

using System;
using System.Text;

public class Test
{
static void Main()
{
string x = Encoding.ASCII.GetString (new byte[]{128});

Console.WriteLine ((int)x[0]);
}
}

Now, as to how you should fix it:
1) Encode the input string with UTF-8 instead of ASCII. That means you
won't lose data when the input contains non-ASCII characters. (In this
case as you're just taking an MD5 hash, it just means your hash is
weaker than it should be. However, it's a good idea to try not to use
an ASCII encoding when the data might contain non-ASCII characters on
principle.

2) Encode the resulting binary data using Base64 -
Convert.ToBase64String is the easiest method here.

Here's the changed method:

public string generateMD5Hash(string input)
{
// generate byte code for input
byte[] inputData = Encoding.UTF8.GetBytes(input);

// compute MD5 hash
MD5 md5Provider = new MD5CryptoServiceProvider();
byte[] hashResult = md5Provider.ComputeHash(inputData);

return Convert.ToBase64String(hashResult);
}


Now, if you've got old values which need to be matched, you'll have to
mimic the old behaviour instead, which is slightly trickier. I won't do
that now, because you might not need it - let me know if you do, and
I'll see what I can do.
 
D

Dino Buljubasic

Hi,

Thank you for your reply. I haven't had time to look at your example
but you are right, it was returning different values. I did some
research on internet and found a post from a guy whose name I
unfortunatelly don't remember anymore (my appologies). Anyways, hHere
is how I solved it:

public string generateMD5Hash(string input)
{
MD5 md5Provider; // MD5 provider instance

// generate byte code for input
byte[] inputData = ASCIIEncoding.ASCII.GetBytes(input);

// compute MD5 hash
md5Provider = new MD5CryptoServiceProvider();
byte[] hashResult = md5Provider.ComputeHash(inputData);
byte[] fixedByteArray = new byte[hashResult.Length];
for (int i = 0; i < fixedByteArray.Length; i++)
{
fixedByteArray = (byte)((int)hashResult & 127);
}
string hashedPassword =
ASCIIEncoding.ASCII.GetString(fixedByteArray);
//return ASCIIEncoding.ASCII.GetString(hashResult);
return hashedPassword;
}

It seam to be working right. Let me know if you have any suggestions
please.

I appreciate your help,
_dino_

Dino Buljubasic said:
I am using a function to hash a string value:

public string generateMD5Hash(string input)
{
MD5 md5Provider; // MD5 provider instance

// generate byte code for input
byte[] inputData = ASCIIEncoding.ASCII.GetBytes(input);

// compute MD5 hash
md5Provider = new MD5CryptoServiceProvider();
byte[] hashResult = md5Provider.ComputeHash(inputData);

return ASCIIEncoding.ASCII.GetString(hashResult);
}

The last return statement (ASCIIEncoding...) returns different values
if used in .NET Framework 1.1 (or 1.0) and .NET Framework 2.0. That
means that my .NET Framework 2.0 version of application (written in
C#) will not execute sam as writen in .NET Framework 1.1.

Did something changed? How to fix this?

I suspect it is only returning a different value when the byte is
greater than 127. To be honest, I think that's reasonable, as the
behaviour isn't well-defined in that situation, as ASCII doesn't
contain any values greater than 127.

For those interested, here's a sample which demonstrates the "problem":

using System;
using System.Text;

public class Test
{
static void Main()
{
string x = Encoding.ASCII.GetString (new byte[]{128});

Console.WriteLine ((int)x[0]);
}
}

Now, as to how you should fix it:
1) Encode the input string with UTF-8 instead of ASCII. That means you
won't lose data when the input contains non-ASCII characters. (In this
case as you're just taking an MD5 hash, it just means your hash is
weaker than it should be. However, it's a good idea to try not to use
an ASCII encoding when the data might contain non-ASCII characters on
principle.

2) Encode the resulting binary data using Base64 -
Convert.ToBase64String is the easiest method here.

Here's the changed method:

public string generateMD5Hash(string input)
{
// generate byte code for input
byte[] inputData = Encoding.UTF8.GetBytes(input);

// compute MD5 hash
MD5 md5Provider = new MD5CryptoServiceProvider();
byte[] hashResult = md5Provider.ComputeHash(inputData);

return Convert.ToBase64String(hashResult);
}


Now, if you've got old values which need to be matched, you'll have to
mimic the old behaviour instead, which is slightly trickier. I won't do
that now, because you might not need it - let me know if you do, and
I'll see what I can do.
 
J

Jon Skeet [C# MVP]

Dino Buljubasic said:
Thank you for your reply. I haven't had time to look at your example
but you are right, it was returning different values. I did some
research on internet and found a post from a guy whose name I
unfortunatelly don't remember anymore (my appologies). Anyways, hHere
is how I solved it:

public string generateMD5Hash(string input)
{
MD5 md5Provider; // MD5 provider instance

// generate byte code for input
byte[] inputData = ASCIIEncoding.ASCII.GetBytes(input);

// compute MD5 hash
md5Provider = new MD5CryptoServiceProvider();
byte[] hashResult = md5Provider.ComputeHash(inputData);
byte[] fixedByteArray = new byte[hashResult.Length];
for (int i = 0; i < fixedByteArray.Length; i++)
{
fixedByteArray = (byte)((int)hashResult & 127);
}
string hashedPassword =
ASCIIEncoding.ASCII.GetString(fixedByteArray);
//return ASCIIEncoding.ASCII.GetString(hashResult);
return hashedPassword;
}

It seam to be working right. Let me know if you have any suggestions
please.


Do you know that input will always be ASCII? If not, you may still get
different results if ASCIIEncoding has changed behaviour in terms of
GetBytes as well as GetString.

Also, you don't need to create a new byte array - you can just do:

for (int i = 0; i < hashResult.Length; i++)
{
hashResult = (byte)(hashResult & 127);
}

This is what's needed for backwards compatibility with the previous
behaviour - if you ever get a chance to change to the code I suggested,
it would be a more sensible long-term solution IMO.
 
D

Dino Buljubasic

Thank you Jon, ... yes my input will always be ascii.

I will try to get back to it today and change it.

I appreciate your help
_dino_

Dino Buljubasic said:
Thank you for your reply. I haven't had time to look at your example
but you are right, it was returning different values. I did some
research on internet and found a post from a guy whose name I
unfortunatelly don't remember anymore (my appologies). Anyways, hHere
is how I solved it:

public string generateMD5Hash(string input)
{
MD5 md5Provider; // MD5 provider instance

// generate byte code for input
byte[] inputData = ASCIIEncoding.ASCII.GetBytes(input);

// compute MD5 hash
md5Provider = new MD5CryptoServiceProvider();
byte[] hashResult = md5Provider.ComputeHash(inputData);
byte[] fixedByteArray = new byte[hashResult.Length];
for (int i = 0; i < fixedByteArray.Length; i++)
{
fixedByteArray = (byte)((int)hashResult & 127);
}
string hashedPassword =
ASCIIEncoding.ASCII.GetString(fixedByteArray);
//return ASCIIEncoding.ASCII.GetString(hashResult);
return hashedPassword;
}

It seam to be working right. Let me know if you have any suggestions
please.


Do you know that input will always be ASCII? If not, you may still get
different results if ASCIIEncoding has changed behaviour in terms of
GetBytes as well as GetString.

Also, you don't need to create a new byte array - you can just do:

for (int i = 0; i < hashResult.Length; i++)
{
hashResult = (byte)(hashResult & 127);
}

This is what's needed for backwards compatibility with the previous
behaviour - if you ever get a chance to change to the code I suggested,
it would be a more sensible long-term solution IMO.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top