Created
January 16, 2012 20:11
-
-
Save amrishodiq/1622737 to your computer and use it in GitHub Desktop.
Triple DES encryption and MD5 for Windows Phone 7 with C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Net; | |
using System.Windows; | |
using System.Windows.Controls; | |
using System.Windows.Documents; | |
using System.Windows.Ink; | |
using System.Windows.Input; | |
using System.Windows.Media; | |
using System.Windows.Media.Animation; | |
using System.Windows.Shapes; | |
using System.Text; | |
namespace PhoneApp1 | |
{ | |
public class CryptoUtilities | |
{ | |
public static string GetMD5Hash(string input) | |
{ | |
byte[] bs = System.Text.Encoding.UTF8.GetBytes(input); | |
MD5Managed md5 = new MD5Managed(); | |
byte[] hash = md5.ComputeHash(bs); | |
StringBuilder sb = new StringBuilder(); | |
foreach (byte b in hash) | |
{ | |
sb.Append(b.ToString("x2").ToLower()); | |
} | |
return sb.ToString(); | |
} | |
private static System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); | |
public static string EncryptString(string Value, string KeyString) | |
{ | |
byte[] Key = DurianBerryProducts.DESCrytography.DoPadWithString(encoding.GetBytes(KeyString), 24, (byte)0); | |
byte[] plainText = new byte[1024]; | |
plainText = DurianBerryProducts.DESCrytography.DoPadWithString(encoding.GetBytes(Value), 8, (byte)0); | |
byte[] cipherText = null; | |
DurianBerryProducts.DESCrytography.TripleDES(plainText, ref cipherText, Key, true); | |
string result = Convert.ToBase64String(cipherText); | |
result = result.Replace("+", "-").Replace("/", "_"); | |
return result; | |
} | |
public static string DecryptString(string Value, string KeyString) | |
{ | |
byte[] Key = DurianBerryProducts.DESCrytography.DoPadWithString(encoding.GetBytes(KeyString), 24, (byte)0); | |
byte[] plainText = Convert.FromBase64String(Value.Replace("-", "+").Replace("_", "/")); | |
byte[] cipherText = null; | |
DurianBerryProducts.DESCrytography.TripleDES(plainText, ref cipherText, Key, false); | |
string result = encoding.GetString(cipherText, 0, cipherText.Length).Replace(Convert.ToChar(0x0).ToString(), ""); | |
return result; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
#if DEBUG | |
using System.Diagnostics; | |
#endif // #if DEBUG | |
using System.Linq; | |
using System.Text; | |
using System.IO; | |
using System.Security.Cryptography; | |
/// <summary> | |
/// I picked this class from somewhere named BroccoliProducts. It's a good class indeed, | |
/// but I found this class did not support my requirements to use TripleDES with ECB mode, | |
/// no padding (or padding with null). So I make some changes. | |
/// </summary> | |
namespace DurianBerryProducts | |
{ | |
/// <summary> | |
/// Declaration of DESCrytography class | |
/// </summary> | |
public static class DESCrytography | |
{ | |
///////////////////////////////////////////////////////////// | |
// Nested classes | |
/// <summary> | |
/// Declaration of BLOCK8BYTE class | |
/// </summary> | |
internal class BLOCK8BYTE | |
{ | |
///////////////////////////////////////////////////////// | |
// Constants | |
public const int BYTE_LENGTH = 8; | |
///////////////////////////////////////////////////////// | |
// Attributes | |
internal byte[] m_data = new byte[BYTE_LENGTH]; | |
///////////////////////////////////////////////////////// | |
// Operations | |
public void Reset() | |
{ | |
// Reset bytes | |
Array.Clear(m_data, 0, BYTE_LENGTH); | |
} | |
public void Set(BLOCK8BYTE Source) | |
{ | |
// Copy source data to this | |
this.Set(Source.m_data, 0); | |
} | |
public void Set(byte[] buffer, int iOffset) | |
{ | |
// Set contents by copying array | |
Array.Copy(buffer, iOffset, m_data, 0, BYTE_LENGTH); | |
} | |
public void Xor(BLOCK8BYTE A, BLOCK8BYTE B) | |
{ | |
// Set byte to A ^ B | |
for (int iOffset = 0; iOffset < BYTE_LENGTH; iOffset++) | |
m_data[iOffset] = Convert.ToByte(A.m_data[iOffset] ^ B.m_data[iOffset]); | |
} | |
public void SetBit(int iByteOffset, int iBitOffset, bool bFlag) | |
{ | |
// Compose mask | |
byte mask = Convert.ToByte(1 << iBitOffset); | |
if (((m_data[iByteOffset] & mask) == mask) != bFlag) | |
m_data[iByteOffset] ^= mask; | |
} | |
public bool GetBit(int iByteOffset, int iBitOffset) | |
{ | |
// call sibling function | |
return ((this.m_data[iByteOffset] >> iBitOffset) & 0x01) == 0x01; | |
} | |
public void ShiftLeftWrapped(BLOCK8BYTE S, int iBitShift) | |
{ | |
// this shift is only applied to the first 32 bits, and parity bit is ignored | |
// Declaration of local variables | |
int iByteOffset = 0; | |
bool bBit = false; | |
// Copy byte and shift regardless | |
for (iByteOffset = 0; iByteOffset < 4; iByteOffset++) | |
m_data[iByteOffset] = Convert.ToByte((S.m_data[iByteOffset] << iBitShift) & 0xFF); | |
// if shifting by 1... | |
if (iBitShift == 1) | |
{ | |
// repair bits on right of BYTE | |
for (iByteOffset = 0; iByteOffset < 3; iByteOffset++) | |
{ | |
// get repairing bit offsets | |
bBit = S.GetBit(iByteOffset + 1, 7); | |
this.SetBit(iByteOffset, 1, bBit); | |
} | |
// wrap around the final bit | |
this.SetBit(3, 1, S.GetBit(0, 7)); | |
} | |
else if (iBitShift == 2) | |
{ | |
// repair bits on right of BYTE | |
for (iByteOffset = 0; iByteOffset < 3; iByteOffset++) | |
{ | |
// get repairing bit offsets | |
bBit = S.GetBit(iByteOffset + 1, 7); | |
this.SetBit(iByteOffset, 2, bBit); | |
bBit = S.GetBit(iByteOffset + 1, 6); | |
this.SetBit(iByteOffset, 1, bBit); | |
} | |
// wrap around the final bit | |
this.SetBit(3, 2, S.GetBit(0, 7)); | |
this.SetBit(3, 1, S.GetBit(0, 6)); | |
} | |
#if DEBUG | |
else | |
Debug.Assert(false); | |
#endif // #if DEBUG | |
} | |
} | |
/// <summary> | |
/// Declaration of KEY_SET class | |
/// </summary> | |
internal class KEY_SET | |
{ | |
///////////////////////////////////////////////////////// | |
// Constants | |
public const int KEY_COUNT = 17; | |
///////////////////////////////////////////////////////// | |
// Attributes | |
internal BLOCK8BYTE[] m_array; | |
///////////////////////////////////////////////////////// | |
// Construction | |
internal KEY_SET() | |
{ | |
// Create array | |
m_array = new BLOCK8BYTE[KEY_COUNT]; | |
for (int i1 = 0; i1 < KEY_COUNT; i1++) | |
m_array[i1] = new BLOCK8BYTE(); | |
} | |
///////////////////////////////////////////////////////// | |
// Operations | |
public BLOCK8BYTE GetAt(int iArrayOffset) | |
{ | |
return m_array[iArrayOffset]; | |
} | |
} | |
/// <summary> | |
/// Declaration of WORKING_SET class | |
/// </summary> | |
internal class WORKING_SET | |
{ | |
///////////////////////////////////////////////////////// | |
// Attributes | |
internal BLOCK8BYTE IP = new BLOCK8BYTE(); | |
internal BLOCK8BYTE[] Ln = new BLOCK8BYTE[17]; | |
internal BLOCK8BYTE[] Rn = new BLOCK8BYTE[17]; | |
internal BLOCK8BYTE RnExpand = new BLOCK8BYTE(); | |
internal BLOCK8BYTE XorBlock = new BLOCK8BYTE(); | |
internal BLOCK8BYTE SBoxValues = new BLOCK8BYTE(); | |
internal BLOCK8BYTE f = new BLOCK8BYTE(); | |
internal BLOCK8BYTE X = new BLOCK8BYTE(); | |
internal BLOCK8BYTE DataBlockIn = new BLOCK8BYTE(); | |
internal BLOCK8BYTE DataBlockOut = new BLOCK8BYTE(); | |
internal BLOCK8BYTE DecryptXorBlock = new BLOCK8BYTE(); | |
///////////////////////////////////////////////////////// | |
// Construction | |
internal WORKING_SET() | |
{ | |
// Build the arrays | |
for (int i1 = 0; i1 < 17; i1++) | |
{ | |
Ln[i1] = new BLOCK8BYTE(); | |
Rn[i1] = new BLOCK8BYTE(); | |
} | |
} | |
///////////////////////////////////////////////////////// | |
// Operations | |
internal void Scrub() | |
{ | |
// Scrub data | |
IP.Reset(); | |
for (int i1 = 0; i1 < 17; i1++) | |
{ | |
Ln[i1].Reset(); | |
Rn[i1].Reset(); | |
} | |
RnExpand.Reset(); | |
XorBlock.Reset(); | |
SBoxValues.Reset(); | |
f.Reset(); | |
X.Reset(); | |
DataBlockIn.Reset(); | |
DataBlockOut.Reset(); | |
DecryptXorBlock.Reset(); | |
} | |
} | |
///////////////////////////////////////////////////////////// | |
// Constants | |
public const int KEY_BYTE_LENGTH = 8; | |
public const int BITS_PER_BYTE = 8; | |
///////////////////////////////////////////////////////////// | |
#region DES Tables | |
/* PERMUTED CHOICE 1 (PCl) */ | |
private static byte[] bytePC1 = { | |
57, 49, 41, 33, 25, 17, 9, | |
1, 58, 50, 42, 34, 26, 18, | |
10, 2, 59, 51, 43, 35, 27, | |
19, 11, 3, 60, 52, 44, 36, | |
63, 55, 47, 39, 31, 23, 15, | |
7, 62, 54, 46, 38, 30, 22, | |
14, 6, 61, 53, 45, 37, 29, | |
21, 13, 5, 28, 20, 12, 4, | |
}; | |
/* PERMUTED CHOICE 2 (PC2) */ | |
private static byte[] bytePC2 = { | |
14, 17, 11, 24, 1, 5, | |
3, 28, 15, 6, 21, 10, | |
23, 19, 12, 4, 26, 8, | |
16, 7, 27, 20, 13, 2, | |
41, 52, 31, 37, 47, 55, | |
30, 40, 51, 45, 33, 48, | |
44, 49, 39, 56, 34, 53, | |
46, 42, 50, 36, 29, 32, | |
}; | |
/* INITIAL PERMUTATION (IP) */ | |
private static byte[] byteIP = { | |
58, 50, 42, 34, 26, 18, 10, 2, | |
60, 52, 44, 36, 28, 20, 12, 4, | |
62, 54, 46, 38, 30, 22, 14, 6, | |
64, 56, 48, 40, 32, 24, 16, 8, | |
57, 49, 41, 33, 25, 17, 9, 1, | |
59, 51, 43, 35, 27, 19, 11, 3, | |
61, 53, 45, 37, 29, 21, 13, 5, | |
63, 55, 47, 39, 31, 23, 15, 7 | |
}; | |
/* REVERSE FINAL PERMUTATION (IP-1) */ | |
private static byte[] byteRFP = { | |
40, 8, 48, 16, 56, 24, 64, 32, | |
39, 7, 47, 15, 55, 23, 63, 31, | |
38, 6, 46, 14, 54, 22, 62, 30, | |
37, 5, 45, 13, 53, 21, 61, 29, | |
36, 4, 44, 12, 52, 20, 60, 28, | |
35, 3, 43, 11, 51, 19, 59, 27, | |
34, 2, 42, 10, 50, 18, 58, 26, | |
33, 1, 41, 9, 49, 17, 57, 25, | |
}; | |
/* E BIT-SELECTION TABLE */ | |
private static byte[] byteE = { | |
32, 1, 2, 3, 4, 5, | |
4, 5, 6, 7, 8, 9, | |
8, 9, 10, 11, 12, 13, | |
12, 13, 14, 15, 16, 17, | |
16, 17, 18, 19, 20, 21, | |
20, 21, 22, 23, 24, 25, | |
24, 25, 26, 27, 28, 29, | |
28, 29, 30, 31, 32, 1 | |
}; | |
/* PERMUTATION FUNCTION P */ | |
private static byte[] byteP = { | |
16, 7, 20, 21, | |
29, 12, 28, 17, | |
1, 15, 23, 26, | |
5, 18, 31, 10, | |
2, 8, 24, 14, | |
32, 27, 3, 9, | |
19, 13, 30, 6, | |
22, 11, 4, 25 | |
}; | |
// Schedule of left shifts for C and D blocks | |
private static byte[] byteShifts = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; | |
// S-Boxes | |
private static byte[,] byteSBox = new byte[,] { | |
{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7}, | |
{ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8}, | |
{ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0}, | |
{15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, | |
{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10}, | |
{3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5}, | |
{0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15}, | |
{13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, | |
{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8}, | |
{13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1}, | |
{13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7}, | |
{1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, | |
{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15}, | |
{13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9}, | |
{10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4}, | |
{3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, | |
{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9}, | |
{14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6}, | |
{4, 2, 1, 11, 10, 13, 7, 8,15, 9, 12, 5, 6, 3, 0, 14}, | |
{11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, | |
{12, 1, 10, 15, 9, 2, 6, 8,0, 13, 3, 4, 14, 7, 5, 11}, | |
{10, 15, 4, 2, 7, 12, 9, 5,6, 1, 13, 14, 0, 11, 3, 8}, | |
{9, 14, 15, 5, 2, 8, 12, 3,7, 0, 4, 10, 1, 13, 11, 6}, | |
{4, 3, 2, 12, 9, 5, 15, 10,11, 14, 1, 7, 6, 0, 8, 13}, | |
{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1}, | |
{13, 0, 11, 7, 4, 9, 1, 10,14, 3, 5, 12, 2, 15, 8, 6}, | |
{1, 4, 11, 13, 12, 3, 7, 14,10, 15, 6, 8, 0, 5, 9, 2}, | |
{6, 11, 13, 8, 1, 4, 10, 7,9, 5, 0, 15, 14, 2, 3, 12}, | |
{13, 2, 8, 4, 6, 15, 11, 1,10, 9, 3, 14, 5, 0, 12, 7}, | |
{1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2}, | |
{7, 11, 4, 1, 9, 12, 14, 2,0, 6, 10, 13, 15, 3, 5, 8}, | |
{2, 1, 14, 7, 4, 10, 8, 13,15, 12, 9, 0, 3, 5, 6, 11} | |
}; | |
#endregion DES Tables | |
///////////////////////////////////////////////////////////// | |
#region Static Operations - DES | |
public static bool IsValidDESKey(byte[] Key) | |
{ | |
// Shortcuts | |
if (Key == null) | |
return false; | |
if (Key.Length != KEY_BYTE_LENGTH) | |
return false; | |
if (!IsStrongDESKey(Key)) | |
return false; | |
// Make sure end bits have odd parity | |
for (int iByteOffset = 0; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) | |
{ | |
// Add bits for this byte | |
int iTotalBits = 0; | |
byte Mask = 1; | |
for (int iBitOffset = 0; iBitOffset < BITS_PER_BYTE; iBitOffset++) | |
{ | |
if ((Key[iByteOffset] & Mask) != 0) | |
iTotalBits++; | |
Mask <<= 1; | |
} | |
// If the total bits is not odd... | |
if ((iTotalBits % 2) != 1) | |
return false; | |
} | |
// Return success | |
return true; | |
} | |
public static bool IsStrongDESKey(byte[] Key) | |
{ | |
// Compare by large integer | |
UInt64 uiKey = BitConverter.ToUInt64(Key, 0); | |
// Find weak keys... | |
if ( | |
(uiKey == 0x0000000000000000) || | |
(uiKey == 0x00000000FFFFFFFF) || | |
(uiKey == 0xE0E0E0E0F1F1F1F1) || | |
(uiKey == 0x1F1F1F1F0E0E0E0E) | |
) | |
return false; | |
// Find semi-weak keys... | |
if ( | |
(uiKey == 0x011F011F010E010E) || | |
(uiKey == 0x1F011F010E010E01) || | |
(uiKey == 0x01E001E001F101F1) || | |
(uiKey == 0xE001E001F101F101) || | |
(uiKey == 0x01FE01FE01FE01FE) || | |
(uiKey == 0xFE01FE01FE01FE01) || | |
(uiKey == 0x1FE01FE00EF10EF1) || | |
(uiKey == 0xE01FE01FF10EF10E) || | |
(uiKey == 0x1FFE1FFE0EFE0EFE) || | |
(uiKey == 0xFE1FFE1FFE0EFE0E) || | |
(uiKey == 0xE0FEE0FEF1FEF1FE) || | |
(uiKey == 0xFEE0FEE0FEF1FEF1) | |
) | |
return false; | |
// Return success | |
return true; | |
} | |
/// <summary> | |
/// I put this method because I need to implement padding mechanism that uses byte 0 as padding. | |
/// This is not available by default. | |
/// </summary> | |
/// <param name="Input"></param> | |
/// <param name="PadLength"></param> | |
/// <param name="Padding"></param> | |
/// <returns></returns> | |
public static byte[] DoPadWithString(byte[] Input, int PadLength, byte Padding) | |
{ | |
int slen = Input.Length; | |
int i = PadLength - (Input.Length % PadLength); | |
Debug.WriteLine("SLEN: "+slen); | |
Debug.WriteLine("I: " + i); | |
Debug.WriteLine("I: " + (i % PadLength)); | |
Debug.WriteLine("PadLength: " + PadLength); | |
if ((i > 0) && (i % PadLength < PadLength)) | |
{ | |
Debug.WriteLine("Masuk IF, pad length: "+PadLength); | |
byte[] res = new byte[((Input.Length / PadLength)+1) * PadLength]; | |
Array.Copy(Input, 0, res, 0, slen); | |
//slen = Input.Length % PadLength; | |
Debug.WriteLine("SLEN: " + slen); | |
//for (i = Input.Length + slen; i < PadLength; i++) | |
while (slen % PadLength > 0) | |
{ | |
Debug.WriteLine("+ SLEN: " + slen); | |
res[slen] = Padding; | |
slen++; | |
} | |
Debug.WriteLine("RES: " + res+"; LENGTH: "+res.Length); | |
Input = res; | |
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); | |
Debug.WriteLine("Res: " + encoding.GetString(Input, 0, Input.Length)); | |
return Input; | |
} | |
else return Input; | |
} | |
public static void DES(byte[] bufferIn, ref byte[] bufferOut, byte[] Key, bool bEncrypt) | |
{ | |
// Shortcuts | |
if (!IsValidDESKey(Key)) | |
throw new Exception("Invalid DES key."); | |
// Create the output buffer | |
_createBufferOut(bufferIn.Length, ref bufferOut, bEncrypt); | |
// Expand the keys into Kn | |
KEY_SET[] Kn = new KEY_SET[1] { | |
_expandKey(Key, 0) | |
}; | |
// Apply DES keys | |
_desAlgorithm(bufferIn, ref bufferOut, Kn, bEncrypt); | |
// If decrypting... | |
if (!bEncrypt) | |
_removePadding(ref bufferOut); | |
} | |
#endregion Static Operations - DES | |
///////////////////////////////////////////////////////////// | |
#region Static Operations - TripleDES | |
/// <summary> | |
/// Sorry, I commented some lines to match my requirement. I don't need to check | |
/// wether there are any same sub keys or not. | |
/// </summary> | |
/// <param name="Key"></param> | |
/// <returns></returns> | |
public static bool IsValidTripleDESKey(byte[] Key) | |
{ | |
// Shortcuts | |
if (Key == null) | |
return false; | |
if (Key.Length != (3 * KEY_BYTE_LENGTH)) | |
return false; | |
// Check each part of the key | |
/* | |
byte[] SubKey = new byte[KEY_BYTE_LENGTH]; | |
for (int iKeyLoop = 0; iKeyLoop < 3; iKeyLoop++) | |
{ | |
// Get sub-key | |
Array.Copy(Key, iKeyLoop * 8, SubKey, 0, KEY_BYTE_LENGTH); | |
// Check this DES key | |
if (!IsValidDESKey(SubKey)) | |
return false; | |
} | |
// Keys must not be equal | |
/* | |
bool bAEqualsB = true; | |
bool bAEqualsC = true; | |
bool bBEqualsC = true; | |
for (int iByteOffset = 0; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) | |
{ | |
if (Key[iByteOffset] != Key[iByteOffset + KEY_BYTE_LENGTH]) | |
bAEqualsB = false; | |
if (Key[iByteOffset] != Key[iByteOffset + KEY_BYTE_LENGTH + KEY_BYTE_LENGTH]) | |
bAEqualsC = false; | |
if (Key[iByteOffset + KEY_BYTE_LENGTH] != Key[iByteOffset + KEY_BYTE_LENGTH + KEY_BYTE_LENGTH]) | |
bBEqualsC = false; | |
} | |
if ((bAEqualsB) || (bAEqualsC) || (bBEqualsC)) | |
return false; | |
*/ | |
// Return success | |
return true; | |
} | |
/// <summary> | |
/// Sorry, I need to comment _removePadding because I implement my own padding mechanism. So I don't need the original remove padding. | |
/// </summary> | |
/// <param name="bufferIn"></param> | |
/// <param name="bufferOut"></param> | |
/// <param name="Key"></param> | |
/// <param name="bEncrypt"></param> | |
public static void TripleDES(byte[] bufferIn, ref byte[] bufferOut, byte[] Key, bool bEncrypt) | |
{ | |
// Shortcuts | |
if (!IsValidTripleDESKey(Key)) | |
throw new Exception("Invalid DES key."); | |
// Create the output buffer | |
_createBufferOut(bufferIn.Length, ref bufferOut, bEncrypt); | |
// Expand the keys into Kn | |
KEY_SET[] Kn = new KEY_SET[3] { | |
_expandKey(Key, 0), | |
_expandKey(Key, 8), | |
_expandKey(Key, 16) | |
}; | |
// Apply DES keys | |
_desAlgorithm(bufferIn, ref bufferOut, Kn, bEncrypt); | |
// If decrypting... | |
//if (!bEncrypt) | |
// _removePadding(ref bufferOut); | |
} | |
#endregion Static Operations - TripleDES | |
///////////////////////////////////////////////////////////// | |
#region Static Operations | |
private static void _incKey(byte[] Key, int iInc) | |
{ | |
#if DEBUG | |
Debug.Assert(Key.Length == KEY_BYTE_LENGTH); | |
#endif // #if DEBUG | |
// shortcuts | |
if (iInc == 0) | |
return; | |
// Add the increment | |
int iCarry = iInc; | |
for (int iByteOffset = 0; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) | |
{ | |
int iTemp = Key[iByteOffset] + iCarry; | |
iCarry = iTemp >> 8; | |
Key[iByteOffset] = Convert.ToByte(iTemp & 0xFF); | |
if (iCarry == 0) | |
break; | |
} | |
} | |
private static void _modifyKeyParity(byte[] Key) | |
{ | |
#if DEBUG | |
Debug.Assert(Key.Length == KEY_BYTE_LENGTH); | |
#endif // #if DEBUG | |
// Make sure end bits have odd parity | |
for (int iByteOffset = 0; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) | |
{ | |
// Add bits for this byte | |
int iTotalBits = 0; | |
byte Mask = 1; | |
for (int iBitOffset = 0; iBitOffset < BITS_PER_BYTE; iBitOffset++) | |
{ | |
if ((Key[iByteOffset] & Mask) != 0) | |
iTotalBits++; | |
Mask <<= 1; | |
} | |
// If the total bits is not odd... | |
if ((iTotalBits % 2) != 1) | |
{ | |
// Flip the first bit to retain odd parity | |
Key[iByteOffset] ^= 0x01; | |
} | |
} | |
} | |
private static KEY_SET _expandKey(byte[] Key, int iOffset) | |
{ | |
// | |
// Expand an 8 byte DES key into a set of permuted keys | |
// | |
// Declare return variable | |
KEY_SET Ftmp = new KEY_SET(); | |
// Declaration of local variables | |
int iTableOffset, iArrayOffset, iPermOffset, iByteOffset, iBitOffset; | |
bool bBit; | |
// Put key into an 8-bit block | |
BLOCK8BYTE K = new BLOCK8BYTE(); | |
K.Set(Key, iOffset); | |
// Permutate Kp with PC1 | |
BLOCK8BYTE Kp = new BLOCK8BYTE(); | |
for (iArrayOffset = 0; iArrayOffset < bytePC1.Length; iArrayOffset++) | |
{ | |
// Get permute offset | |
iPermOffset = bytePC1[iArrayOffset]; | |
iPermOffset--; | |
// Get and set bit | |
Kp.SetBit( | |
_bitAddressToByteOffset(iArrayOffset, 7), | |
_bitAddressToBitOffset(iArrayOffset, 7), | |
K.GetBit( | |
_bitAddressToByteOffset(iPermOffset, 8), | |
_bitAddressToBitOffset(iPermOffset, 8) | |
) | |
); | |
} | |
// Create 17 blocks of C and D from Kp | |
BLOCK8BYTE[] KpCn = new BLOCK8BYTE[17]; | |
BLOCK8BYTE[] KpDn = new BLOCK8BYTE[17]; | |
for (iArrayOffset = 0; iArrayOffset < 17; iArrayOffset++) | |
{ | |
KpCn[iArrayOffset] = new BLOCK8BYTE(); | |
KpDn[iArrayOffset] = new BLOCK8BYTE(); | |
} | |
for (iArrayOffset = 0; iArrayOffset < 32; iArrayOffset++) | |
{ | |
// Set bit in KpCn | |
iByteOffset = _bitAddressToByteOffset(iArrayOffset, 8); | |
iBitOffset = _bitAddressToBitOffset(iArrayOffset, 8); | |
bBit = Kp.GetBit(iByteOffset, iBitOffset); | |
KpCn[0].SetBit(iByteOffset, iBitOffset, bBit); | |
// Set bit in KpDn | |
bBit = Kp.GetBit(iByteOffset + 4, iBitOffset); | |
KpDn[0].SetBit(iByteOffset, iBitOffset, bBit); | |
} | |
for (iArrayOffset = 1; iArrayOffset < 17; iArrayOffset++) | |
{ | |
// Shift left wrapped | |
KpCn[iArrayOffset].ShiftLeftWrapped(KpCn[iArrayOffset - 1], byteShifts[iArrayOffset - 1]); | |
KpDn[iArrayOffset].ShiftLeftWrapped(KpDn[iArrayOffset - 1], byteShifts[iArrayOffset - 1]); | |
} | |
// Create 17 keys Kn | |
for (iArrayOffset = 0; iArrayOffset < 17; iArrayOffset++) | |
{ | |
// Loop through the bits | |
for (iTableOffset = 0; iTableOffset < 48; iTableOffset++) | |
{ | |
// Get address if bit | |
iPermOffset = bytePC2[iTableOffset]; | |
iPermOffset--; | |
// Convert to byte and bit offsets | |
iByteOffset = _bitAddressToByteOffset(iPermOffset, 7); | |
iBitOffset = _bitAddressToBitOffset(iPermOffset, 7); | |
// Get bit | |
if (iByteOffset < 4) | |
bBit = KpCn[iArrayOffset].GetBit(iByteOffset, iBitOffset); | |
else | |
bBit = KpDn[iArrayOffset].GetBit(iByteOffset - 4, iBitOffset); | |
// Set bit | |
iByteOffset = _bitAddressToByteOffset(iTableOffset, 6); | |
iBitOffset = _bitAddressToBitOffset(iTableOffset, 6); | |
Ftmp.GetAt(iArrayOffset).SetBit(iByteOffset, iBitOffset, bBit); | |
} | |
} | |
// Return variable | |
return Ftmp; | |
} | |
private static void _createBufferOut(int iBufferInLength, ref byte[] bufferOut, bool bEncrypt) | |
{ | |
// | |
// Create a buffer for the output, which may be trimmed later | |
// | |
// If encrypting... | |
int iOutputLength; | |
if (bEncrypt) | |
{ | |
if ((iBufferInLength % KEY_BYTE_LENGTH) != 0) | |
{ | |
iOutputLength = ((iBufferInLength / KEY_BYTE_LENGTH) + 1) * KEY_BYTE_LENGTH; | |
//iOutputLength = ((iBufferInLength / KEY_BYTE_LENGTH)) * KEY_BYTE_LENGTH; | |
} | |
else | |
{ | |
//iOutputLength = iBufferInLength + KEY_BYTE_LENGTH; | |
iOutputLength = iBufferInLength; | |
} | |
} | |
else | |
{ | |
if (iBufferInLength < 8) | |
throw new Exception("DES cypher-text must be at least 8 bytes."); | |
if ((iBufferInLength % 8) != 0) | |
throw new Exception("DES cypher-text must be a factor of 8 bytes in length."); | |
iOutputLength = iBufferInLength; | |
} | |
// Create buffer | |
if ((bufferOut == null) || (bufferOut.Length != iOutputLength)) | |
bufferOut = new byte[iOutputLength]; | |
else | |
Array.Clear(bufferOut, 0, bufferOut.Length); | |
} | |
private static void _removePadding(ref byte[] bufferOut) | |
{ | |
// | |
// Remove the padding after decrypting | |
// | |
// Get the padding... | |
byte Padding = bufferOut[bufferOut.Length - 1]; | |
if ((Padding == 0) || (Padding > 8)) | |
throw new Exception("Invalid padding on DES data."); | |
// Confirm padding | |
bool bPaddingOk = true; | |
for (int iByteOffset = 1; iByteOffset < Padding; iByteOffset++) | |
{ | |
if (bufferOut[bufferOut.Length - 1 - iByteOffset] != Padding) | |
{ | |
bPaddingOk = false; | |
break; | |
} | |
} | |
if (bPaddingOk) | |
{ | |
// Chop off the padding | |
Array.Resize(ref bufferOut, bufferOut.Length - Padding); | |
} | |
else | |
throw new Exception("Invalid padding on DES data."); | |
} | |
private static void _desAlgorithm(byte[] bufferIn, ref byte[] bufferOut, KEY_SET[] KeySets, bool bEncrypt) | |
{ | |
// | |
// Apply the DES algorithm to each block | |
// | |
// Declare a workset set of variables | |
WORKING_SET workingSet = new WORKING_SET(); | |
// encode/decode blocks | |
int iBufferPos = 0; | |
while (true) | |
{ | |
// Check buffer position | |
if (bEncrypt) | |
{ | |
// If end of buffer... | |
if (iBufferPos >= bufferOut.Length) | |
break; | |
// Calulate remaining bytes | |
int iRemainder = (bufferIn.Length - iBufferPos); | |
if (iRemainder >= 8) | |
workingSet.DataBlockIn.Set(bufferIn, iBufferPos); | |
else | |
{ | |
// Copy part-block | |
workingSet.DataBlockIn.Reset(); | |
if (iRemainder > 0) | |
Array.Copy(bufferIn, iBufferPos, workingSet.DataBlockIn.m_data, 0, iRemainder); | |
// Get the padding byte | |
byte Padding = Convert.ToByte(KEY_BYTE_LENGTH - iRemainder); | |
// Add padding to block | |
for (int iByteOffset = iRemainder; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) | |
workingSet.DataBlockIn.m_data[iByteOffset] = Padding; | |
} | |
} | |
else | |
{ | |
// If end of buffer... | |
if (iBufferPos >= bufferIn.Length) | |
break; | |
// Get the next block | |
workingSet.DataBlockIn.Set(bufferIn, iBufferPos); | |
} | |
// if encrypting and not the first block... | |
if ((bEncrypt) && (iBufferPos > 0)) | |
{ | |
// Amri commented these lines since Amri need EBC, not CBC | |
// Apply succession => XOR M with previous block | |
//workingSet.DataBlockIn.Xor(workingSet.DataBlockOut, workingSet.DataBlockIn); | |
} | |
// Apply the algorithm | |
workingSet.DataBlockOut.Set(workingSet.DataBlockIn); | |
_lowLevel_desAlgorithm(workingSet, KeySets, bEncrypt); | |
// If decrypting... | |
if (!bEncrypt) | |
{ | |
// Amri commented these lines since Amri need EBC, not CBC | |
// Retain the succession | |
//if (iBufferPos > 0) | |
// workingSet.DataBlockOut.Xor(workingSet.DecryptXorBlock, workingSet.DataBlockOut); | |
// Retain the last block | |
workingSet.DecryptXorBlock.Set(workingSet.DataBlockIn); | |
} | |
// Update buffer out | |
Array.Copy(workingSet.DataBlockOut.m_data, 0, bufferOut, iBufferPos, 8); | |
// Move on | |
iBufferPos += 8; | |
} | |
// Scrub the working set | |
workingSet.Scrub(); | |
} | |
private static void _lowLevel_desAlgorithm(WORKING_SET workingSet, KEY_SET[] KeySets, bool bEncrypt) | |
{ | |
// | |
// Apply 1 or 3 keys to a block of data | |
// | |
// Declaration of local variables | |
int iTableOffset; | |
int iArrayOffset; | |
int iPermOffset; | |
int iByteOffset; | |
int iBitOffset; | |
// Loop through keys | |
for (int iKeySetOffset = 0; iKeySetOffset < KeySets.Length; iKeySetOffset++) | |
{ | |
// Permute with byteIP | |
workingSet.IP.Reset(); | |
for (iTableOffset = 0; iTableOffset < byteIP.Length; iTableOffset++) | |
{ | |
// Get perm offset | |
iPermOffset = byteIP[iTableOffset]; | |
iPermOffset--; | |
// Get and set bit | |
workingSet.IP.SetBit( | |
_bitAddressToByteOffset(iTableOffset, 8), | |
_bitAddressToBitOffset(iTableOffset, 8), | |
workingSet.DataBlockOut.GetBit( | |
_bitAddressToByteOffset(iPermOffset, 8), | |
_bitAddressToBitOffset(iPermOffset, 8) | |
) | |
); | |
} | |
// Create Ln[0] and Rn[0] | |
workingSet.Ln[0].Reset(); | |
workingSet.Rn[0].Reset(); | |
for (iArrayOffset = 0; iArrayOffset < 32; iArrayOffset++) | |
{ | |
iByteOffset = _bitAddressToByteOffset(iArrayOffset, 8); | |
iBitOffset = _bitAddressToBitOffset(iArrayOffset, 8); | |
workingSet.Ln[0].SetBit(iByteOffset, iBitOffset, workingSet.IP.GetBit(iByteOffset, iBitOffset)); | |
workingSet.Rn[0].SetBit(iByteOffset, iBitOffset, workingSet.IP.GetBit(iByteOffset + 4, iBitOffset)); | |
} | |
// Loop through 17 interations | |
for (int iBlockOffset = 1; iBlockOffset < 17; iBlockOffset++) | |
{ | |
// Get the array offset | |
int iKeyOffset; | |
if (bEncrypt != (iKeySetOffset == 1)) | |
iKeyOffset = iBlockOffset; | |
else | |
iKeyOffset = 17 - iBlockOffset; | |
// Set Ln[N] = Rn[N-1] | |
workingSet.Ln[iBlockOffset].Set(workingSet.Rn[iBlockOffset - 1]); | |
// Set Rn[N] = Ln[0] + f(R[N-1],K[N]) | |
for (iTableOffset = 0; iTableOffset < byteE.Length; iTableOffset++) | |
{ | |
// Get perm offset | |
iPermOffset = byteE[iTableOffset]; | |
iPermOffset--; | |
// Get and set bit | |
workingSet.RnExpand.SetBit( | |
_bitAddressToByteOffset(iTableOffset, 6), | |
_bitAddressToBitOffset(iTableOffset, 6), | |
workingSet.Rn[iBlockOffset - 1].GetBit( | |
_bitAddressToByteOffset(iPermOffset, 8), | |
_bitAddressToBitOffset(iPermOffset, 8) | |
) | |
); | |
} | |
// XOR expanded block with K-block | |
if (bEncrypt != (iKeySetOffset == 1)) | |
workingSet.XorBlock.Xor(workingSet.RnExpand, KeySets[iKeySetOffset].GetAt(iKeyOffset)); | |
else | |
workingSet.XorBlock.Xor(workingSet.RnExpand, KeySets[KeySets.Length - 1 - iKeySetOffset].GetAt(iKeyOffset)); | |
// Set S-Box values | |
workingSet.SBoxValues.Reset(); | |
for (iTableOffset = 0; iTableOffset < 8; iTableOffset++) | |
{ | |
// Calculate m and n | |
int m = ((workingSet.XorBlock.GetBit(iTableOffset, 7) ? 1 : 0) << 1) | (workingSet.XorBlock.GetBit(iTableOffset, 2) ? 1 : 0); | |
int n = (workingSet.XorBlock.m_data[iTableOffset] >> 3) & 0x0F; | |
// Get s-box value | |
iPermOffset = byteSBox[(iTableOffset * 4) + m, n]; | |
workingSet.SBoxValues.m_data[iTableOffset] = (byte)(iPermOffset << 4); | |
} | |
// Permute with P -> f | |
workingSet.f.Reset(); | |
for (iTableOffset = 0; iTableOffset < byteP.Length; iTableOffset++) | |
{ | |
// Get perm offset | |
iPermOffset = byteP[iTableOffset]; | |
iPermOffset--; | |
// Get and set bit | |
workingSet.f.SetBit( | |
_bitAddressToByteOffset(iTableOffset, 4), | |
_bitAddressToBitOffset(iTableOffset, 4), | |
workingSet.SBoxValues.GetBit( | |
_bitAddressToByteOffset(iPermOffset, 4), | |
_bitAddressToBitOffset(iPermOffset, 4) | |
) | |
); | |
} | |
// Rn[N] = Ln[N-1] ^ f | |
workingSet.Rn[iBlockOffset].Reset(); | |
for (iTableOffset = 0; iTableOffset < 8; iTableOffset++) | |
{ | |
// Get Ln[N-1] -> A | |
byte A = workingSet.Ln[iBlockOffset - 1].m_data[(iTableOffset >> 1)]; | |
if ((iTableOffset % 2) == 0) | |
A >>= 4; | |
else | |
A &= 0x0F; | |
// Get f -> B | |
byte B = Convert.ToByte(workingSet.f.m_data[iTableOffset] >> 4); | |
// Update Rn[N] | |
if ((iTableOffset % 2) == 0) | |
workingSet.Rn[iBlockOffset].m_data[iTableOffset >> 1] |= Convert.ToByte((A ^ B) << 4); | |
else | |
workingSet.Rn[iBlockOffset].m_data[iTableOffset >> 1] |= Convert.ToByte(A ^ B); | |
} | |
} | |
// X = R16 L16 | |
workingSet.X.Reset(); | |
for (iTableOffset = 0; iTableOffset < 4; iTableOffset++) | |
{ | |
workingSet.X.m_data[iTableOffset] = workingSet.Rn[16].m_data[iTableOffset]; | |
workingSet.X.m_data[iTableOffset + 4] = workingSet.Ln[16].m_data[iTableOffset]; | |
} | |
// C = X perm IP | |
workingSet.DataBlockOut.Reset(); | |
for (iTableOffset = 0; iTableOffset < byteRFP.Length; iTableOffset++) | |
{ | |
// Get perm offset | |
iPermOffset = byteRFP[iTableOffset]; | |
iPermOffset--; | |
// Get and set bit | |
workingSet.DataBlockOut.SetBit( | |
_bitAddressToByteOffset(iTableOffset, 8), | |
_bitAddressToBitOffset(iTableOffset, 8), | |
workingSet.X.GetBit( | |
_bitAddressToByteOffset(iPermOffset, 8), | |
_bitAddressToBitOffset(iPermOffset, 8) | |
) | |
); | |
} | |
} // key loop | |
} | |
#endregion Static Operations | |
///////////////////////////////////////////////////////////// | |
// Helper Operations | |
private static int _bitAddressToByteOffset(int iTableAddress, int iTableWidth) | |
{ | |
int iFtmp = iTableAddress / iTableWidth; | |
return iFtmp; | |
} | |
private static int _bitAddressToBitOffset(int iTableAddress, int iTableWidth) | |
{ | |
int iFtmp = BITS_PER_BYTE - 1 - (iTableAddress % iTableWidth); | |
return iFtmp; | |
} | |
///////////////////////////////////////////////////////////// | |
#region Debug Operations | |
#if DEBUG | |
#if !SILVERLIGHT | |
private static void MicrosoftDESEncrypt(byte[] bufferIn, ref byte[] bufferOut, byte[] Key, bool bEncrypt, bool bDESMode) | |
{ | |
// Declaration of key and IV | |
byte[] bufferTemp = new byte[1024]; | |
byte[] IV; | |
if(bDESMode) | |
IV = new byte[8]; | |
else | |
IV = new byte[8*3]; | |
// Declare a crypto object | |
ICryptoTransform crypto; | |
if (bDESMode) | |
{ | |
DESCryptoServiceProvider des = new DESCryptoServiceProvider(); | |
des.Padding = PaddingMode.PKCS7; | |
if (bEncrypt) | |
crypto = des.CreateEncryptor(Key, IV); | |
else | |
crypto = des.CreateDecryptor(Key, IV); | |
} | |
else | |
{ | |
TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider(); | |
tripleDes.Padding = PaddingMode.PKCS7; | |
if (bEncrypt) | |
crypto = tripleDes.CreateEncryptor(Key, IV); | |
else | |
crypto = tripleDes.CreateDecryptor(Key, IV); | |
} | |
// a memory stream for the cyrpto | |
using(MemoryStream ms = new MemoryStream()) | |
{ | |
// Create a CryptoStream using the memory stream | |
using (CryptoStream encStream = new CryptoStream(ms, crypto, CryptoStreamMode.Write)) | |
{ | |
// Encrypt/decrypt and flush | |
encStream.Write(bufferIn, 0, bufferIn.Length); | |
encStream.Flush(); | |
encStream.FlushFinalBlock(); | |
encStream.Close(); | |
// Get the data into a buffer | |
bufferOut = ms.ToArray(); | |
} | |
} | |
} | |
#endif // #if !SILVERLIGHT | |
#endif // #if DEBUG | |
#if DEBUG | |
#if !SILVERLIGHT | |
public static void _assertBufferMatch(byte[] A, byte[] B) | |
{ | |
// Compare outputs | |
Debug.Assert(A.Length == B.Length); | |
for (int iOffset = 0; iOffset < A.Length; iOffset++) | |
Debug.Assert(A[iOffset] == B[iOffset]); | |
} | |
#endif // #if !SILVERLIGHT | |
#endif // #if DEBUG | |
#if DEBUG | |
#if !SILVERLIGHT | |
public static void Test() | |
{ | |
// | |
// This function encrypts and encrypts data using our DES algorithm, and | |
// ensures the results are the same as the Microsoft algorithm with default padding settings. | |
// | |
// Declaration of local variables | |
Random rnd = new Random(1); | |
byte[] DesKey, Des3Key; | |
byte[] plainText = null, cypherText = null, plainText2 = null, msCypherText = null, msPlainText2 = null; | |
// Compare the DES algorithm with the Microsoft algorithm | |
for (int iTest = 0; iTest < 100*1000; iTest++) | |
{ | |
// Dump progress | |
if ((iTest % 200) == 0) | |
Trace.TraceInformation("Test {0}", iTest); | |
// Generate test data | |
DesKey = DESCrytography.CreateDesKey(rnd); | |
Des3Key = DESCrytography.CreateTripleDesKey(rnd); | |
int iLength = rnd.Next(0, 256); | |
if ((plainText == null) || (plainText.Length != iLength)) | |
plainText = new byte[iLength]; | |
rnd.NextBytes(plainText); | |
// DES Test | |
{ | |
// Encrypt using our algorithm | |
DESCrytography.DES(plainText, ref cypherText, DesKey, true); | |
// Decrypt using our algorithm | |
DESCrytography.DES(cypherText, ref plainText2, DesKey, false); | |
// Compare outputs | |
_assertBufferMatch(plainText,plainText2); | |
// Encrypt using Microsoft algorithm | |
MicrosoftDESEncrypt(plainText, ref msCypherText, DesKey, true, true); | |
// Decrypt using Microsoft algorithm | |
MicrosoftDESEncrypt(msCypherText, ref msPlainText2, DesKey, false, true); | |
// Compare outputs | |
_assertBufferMatch(plainText, msPlainText2); | |
// Make sure Microsoft and our algorithms are the same | |
_assertBufferMatch(cypherText, msCypherText); | |
} | |
// TripleDES Test | |
{ | |
// Encrypt using our algorithm | |
DESCrytography.TripleDES(plainText, ref cypherText, Des3Key, true); | |
// Decrypt using our algorithm | |
DESCrytography.TripleDES(cypherText, ref plainText2, Des3Key, false); | |
// Compare outputs | |
_assertBufferMatch(plainText, plainText2); | |
// Encrypt using Microsoft algorithm | |
MicrosoftDESEncrypt(plainText, ref msCypherText, Des3Key, true, false); | |
// Decrypt using Microsoft algorithm | |
MicrosoftDESEncrypt(msCypherText, ref msPlainText2, Des3Key, false, false); | |
// Compare outputs | |
_assertBufferMatch(plainText, msPlainText2); | |
// Make sure Microsoft and our algorithms are the same | |
_assertBufferMatch(cypherText, msCypherText); | |
} | |
} // for-loop | |
} | |
#endif // #if !SILVERLIGHT | |
#endif // #if DEBUG | |
#endregion Debug Operations | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Copyright (c) Microsoft Corporation. All rights reserved. | |
using System; | |
using System.Text; | |
// ************************************************************** | |
// * Raw implementation of the MD5 hash algorithm | |
// * from RFC 1321. | |
// * | |
// * Written By: Reid Borsuk and Jenny Zheng | |
// * Copyright (c) Microsoft Corporation. All rights reserved. | |
// ************************************************************** | |
// Simple struct for the (a,b,c,d) which is used to compute the mesage digest. | |
struct ABCDStruct | |
{ | |
public uint A; | |
public uint B; | |
public uint C; | |
public uint D; | |
} | |
public sealed class MD5Core | |
{ | |
//Prevent CSC from adding a default public constructor | |
private MD5Core() { } | |
public static byte[] GetHash(string input, Encoding encoding) | |
{ | |
if (null == input) | |
throw new System.ArgumentNullException("input", "Unable to calculate hash over null input data"); | |
if (null == encoding) | |
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"); | |
byte[] target = encoding.GetBytes(input); | |
return GetHash(target); | |
} | |
public static byte[] GetHash(string input) | |
{ | |
return GetHash(input, new UTF8Encoding()); | |
} | |
public static string GetHashString(byte[] input) | |
{ | |
if (null == input) | |
throw new System.ArgumentNullException("input", "Unable to calculate hash over null input data"); | |
string retval = BitConverter.ToString(GetHash(input)); | |
retval = retval.Replace("-", ""); | |
return retval; | |
} | |
public static string GetHashString(string input, Encoding encoding) | |
{ | |
if (null == input) | |
throw new System.ArgumentNullException("input", "Unable to calculate hash over null input data"); | |
if (null == encoding) | |
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"); | |
byte[] target = encoding.GetBytes(input); | |
return GetHashString(target); | |
} | |
public static string GetHashString(string input) | |
{ | |
return GetHashString(input, new UTF8Encoding()); | |
} | |
public static byte[] GetHash(byte[] input) | |
{ | |
if (null == input) | |
throw new System.ArgumentNullException("input", "Unable to calculate hash over null input data"); | |
//Intitial values defined in RFC 1321 | |
ABCDStruct abcd = new ABCDStruct(); | |
abcd.A = 0x67452301; | |
abcd.B = 0xefcdab89; | |
abcd.C = 0x98badcfe; | |
abcd.D = 0x10325476; | |
//We pass in the input array by block, the final block of data must be handled specialy for padding & length embeding | |
int startIndex = 0; | |
while (startIndex <= input.Length - 64) | |
{ | |
MD5Core.GetHashBlock(input, ref abcd, startIndex); | |
startIndex += 64; | |
} | |
// The final data block. | |
return MD5Core.GetHashFinalBlock(input, startIndex, input.Length - startIndex, abcd, (Int64)input.Length * 8); | |
} | |
internal static byte[] GetHashFinalBlock(byte[] input, int ibStart, int cbSize, ABCDStruct ABCD, Int64 len) | |
{ | |
byte[] working = new byte[64]; | |
byte[] length = BitConverter.GetBytes(len); | |
//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 | |
//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 | |
//use a temporary array rather then doing in-place assignment (5% for small inputs) | |
Array.Copy(input, ibStart, working, 0, cbSize); | |
working[cbSize] = 0x80; | |
//We have enough room to store the length in this chunk | |
if (cbSize < 56) | |
{ | |
Array.Copy(length, 0, working, 56, 8); | |
GetHashBlock(working, ref ABCD, 0); | |
} | |
else //We need an aditional chunk to store the length | |
{ | |
GetHashBlock(working, ref ABCD, 0); | |
//Create an entirely new chunk due to the 0-assigned trick mentioned above, to avoid an extra function call clearing the array | |
working = new byte[64]; | |
Array.Copy(length, 0, working, 56, 8); | |
GetHashBlock(working, ref ABCD, 0); | |
} | |
byte[] output = new byte[16]; | |
Array.Copy(BitConverter.GetBytes(ABCD.A), 0, output, 0, 4); | |
Array.Copy(BitConverter.GetBytes(ABCD.B), 0, output, 4, 4); | |
Array.Copy(BitConverter.GetBytes(ABCD.C), 0, output, 8, 4); | |
Array.Copy(BitConverter.GetBytes(ABCD.D), 0, output, 12, 4); | |
return output; | |
} | |
// Performs a single block transform of MD5 for a given set of ABCD inputs | |
/* If implementing your own hashing framework, be sure to set the initial ABCD correctly according to RFC 1321: | |
// A = 0x67452301; | |
// B = 0xefcdab89; | |
// C = 0x98badcfe; | |
// D = 0x10325476; | |
*/ | |
internal static void GetHashBlock(byte[] input, ref ABCDStruct ABCDValue, int ibStart) | |
{ | |
uint[] temp = Converter(input, ibStart); | |
uint a = ABCDValue.A; | |
uint b = ABCDValue.B; | |
uint c = ABCDValue.C; | |
uint d = ABCDValue.D; | |
a = r1(a, b, c, d, temp[0], 7, 0xd76aa478); | |
d = r1(d, a, b, c, temp[1], 12, 0xe8c7b756); | |
c = r1(c, d, a, b, temp[2], 17, 0x242070db); | |
b = r1(b, c, d, a, temp[3], 22, 0xc1bdceee); | |
a = r1(a, b, c, d, temp[4], 7, 0xf57c0faf); | |
d = r1(d, a, b, c, temp[5], 12, 0x4787c62a); | |
c = r1(c, d, a, b, temp[6], 17, 0xa8304613); | |
b = r1(b, c, d, a, temp[7], 22, 0xfd469501); | |
a = r1(a, b, c, d, temp[8], 7, 0x698098d8); | |
d = r1(d, a, b, c, temp[9], 12, 0x8b44f7af); | |
c = r1(c, d, a, b, temp[10], 17, 0xffff5bb1); | |
b = r1(b, c, d, a, temp[11], 22, 0x895cd7be); | |
a = r1(a, b, c, d, temp[12], 7, 0x6b901122); | |
d = r1(d, a, b, c, temp[13], 12, 0xfd987193); | |
c = r1(c, d, a, b, temp[14], 17, 0xa679438e); | |
b = r1(b, c, d, a, temp[15], 22, 0x49b40821); | |
a = r2(a, b, c, d, temp[1], 5, 0xf61e2562); | |
d = r2(d, a, b, c, temp[6], 9, 0xc040b340); | |
c = r2(c, d, a, b, temp[11], 14, 0x265e5a51); | |
b = r2(b, c, d, a, temp[0], 20, 0xe9b6c7aa); | |
a = r2(a, b, c, d, temp[5], 5, 0xd62f105d); | |
d = r2(d, a, b, c, temp[10], 9, 0x02441453); | |
c = r2(c, d, a, b, temp[15], 14, 0xd8a1e681); | |
b = r2(b, c, d, a, temp[4], 20, 0xe7d3fbc8); | |
a = r2(a, b, c, d, temp[9], 5, 0x21e1cde6); | |
d = r2(d, a, b, c, temp[14], 9, 0xc33707d6); | |
c = r2(c, d, a, b, temp[3], 14, 0xf4d50d87); | |
b = r2(b, c, d, a, temp[8], 20, 0x455a14ed); | |
a = r2(a, b, c, d, temp[13], 5, 0xa9e3e905); | |
d = r2(d, a, b, c, temp[2], 9, 0xfcefa3f8); | |
c = r2(c, d, a, b, temp[7], 14, 0x676f02d9); | |
b = r2(b, c, d, a, temp[12], 20, 0x8d2a4c8a); | |
a = r3(a, b, c, d, temp[5], 4, 0xfffa3942); | |
d = r3(d, a, b, c, temp[8], 11, 0x8771f681); | |
c = r3(c, d, a, b, temp[11], 16, 0x6d9d6122); | |
b = r3(b, c, d, a, temp[14], 23, 0xfde5380c); | |
a = r3(a, b, c, d, temp[1], 4, 0xa4beea44); | |
d = r3(d, a, b, c, temp[4], 11, 0x4bdecfa9); | |
c = r3(c, d, a, b, temp[7], 16, 0xf6bb4b60); | |
b = r3(b, c, d, a, temp[10], 23, 0xbebfbc70); | |
a = r3(a, b, c, d, temp[13], 4, 0x289b7ec6); | |
d = r3(d, a, b, c, temp[0], 11, 0xeaa127fa); | |
c = r3(c, d, a, b, temp[3], 16, 0xd4ef3085); | |
b = r3(b, c, d, a, temp[6], 23, 0x04881d05); | |
a = r3(a, b, c, d, temp[9], 4, 0xd9d4d039); | |
d = r3(d, a, b, c, temp[12], 11, 0xe6db99e5); | |
c = r3(c, d, a, b, temp[15], 16, 0x1fa27cf8); | |
b = r3(b, c, d, a, temp[2], 23, 0xc4ac5665); | |
a = r4(a, b, c, d, temp[0], 6, 0xf4292244); | |
d = r4(d, a, b, c, temp[7], 10, 0x432aff97); | |
c = r4(c, d, a, b, temp[14], 15, 0xab9423a7); | |
b = r4(b, c, d, a, temp[5], 21, 0xfc93a039); | |
a = r4(a, b, c, d, temp[12], 6, 0x655b59c3); | |
d = r4(d, a, b, c, temp[3], 10, 0x8f0ccc92); | |
c = r4(c, d, a, b, temp[10], 15, 0xffeff47d); | |
b = r4(b, c, d, a, temp[1], 21, 0x85845dd1); | |
a = r4(a, b, c, d, temp[8], 6, 0x6fa87e4f); | |
d = r4(d, a, b, c, temp[15], 10, 0xfe2ce6e0); | |
c = r4(c, d, a, b, temp[6], 15, 0xa3014314); | |
b = r4(b, c, d, a, temp[13], 21, 0x4e0811a1); | |
a = r4(a, b, c, d, temp[4], 6, 0xf7537e82); | |
d = r4(d, a, b, c, temp[11], 10, 0xbd3af235); | |
c = r4(c, d, a, b, temp[2], 15, 0x2ad7d2bb); | |
b = r4(b, c, d, a, temp[9], 21, 0xeb86d391); | |
ABCDValue.A = unchecked(a + ABCDValue.A); | |
ABCDValue.B = unchecked(b + ABCDValue.B); | |
ABCDValue.C = unchecked(c + ABCDValue.C); | |
ABCDValue.D = unchecked(d + ABCDValue.D); | |
return; | |
} | |
//Manually unrolling these equations nets us a 20% performance improvement | |
private static uint r1(uint a, uint b, uint c, uint d, uint x, int s, uint t) | |
{ | |
// (b + LSR((a + F(b, c, d) + x + t), s)) | |
//F(x, y, z) ((x & y) | ((x ^ 0xFFFFFFFF) & z)) | |
return unchecked(b + LSR((a + ((b & c) | ((b ^ 0xFFFFFFFF) & d)) + x + t), s)); | |
} | |
private static uint r2(uint a, uint b, uint c, uint d, uint x, int s, uint t) | |
{ | |
// (b + LSR((a + G(b, c, d) + x + t), s)) | |
//G(x, y, z) ((x & z) | (y & (z ^ 0xFFFFFFFF))) | |
return unchecked(b + LSR((a + ((b & d) | (c & (d ^ 0xFFFFFFFF))) + x + t), s)); | |
} | |
private static uint r3(uint a, uint b, uint c, uint d, uint x, int s, uint t) | |
{ | |
// (b + LSR((a + H(b, c, d) + k + i), s)) | |
//H(x, y, z) (x ^ y ^ z) | |
return unchecked(b + LSR((a + (b ^ c ^ d) + x + t), s)); | |
} | |
private static uint r4(uint a, uint b, uint c, uint d, uint x, int s, uint t) | |
{ | |
// (b + LSR((a + I(b, c, d) + k + i), s)) | |
//I(x, y, z) (y ^ (x | (z ^ 0xFFFFFFFF))) | |
return unchecked(b + LSR((a + (c ^ (b | (d ^ 0xFFFFFFFF))) + x + t), s)); | |
} | |
// Implementation of left rotate | |
// s is an int instead of a uint becuase the CLR requires the argument passed to >>/<< is of | |
// type int. Doing the demoting inside this function would add overhead. | |
private static uint LSR(uint i, int s) | |
{ | |
return ((i << s) | (i >> (32 - s))); | |
} | |
//Convert input array into array of UInts | |
private static uint[] Converter(byte[] input, int ibStart) | |
{ | |
if (null == input) | |
throw new System.ArgumentNullException("input", "Unable convert null array to array of uInts"); | |
uint[] result = new uint[16]; | |
for (int i = 0; i < 16; i++) | |
{ | |
result[i] = (uint)input[ibStart + i * 4]; | |
result[i] += (uint)input[ibStart + i * 4 + 1] << 8; | |
result[i] += (uint)input[ibStart + i * 4 + 2] << 16; | |
result[i] += (uint)input[ibStart + i * 4 + 3] << 24; | |
} | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Copyright (c) Microsoft Corporation. All rights reserved. | |
using System; | |
using System.Security.Cryptography; | |
// ************************************************************** | |
// * Raw implementation of the MD5 hash algorithm | |
// * from RFC 1321. | |
// * | |
// * Written By: Reid Borsuk and Jenny Zheng | |
// * Copyright (c) Microsoft Corporation. All rights reserved. | |
// ************************************************************** | |
#if SILVERLIGHT | |
public class MD5Managed : HashAlgorithm | |
#else | |
public class MD5Managed : MD5 | |
#endif | |
{ | |
private byte[] _data; | |
private ABCDStruct _abcd; | |
private Int64 _totalLength; | |
private int _dataSize; | |
public MD5Managed() | |
{ | |
base.HashSizeValue = 0x80; | |
this.Initialize(); | |
} | |
public override void Initialize() | |
{ | |
_data = new byte[64]; | |
_dataSize = 0; | |
_totalLength = 0; | |
_abcd = new ABCDStruct(); | |
//Intitial values as defined in RFC 1321 | |
_abcd.A = 0x67452301; | |
_abcd.B = 0xefcdab89; | |
_abcd.C = 0x98badcfe; | |
_abcd.D = 0x10325476; | |
} | |
protected override void HashCore(byte[] array, int ibStart, int cbSize) | |
{ | |
int startIndex = ibStart; | |
int totalArrayLength = _dataSize + cbSize; | |
if (totalArrayLength >= 64) | |
{ | |
Array.Copy(array, startIndex, _data, _dataSize, 64 - _dataSize); | |
// Process message of 64 bytes (512 bits) | |
MD5Core.GetHashBlock(_data, ref _abcd, 0); | |
startIndex += 64 - _dataSize; | |
totalArrayLength -= 64; | |
while (totalArrayLength >= 64) | |
{ | |
Array.Copy(array, startIndex, _data, 0, 64); | |
MD5Core.GetHashBlock(array, ref _abcd, startIndex); | |
totalArrayLength -= 64; | |
startIndex += 64; | |
} | |
_dataSize = totalArrayLength; | |
Array.Copy(array, startIndex, _data, 0, totalArrayLength); | |
} | |
else | |
{ | |
Array.Copy(array, startIndex, _data, _dataSize, cbSize); | |
_dataSize = totalArrayLength; | |
} | |
_totalLength += cbSize; | |
} | |
protected override byte[] HashFinal() | |
{ | |
base.HashValue = MD5Core.GetHashFinalBlock(_data, 0, _dataSize, _abcd, _totalLength * 8); | |
return base.HashValue; | |
} | |
} |
nice code.
can you send me the form please ! to my email mooi88@hotmail.com
and possible for me to build and test it directly on my visual studio
many thanks Wayan from Bali
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nice code.
can you send me the form please ! to my email mooi88@hotmail.com
and possible for me to build and test it directly on my visual studio
many thanks Wayan from Bali