Computing md5 hash - c#

I am computing md5hash of files to check if identical so I wrote the following
private static byte[] GetMD5(string p)
{
FileStream fs = new FileStream(p, FileMode.Open);
HashAlgorithm alg = new HMACMD5();
byte[] hashValue = alg.ComputeHash(fs);
fs.Close();
return hashValue;
}
and to test if for the beginning I called it like
var v1 = GetMD5("C:\\test.mp4");
var v2 = GetMD5("C:\\test.mp4");
and from debugger I listed v1 and v2 values and they are different !! why is that ?

It's because you're using HMACMD5, a keyed hashing algorithm, which combines a key with the input to produce a hash value. When you create an HMACMD5 via it's default constructor, it will use a random key each time, therefore the hashes will always be different.
You need to use MD5:
private static byte[] GetMD5(string p)
{
using(var fs = new FileStream(p, FileMode.Open))
{
using(var alg = new MD5CryptoServiceProvider())
{
return alg.ComputeHash(fs);
}
}
}
I've changed the code to use usings as well.

From the HMACMD5 constructor doc:
HMACMD5 is a type of keyed hash algorithm that is constructed from the
MD5 hash function and used as a Hash-based Message Authentication Code
(HMAC). The HMAC process mixes a secret key with the message data,
hashes the result with the hash function, mixes that hash value with
the secret key again, and then applies the hash function a second
time. The output hash will be 128 bits in length.
With this constructor, a 64-byte, randomly generated key is used.
(Emphasis mine)
With every call to GetMD5(), you're generating a new random key.
You might want to use System.Security.Cryptography.MD5Cng

My guess is that you did something like:
Console.WriteLine(v1);
Console.WriteLine(v2);
or
Console.WriteLine(v1 == v2);
That just shows that the variable values refer to distinct arrays - it doesn't say anything about the values within those arrays.
Instead, try this (to print out the hex):
Console.WriteLine(BitConverter.ToString(v1));
Console.WriteLine(BitConverter.ToString(v2))

Use ToString() methode to get the value of the array byte

Related

Convert encrypt/decrypt from Java to C# using bouncycastle

I'm trying to convert a couple of function from Java to c# using Portable.BouncyCastle and while there are plenty of example out there, I don't seem to be able to find one to match my requirements as most example seem to explain one specific encryption/decryption method while this function appears to be more generic. I could be wrong of course as I'm a complete newbie to this and don't have any experience in either BouncyCastle, Java or encryption, so please bear with me on this one.
The java function is:
public static byte[] Cipher(int mode, byte[] key,
byte[] data, string algorithm, AlgorithmParameterSpec spec)
{
Cipher cipher = Cipher.getInstance(algorithm);
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
if (spec != null)
cipher.init(mode, keySpec, spec);
else
cipher.init(mode, keySpec);
return cipher.doFinal(data);
}
I found some code from BouncyCasle where I can match most of the functionality from what I can see:
byte[] K = Hex.Decode("404142434445464748494a4b4c4d4e4f");
byte[] N = Hex.Decode("10111213141516");
byte[] P = Hex.Decode("68656c6c6f20776f726c642121");
byte[] C = Hex.Decode("39264f148b54c456035de0a531c8344f46db12b388");
KeyParameter key = ParameterUtilities.CreateKeyParameter("AES", K);
IBufferedCipher inCipher = CipherUtilities.
GetCipher("AES/CCM/NoPadding");
inCipher.Init(true, new ParametersWithIV(key, N));
byte[] enc = inCipher.DoFinal(P);
1. SecretKeySpec:
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
How do I create this using BC? Is that the equivalent of the SecretKeySpec:
KeyParameter key = ParameterUtilities.CreateKeyParameter("AES", K);
If it is, can I pass the "AES/CCM/NoPadding" instead of AES as it is done in Java?
2. spec parameter:
It passes parameters of type IvParameterSpec to the Cypher function when called from `Java` via the `AlgorithmParameterSpec spec` parameter:
Cipher(ENCRYPT_MODE, key, clearBytes,
algorithm,
new IvParameterSpec(iv))
`BouncyCastle` does not have an overloaded function for `.Init` to allow me to pass the spec parameter as it does in `Java`, so how do I mimic this behaviour?
3. IvParameterSpec: You can see that when cypher is called from java, it passes the AlgorithmParameterSpec spec as new IvParameterSpec(iv) but with BouncyCastle, it seems to be expecting a key?
ParametersWithIV(key, N)
Will that difference have any impact on the encryption/decryption?
This is current my attempt at "converting" this function:
public static byte[] Cipher(bool isEncrypt, byte[] key, byte[] data,
string algorithm, ICipherParameters spec)
{
IBufferedCipher cipher = CipherUtilities.GetCipher(algorithm);
KeyParameter keySpec = ParameterUtilities.
CreateKeyParameter(algorithm, key);
cipher.Init(isEncrypt, new ParametersWithIV(keySpec,
keySpec.GetKey()));
return cipher.DoFinal(data);
}
As you can see I've changed the spec parameter to ICipherParameters spec but I don't know if this will work as when using Bouncy, it looks like that when I create a new ParametersWithIV, it needs a key and from the test sample I provided above, that key is created using KeyParameter key = ParameterUtilities.CreateKeyParameter("AES", K); so technically won't work when trying to call my Cipher function as I will have called this function yet. Should I change spec parameter to iv and pass a byte[] instead?
Apologies if there is confusion or if things are not explained correctly but as I said, I'm new to this and trying to understand it better while also converting. I hope most of it makes sense and you'll be able to help.
Many Thanks.
PS: Note that I'm not in a position to test these in Java yet, but I will hopefully have an environment set up correctly in the new few days which will hopefully help testing values between .net & java.
UPDATE 1
Passing AES/CCM/NoPadding to:
KeyParameter key = ParameterUtilities.CreateKeyParameter
Throws an error, so this partially answers one of my questions. Is there a function in BouncyCastle to determine the correct value that is required i.e. AES when AES/CCM/NoPadding is passed?
Ended up using the code below as it appears to be working as expected but still annoyed I had to hardcode the AES as the key of IV parameter part. Ideally I would have liked this to have been based on my Algorithm. So, now I have a single function to encrypt and decrypt:
public static byte[] Cipher(bool forEncryption, byte[] key,
byte[] data, string algorithm, byte[] iv)
{
IBufferedCipher cipher = CipherUtilities.GetCipher(algorithm);
KeyParameter keySpec = ParameterUtilities.CreateKeyParameter("AES", key);
cipher.Init(forEncryption, new ParametersWithIV(keySpec, iv));
return cipher.DoFinal(data);
}
Hope this helps.

xxHash convert resulting in hash too long

I'm using xxHash for C# to hash a value for consistency.
ComputeHash returns a byte[], but I need to store the results in a long.
I'm able to convert the results into an int32 using the BitConverter. Here is what I've tried:
var xxHash = new System.Data.HashFunction.xxHash();
byte[] hashedValue = xxHash.ComputeHash(Encoding.UTF8.GetBytes(valueItem));
long value = BitConverter.ToInt64(hashedValue, 0);
When I use int this works fine, but when I change to ToInt64 it fails.
Here's the exception I get:
Destination array is not long enough to copy all the items in the collection. Check array index and length.
When you construct your xxHash object, you need to supply a hashsize:
var hasher = new xxHash(32);
valid hash sizes are 32 and 64.
See https://github.com/brandondahler/Data.HashFunction/blob/master/src/xxHash/xxHash.cs for the source.
Adding a new answer because current implementation of xxHash from Brandon Dahler uses a hashing factory where you initialize the factory with a configuration containing hashsize and seed:
using System.Data.HashFunction.xxHash;
//can also set seed here, (ulong) Seed=234567
xxHashConfig config = new xxHashConfig() { HashSizeInBits = 64 };
var factory = xxHashFactory.Instance.Create(config);
byte[] hashedValue = factory.ComputeHash(Encoding.UTF8.GetBytes(valueItem)).Hash;
BitConverter.ToInt64 expects hashedValue to have 8 bytes (= 64bits). You could manually extend, and then pass it.

Generating 64 Chars key for API

I want to generate a API key for new clients that want to use any of my API services.
because I'm using a open API service i don't want to use authentication only identify the client usage by the API key
I tried to use this code
public static string GetAPIKey()
{
string sig = string.Empty;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
var ex = rsa.ExportParameters(true);
sig = Convert.ToBase64String(ex.DQ);
sig = sig
.Replace("+", "")
.Replace("/", "")
.TrimEnd('=');
}
return sig.Substring(0, 64);
}
In my tests i do get a random 64 length string, but something not feeling right with the method usage. proberly because of the RSACryptoServiceProvider usage, especially when i try to generate the DQ property
Do you know any better implementation of generating a random 64 string?
If you do not want to use a GUID you can use the standard Random class in C#.
private static readonly Random Random = new Random();
public static string GetApiKey()
{
var bytes = new byte[48];
Random.NextBytes(bytes);
var result = Convert.ToBase64String(bytes);
return result;
}
Since 48 bytes will map to 64 characters in Base64, this gives you 64 random characters. It does not guarantee uniqueness however.
Why not just use a GUID?
Use it twice to generate a 64 character string, which is completely random and unique.

Weird behaviour of MD5 hashing

I have faced aweird problem with the following code, the code below suppose to stop after one iteration, but it just keep going. However, if I remove the last "result_bytes = md5.ComputeHash(orig_bytes);" then it will work. Does anyone face similar problem before?
MD5 md5;
byte[] orig_bytes;
byte[] result_bytes;
Dictionary<byte[], string> hashes = new Dictionary<byte[], string>();
string input = "NEW YORK";
result_bytes = UnicodeEncoding.Default.GetBytes("HELLO");
while (!hashes.ContainsKey(result_bytes))
{
md5 = new MD5CryptoServiceProvider();
orig_bytes = UnicodeEncoding.Default.GetBytes(input);
result_bytes = md5.ComputeHash(orig_bytes);
hashes.Add(result_bytes, input);
Console.WriteLine(BitConverter.ToString(result_bytes));
Console.WriteLine(hashes.ContainsKey(result_bytes));
result_bytes = md5.ComputeHash(orig_bytes);
}
When you reassign result_bytes to a new value in the last line, you have a new reference to a byte array, which is not equal to the one in the collection, therefore hashes.ContainsKey returns false.
You're assuming that byte arrays override Equals and GetHashCode to compare for equality: they don't. They just use the default identity test - so without the extra assignment at the end, you're just checking whether the exact key object you've just added is still in the dictionary - which of course it is.
One way round this would be to store a reversible string representation of the hash (e.g. using base64), instead of the hash itself. Or write your own implementation of IEqualityComparer<byte[]> and pass that to the Dictionary constructor, so that it uses that implementation to find the hash code of byte arrays and compare them with each other.
In short: this has nothing to do with MD5, and everything to do with the fact that
Console.WriteLine(new byte[0].Equals(new byte[0]));
will print False :)

C# MD5 Hash results not expected result

I've tried every example I can find on the web but I cannot get my .NET code to produce the same MD5 Hash results from my VB6 app.
The VB6 app produces identical results to this site:
http://www.functions-online.com/md5.html
But I cannot get the same results for the same input in C# (using either the MD5.ComputeHash method or the FormsAuthentication encryption method)
Please help!!!!
As requested, here is some code. This is pulled straight from MSDN:
public string hashString(string input)
{
// Create a new instance of the MD5CryptoServiceProvider object.
MD5 md5Hasher = MD5.Create();
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
My test string is:
QWERTY123TEST
The results from this code is:
8c31a947080131edeaf847eb7c6fcad5
The result from Test MD5 is:
f6ef5dc04609664c2875895d7da34eb9
Note: The result from the TestMD5 is what I am expecting
Note: I've been really, really stupid, sorry - just realised I had the wrong input. As soon as I hard-coded it, it worked. Thanks for the help
This is a C# MD5 method that i know works, i have used it to authenticate via different web restful APIs
public static string GetMD5Hash(string input)
{
System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] bs = System.Text.Encoding.UTF8.GetBytes(input);
bs = x.ComputeHash(bs);
System.Text.StringBuilder s = new System.Text.StringBuilder();
foreach (byte b in bs)
{
s.Append(b.ToString("x2").ToLower());
}
return s.ToString();
}
What makes the "functions-online" site (http://www.functions-online.com/md5.html) an authority on MD5? For me, it works OK only for ISO-8859-1. But when I try pasting anything other than ISO-8859-1 into it, it returns the same MD5 hash. Try Cyrillic capital B by itself, code point 0x412. Or try Han Chinese symbol for water, code point 0x98A8.
As far as I know, the posted C# applet is correct.

Categories

Resources