При написании одного проекта столкнулся с тем, что не нашел генератора хэшей SHA1 в .NET Compact Framework 1.0, и поэтому нашел
RFC 3174, где есть исходник на C. Т.к. Си я знаю, то перенос кода не составил больших трудностей и я написал это (правда при этом не особо вникал в сам алгоритм):
using System;
namespace MyCrypting
{
public class SHA1
{
public enum SHA
{
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError /* called Input after Result */
}
public const int SHA1HashSize = 20;
public class SHA1Context
{
public uint[] Intermediate_Hash = new uint[SHA1HashSize/4]; /* Message Digest */
public uint Length_Low; /* Message length in bits */
public uint Length_High; /* Message length in bits */
/* Index into message block array */
public int Message_Block_Index;
public byte[] Message_Block = new byte[64]; /* 512-bit message blocks */
public int Computed; /* Is the digest computed? */
public SHA Corrupted; /* Is the message digest corrupted? */
}
private static uint SHA1CircularShift(int bits, uint word) {
return (((word) << (bits)) | ((word) >> (32-(bits))));
}
public static SHA SHA1Reset(ref SHA1Context context) {
if (context == null) {
return SHA.shaNull;
}
context.Length_Low = 0;
context.Length_High = 0;
context.Message_Block_Index = 0;
context.Intermediate_Hash[0] = 0x67452301;
context.Intermediate_Hash[1] = 0xEFCDAB89;
context.Intermediate_Hash[2] = 0x98BADCFE;
context.Intermediate_Hash[3] = 0x10325476;
context.Intermediate_Hash[4] = 0xC3D2E1F0;
context.Computed = 0;
context.Corrupted = 0;
return SHA.shaSuccess;
}
public static SHA SHA1Input(ref SHA1Context context, byte[] message_array) {
if (message_array == null || context == null)
return SHA.shaNull;
uint length = (uint) message_array.Length;
if (length == 0)
{
return SHA.shaSuccess;
}
if (context.Computed != 0)
{
context.Corrupted = SHA.shaStateError;
return SHA.shaStateError;
}
if (context.Corrupted != SHA.shaSuccess) {
return context.Corrupted;
}
int j = 0;
while((length--) != 0 && context.Corrupted == SHA.shaSuccess) {
context.Message_Block[context.Message_Block_Index++] = (byte) (message_array[j] & 0xFF);
context.Length_Low += 8;
if (context.Length_Low == 0) {
context.Length_High++;
if (context.Length_High == 0) {
/* Message is too long */
context.Corrupted = SHA.shaInputTooLong;
}
}
if (context.Message_Block_Index == 64) {
SHA1ProcessMessageBlock(ref context);
}
j++;
}
return SHA.shaSuccess;
}
public static SHA SHA1Result(ref SHA1Context context, ref byte[] Message_Digest/*[SHA1HashSize]*/) {
int i;
if (context == null || Message_Digest == null || Message_Digest.Length != SHA1HashSize) {
return SHA.shaNull;
}
if (context.Corrupted != SHA.shaSuccess) {
return context.Corrupted;
}
if (context.Computed == 0) {
SHA1PadMessage(ref context);
for (i = 0; i < 64; ++i) {
/* message may be sensitive, clear it out */
context.Message_Block[i] = 0;
}
context.Length_Low = 0; /* and clear length */
context.Length_High = 0;
context.Computed = 1;
}
for (i = 0; i < SHA1HashSize; ++i) {
Message_Digest[i] = (byte) (context.Intermediate_Hash[i>>2] >> 8 * (3 - (i & 0x03)));
}
return SHA.shaSuccess;
}
private static void SHA1ProcessMessageBlock(ref SHA1Context context) {
uint[] K = new uint[4] { /* Constants defined in SHA-1 */
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; /* Loop counter */
uint temp; /* Temporary word value */
uint[] W = new uint[80]; /* Word sequence */
uint A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = 0; t < 16; t++) {
W[t] = (uint) (context.Message_Block[t * 4] << 24);
W[t] |= (uint)((context.Message_Block[t * 4 + 1]) << 16);
W[t] |= (uint)((context.Message_Block[t * 4 + 2]) << 8);
W[t] |= context.Message_Block[t * 4 + 3];
}
for (t = 16; t < 80; t++) {
W[t] = SHA1CircularShift(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = context.Intermediate_Hash[0];
B = context.Intermediate_Hash[1];
C = context.Intermediate_Hash[2];
D = context.Intermediate_Hash[3];
E = context.Intermediate_Hash[4];
for (t = 0; t < 20; t++) {
temp = SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = temp;
}
for (t = 20; t < 40; t++) {
temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1];
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = temp;
}
for (t = 40; t < 60; t++) {
temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = temp;
}
for (t = 60; t < 80; t++) {
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context.Intermediate_Hash[0] += A;
context.Intermediate_Hash[1] += B;
context.Intermediate_Hash[2] += C;
context.Intermediate_Hash[3] += D;
context.Intermediate_Hash[4] += E;
context.Message_Block_Index = 0;
}
private static void SHA1PadMessage(ref SHA1Context context) {
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second block.
*/
if (context.Message_Block_Index > 55) {
context.Message_Block[context.Message_Block_Index++] = 0x80;
while (context.Message_Block_Index < 64) {
context.Message_Block[context.Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(ref context);
while (context.Message_Block_Index < 56) {
context.Message_Block[context.Message_Block_Index++] = 0;
}
}
else {
context.Message_Block[context.Message_Block_Index++] = 0x80;
while (context.Message_Block_Index < 56) {
context.Message_Block[context.Message_Block_Index++] = 0;
}
}
/*
* Store the message length as the last 8 octets
*/
context.Message_Block[56] = (byte) ((context.Length_High >> 24) & 0xFF);
context.Message_Block[57] = (byte) ((context.Length_High >> 16) & 0xFF);
context.Message_Block[58] = (byte) ((context.Length_High >> 8) & 0xFF);
context.Message_Block[59] = (byte) ((context.Length_High) & 0xFF);
context.Message_Block[60] = (byte) ((context.Length_Low >> 24) & 0xFF);
context.Message_Block[61] = (byte) ((context.Length_Low >> 16) & 0xFF);
context.Message_Block[62] = (byte) ((context.Length_Low >> 8) & 0xFF);
context.Message_Block[63] = (byte) ((context.Length_Low) & 0xFF);
SHA1ProcessMessageBlock(ref context);
}
public static string GetHash(string input) {
if (input == null)
return null;
SHA1Context sha = new SHA1.SHA1Context();
SHA err;
byte[] Message_Digest = new byte[20];
err = SHA1Reset(ref sha);
if (err != SHA.shaSuccess)
return null;
err = SHA1Input(ref sha, System.Text.Encoding.ASCII.GetBytes(input));
if (err != SHA.shaSuccess)
return null;
err = SHA1Result(ref sha, ref Message_Digest);
if (err != SHA.shaSuccess)
return null;
string s = "";
for (int i = 0; i < 20; i++) {
s += string.Format("{0:x2}", Message_Digest[i]);
}
return s;
}
}
}
Данный код является рабочим, хотя написан "на скорую руку" и, возможно, не содержит необходимого числа проверок, так что если есть предложения и пожелания, готов выслушать.
Простой пример использования:
string result = SHA1.GetHash("Test string");