//Copyright (c) Microsoft Corporation. All rights reserved.<br />using System;<br />using System.Text;<br />// **************************************************************<br />// * Raw implementation of the MD5 hash algorithm<br />// * from RFC 1321.<br />// *<br />// * Written By: Reid Borsuk and Jenny Zheng<br />// * Copyright (c) Microsoft Corporation. All rights reserved.<br />// **************************************************************<br />// Simple struct for the (a,b,c,d) which is used to compute the mesage digest.<br />struct ABCDStruct<br />{<br /> public uint A;<br /> public uint B;<br /> public uint C;<br /> public uint D;<br />}<br />public sealed class MD5Core<br />{<br /> //Prevent CSC from adding a default public constructor<br /> private MD5Core() {}<br /> public static byte[] GetHash(string input, Encoding encoding)<br /> {<br /> if (null == input)<br /> throw new System.ArgumentNullException("input", "Unable to calculate hash over null input data");<br /> if (null == encoding)<br /> throw new System.ArgumentNullException("encoding", "Unable to calculate hash over a string without a default encoding. Consider using the GetHash(string) overload to use UTF8 Encoding");<br /> byte[] target = encoding.GetBytes(input);<br /> return GetHash(target);<br /> }<br /> public static byte[] GetHash(string input)<br /> {<br /> return GetHash(input, new UTF8Encoding());<br /> }<br /> public static string GetHashString(byte[] input)<br /> {<br /> if (null == input)<br /> throw new System.ArgumentNullException("input", "Unable to calculate hash over null input data");<br /> string retval = BitConverter.ToString(GetHash(input));<br /> retval = retval.Replace("-", "");<br /> return retval;<br /> }<br /> public static string GetHashString(string input, Encoding encoding)<br /> {<br /> if (null == input)<br /> throw new System.ArgumentNullException("input", "Unable to calculate hash over null input data");<br /> if (null == encoding)<br /> throw new System.ArgumentNullException("encoding", "Unable to calculate hash over a string without a default encoding. Consider using the GetHashString(string) overload to use UTF8 Encoding");</p><p> byte[] target = encoding.GetBytes(input);<br /> return GetHashString(target);<br /> }<br /> public static string GetHashString(string input)<br /> {<br /> return GetHashString(input, new UTF8Encoding());<br /> }<br /> public static byte[] GetHash(byte[] input)<br /> {<br /> if (null == input)<br /> throw new System.ArgumentNullException("input", "Unable to calculate hash over null input data");<br /> //Intitial values defined in RFC 1321<br /> ABCDStruct abcd = new ABCDStruct();<br /> abcd.A = 0x67452301;<br /> abcd.B = 0xefcdab89;<br /> abcd.C = 0x98badcfe;<br /> abcd.D = 0x10325476;<br /> //We pass in the input array by block, the final block of data must be handled specialy for padding & length embeding<br /> int startIndex = 0;<br /> while (startIndex <= input.Length - 64)<br /> {<br /> MD5Core.GetHashBlock(input, ref abcd, startIndex);<br /> startIndex += 64;<br /> }<br /> // The final data block.<br /> return MD5Core.GetHashFinalBlock(input, startIndex, input.Length - startIndex, abcd, (Int64)input.Length * 8);<br /> }<br /> internal static byte[] GetHashFinalBlock(byte[] input, int ibStart, int cbSize, ABCDStruct ABCD, Int64 len)<br /> {<br /> byte[] working = new byte[64];<br /> byte[] length = BitConverter.GetBytes(len);<br /> //Padding is a single bit 1, followed by the number of 0s required to make size congruent to 448 modulo 512. Step 1 of RFC 1321<br /> //The CLR ensures that our buffer is 0-assigned, we don't need to explicitly set it. This is why it ends up being quicker to just<br /> //use a temporary array rather then doing in-place assignment (5% for small inputs)<br /> Array.Copy(input, ibStart, working, 0, cbSize);<br /> working[cbSize] = 0x80;<br /> //We have enough room to store the length in this chunk<br /> if (cbSize <= 56)<br /> {<br /> Array.Copy(length, 0, working, 56, 8);<br /> GetHashBlock(working, ref ABCD, 0);<br /> }<br /> else //We need an aditional chunk to store the length<br /> {<br /> GetHashBlock(working, ref ABCD, 0);<br /> //Create an entirely new chunk due to the 0-assigned trick mentioned above, to avoid an extra function call clearing the array<br /> working = new byte[64];<br /> Array.Copy(length, 0, working, 56, 8);<br /> GetHashBlock(working, ref ABCD, 0);<br /> }<br /> byte[] output = new byte[16];<br /> Array.Copy(BitConverter.GetBytes(ABCD.A), 0, output, 0, 4);<br /> Array.Copy(BitConverter.GetBytes(ABCD.B), 0, output, 4, 4);<br /> Array.Copy(BitConverter.GetBytes(ABCD.C), 0, output, 8, 4);<br /> Array.Copy(BitConverter.GetBytes(ABCD.D), 0, output, 12, 4);<br /> return output;<br /> }<br /> // Performs a single block transform of MD5 for a given set of ABCD inputs<br /> /* If implementing your own hashing framework, be sure to set the initial ABCD correctly according to RFC 1321:<br /> // A = 0x67452301;<br /> // B = 0xefcdab89;<br /> // C = 0x98badcfe;<br /> // D = 0x10325476;<br /> */<br /> internal static void GetHashBlock(byte[] input, ref ABCDStruct ABCDValue, int ibStart)<br /> {<br /> uint[] temp = Converter(input, ibStart);<br /> uint a = ABCDValue.A;<br /> uint b = ABCDValue.B;<br /> uint c = ABCDValue.C;<br /> uint d = ABCDValue.D;<br /> a = r1(a, b, c, d, temp[0 ], 7, 0xd76aa478);<br /> d = r1(d, a, b, c, temp[1 ], 12, 0xe8c7b756);<br /> c = r1(c, d, a, b, temp[2 ], 17, 0x242070db);<br /> b = r1(b, c, d, a, temp[3 ], 22, 0xc1bdceee);<br /> a = r1(a, b, c, d, temp[4 ], 7, 0xf57c0faf);<br /> d = r1(d, a, b, c, temp[5 ], 12, 0x4787c62a);<br /> c = r1(c, d, a, b, temp[6 ], 17, 0xa8304613);<br /> b = r1(b, c, d, a, temp[7 ], 22, 0xfd469501);<br /> a = r1(a, b, c, d, temp[8 ], 7, 0x698098d8);<br /> d = r1(d, a, b, c, temp[9 ], 12, 0x8b44f7af);<br /> c = r1(c, d, a, b, temp[10], 17, 0xffff5bb1);<br /> b = r1(b, c, d, a, temp[11], 22, 0x895cd7be);<br /> a = r1(a, b, c, d, temp[12], 7, 0x6b901122);<br /> d = r1(d, a, b, c, temp[13], 12, 0xfd987193);<br /> c = r1(c, d, a, b, temp[14], 17, 0xa679438e);<br /> b = r1(b, c, d, a, temp[15], 22, 0x49b40821);<br /> a = r2(a, b, c, d, temp[1 ], 5, 0xf61e2562);<br /> d = r2(d, a, b, c, temp[6 ], 9, 0xc040b340);<br /> c = r2(c, d, a, b, temp[11], 14, 0x265e5a51);<br /> b = r2(b, c, d, a, temp[0 ], 20, 0xe9b6c7aa);<br /> a = r2(a, b, c, d, temp[5 ], 5, 0xd62f105d);<br /> d = r2(d, a, b, c, temp[10], 9, 0x02441453);<br /> c = r2(c, d, a, b, temp[15], 14, 0xd8a1e681);<br /> b = r2(b, c, d, a, temp[4 ], 20, 0xe7d3fbc8);<br /> a = r2(a, b, c, d, temp[9 ], 5, 0x21e1cde6);<br /> d = r2(d, a, b, c, temp[14], 9, 0xc33707d6);<br /> c = r2(c, d, a, b, temp[3 ], 14, 0xf4d50d87);<br /> b = r2(b, c, d, a, temp[8 ], 20, 0x455a14ed);<br /> a = r2(a, b, c, d, temp[13], 5, 0xa9e3e905);<br /> d = r2(d, a, b, c, temp[2 ], 9, 0xfcefa3f8);<br /> c = r2(c, d, a, b, temp[7 ], 14, 0x676f02d9);<br /> b = r2(b, c, d, a, temp[12], 20, 0x8d2a4c8a);<br /> a = r3(a, b, c, d, temp[5 ], 4, 0xfffa3942);<br /> d = r3(d, a, b, c, temp[8 ], 11, 0x8771f681);<br /> c = r3(c, d, a, b, temp[11], 16, 0x6d9d6122);<br /> b = r3(b, c, d, a, temp[14], 23, 0xfde5380c);<br /> a = r3(a, b, c, d, temp[1 ], 4, 0xa4beea44);<br /> d = r3(d, a, b, c, temp[4 ], 11, 0x4bdecfa9);<br /> c = r3(c, d, a, b, temp[7 ], 16, 0xf6bb4b60);<br /> b = r3(b, c, d, a, temp[10], 23, 0xbebfbc70);<br /> a = r3(a, b, c, d, temp[13], 4, 0x289b7ec6);<br /> d = r3(d, a, b, c, temp[0 ], 11, 0xeaa127fa);<br /> c = r3(c, d, a, b, temp[3 ], 16, 0xd4ef3085);<br /> b = r3(b, c, d, a, temp[6 ], 23, 0x04881d05);<br /> a = r3(a, b, c, d, temp[9 ], 4, 0xd9d4d039);<br /> d = r3(d, a, b, c, temp[12], 11, 0xe6db99e5);<br /> c = r3(c, d, a, b, temp[15], 16, 0x1fa27cf8);<br /> b = r3(b, c, d, a, temp[2 ], 23, 0xc4ac5665);<br /> a = r4(a, b, c, d, temp[0 ], 6, 0xf4292244);<br /> d = r4(d, a, b, c, temp[7 ], 10, 0x432aff97);<br /> c = r4(c, d, a, b, temp[14], 15, 0xab9423a7);<br /> b = r4(b, c, d, a, temp[5 ], 21, 0xfc93a039);<br /> a = r4(a, b, c, d, temp[12], 6, 0x655b59c3);<br /> d = r4(d, a, b, c, temp[3 ], 10, 0x8f0ccc92);<br /> c = r4(c, d, a, b, temp[10], 15, 0xffeff47d);<br /> b = r4(b, c, d, a, temp[1 ], 21, 0x85845dd1);<br /> a = r4(a, b, c, d, temp[8 ], 6, 0x6fa87e4f);<br /> d = r4(d, a, b, c, temp[15], 10, 0xfe2ce6e0);<br /> c = r4(c, d, a, b, temp[6 ], 15, 0xa3014314);<br /> b = r4(b, c, d, a, temp[13], 21, 0x4e0811a1);<br /> a = r4(a, b, c, d, temp[4 ], 6, 0xf7537e82);<br /> d = r4(d, a, b, c, temp[11], 10, 0xbd3af235);<br /> c = r4(c, d, a, b, temp[2 ], 15, 0x2ad7d2bb);<br /> b = r4(b, c, d, a, temp[9 ], 21, 0xeb86d391);<br /> ABCDValue.A = unchecked(a + ABCDValue.A);<br /> ABCDValue.B = unchecked(b + ABCDValue.B);<br /> ABCDValue.C = unchecked(c + ABCDValue.C);<br /> ABCDValue.D = unchecked(d + ABCDValue.D);<br /> return;<br /> }<br /> //Manually unrolling these equations nets us a 20% performance improvement<br /> private static uint r1(uint a, uint b, uint c, uint d, uint x, int s, uint t)<br /> {<br /> // (b + LSR((a + F(b, c, d) + x + t), s))<br /> //F(x, y, z) ((x & y) | ((x ^ 0xFFFFFFFF) & z))<br /> return unchecked(b + LSR((a + ((b & c) | ((b ^ 0xFFFFFFFF) & d)) + x + t), s));<br /> }<br /> private static uint r2(uint a, uint b, uint c, uint d, uint x, int s, uint t)<br /> {<br /> // (b + LSR((a + G(b, c, d) + x + t), s))<br /> //G(x, y, z) ((x & z) | (y & (z ^ 0xFFFFFFFF)))<br /> return unchecked(b + LSR((a + ((b & d) | (c & (d ^ 0xFFFFFFFF))) + x + t), s));<br /> }<br /> private static uint r3(uint a, uint b, uint c, uint d, uint x, int s, uint t)<br /> {<br /> // (b + LSR((a + H(b, c, d) + k + i), s))<br /> //H(x, y, z) (x ^ y ^ z)<br /> return unchecked(b + LSR((a + (b ^ c ^ d) + x + t), s));<br /> }<br /> private static uint r4(uint a, uint b, uint c, uint d, uint x, int s, uint t)<br /> {<br /> // (b + LSR((a + I(b, c, d) + k + i), s))<br /> //I(x, y, z) (y ^ (x | (z ^ 0xFFFFFFFF)))<br /> return unchecked(b + LSR((a + (c ^ (b | (d ^ 0xFFFFFFFF))) + x + t), s));<br /> }<br /> // Implementation of left rotate<br /> // s is an int instead of a uint becuase the CLR requires the argument passed to >>/<< is of<br /> // type int. Doing the demoting inside this function would add overhead.<br /> private static uint LSR(uint i, int s)<br /> {<br /> return ((i << s) | (i >> (32-s)));<br /> }<br /> //Convert input array into array of UInts<br /> private static uint[] Converter(byte[] input, int ibStart)<br /> {<br /> if(null == input)<br /> throw new System.ArgumentNullException("input", "Unable convert null array to array of uInts");</p><p> uint[] result = new uint[16];</p><p> for (int i = 0; i < 16; i++)<br /> {<br /> result[i] = (uint)input[ibStart + i * 4];<br /> result[i] += (uint)input[ibStart + i * 4 + 1] << 8;<br /> result[i] += (uint)input[ibStart + i * 4 + 2] << 16;<br /> result[i] += (uint)input[ibStart + i * 4 + 3] << 24;<br /> }</p><p> return result;<br /> }<br />}
調用: BitConverter.ToString(MD5Core.GetHash(key))