[C#]: генератор хэшей SHA1
От: mihasic Украина  
Дата: 06.09.06 13:02
Оценка: 16 (1)
При написании одного проекта столкнулся с тем, что не нашел генератора хэшей 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");
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.