SHA1算法实现及详解

1 SHA1算法简介

安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。

SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。

2 术语和概念

2.1位(Bit),字节(Byte)和字(Word)

SHA1始终把消息当成一个位(bit)字符串来处理。本文中,一个“字”(Word)是32位,而一个“字节”(Byte)是8位。比如,字符串“abc”可以被转换成一个位字符串:01100001 01100010 01100011。它也可以被表示成16进制字符串: 0x616263.

2.2 运算符和符号

下面的逻辑运算符都被运用于“字”(Word)

X^Y    = X, Y逻辑与

X \/ Y   = X, Y逻辑或

X XOR Y= X, Y逻辑异或

~X     =   X逻辑取反

X+Y定义如下:

字 X 和 Y 代表两个整数 x 和y, 其中 0 <= x < 2^32 且 0 <= y < 2^32. 令整数z = (x + y) mod 2^32. 这时候 0 <= z < 2^32. 将z转换成字Z, 那么就是 Z = X + Y.

循环左移位操作符Sn(X)。X是一个字,n是一个整数,0<=n<=32。Sn(X) = (X<<n)OR(X>>32-n)

X<<n定义如下:抛弃最左边的n位数字,将各个位依次向左移动n位,然后用0填补右边的n位(最后结果还是32位)。X>>n是抛弃右边的n位,将各个位依次向右移动n位,然后在左边的n位填0。因此可以叫Sn(X)位循环移位运算

3 SHA1算法描述

在SHA1算法中,我们必须把原始消息(字符串,文件等)转换成位字符串。SHA1算法只接受位作为输入。假设我们对字符串“abc”产生消息摘要。首先,我们将它转换成位字符串如下:

01100001 01100010 01100011

―――――――――――――

‘a’=97   ‘b’=98   ‘c’=99

这个位字符串的长度为24。下面我们需要5个步骤来计算MD5。

3.1 补位

消息必须进行补位,以使其长度在对512取模以后的余数是448。也就是说,(补位后的消息长度)%512 = 448。即使长度已经满足对512取模后余数是448,补位也必须要进行。

补位是这样进行的:先补一个1,然后再补0,直到长度满足对512取模后余数是448。总而言之,补位是至少补一位,最多补512位。还是以前面的“abc”为例显示补位的过程。

原始信息: 01100001 01100010 01100011

补位第一步:01100001 01100010 01100011 1

首先补一个“1”

补位第二步:01100001 01100010 01100011 10…..0

然后补423个“0”

我们可以把最后补位完成后的数据用16进制写成下面的样子

61626380 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000

现在,数据的长度是448了,我们可以进行下一步操作。

3.2 补长度

所谓的补长度是将原始数据的长度补到已经进行了补位操作的消息后面。通常用一个64位的数据来表示原始消息的长度。如果消息长度不大于2^64,那么第一个字就是0。在进行了补长度的操作以后,整个消息就变成下面这样了(16进制格式)

61626380 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000018

如果原始的消息长度超过了512,我们需要将它补成512的倍数。然后我们把整个消息分成一个一个512位的数据块,分别处理每一个数据块,从而得到消息摘要。

3.3 使用的常量

一系列的常量字K(0), K(1), ... , K(79),如果以16进制给出。它们如下:

Kt = 0x5A827999  (0 <= t <= 19)

Kt = 0x6ED9EBA1 (20 <= t <= 39)

Kt = 0x8F1BBCDC (40 <= t <= 59)

Kt = 0xCA62C1D6 (60 <= t <= 79).

3.4 需要使用的函数

在SHA1中我们需要一系列的函数。每个函数ft (0 <= t <= 79)都操作32位字B,C,D并且产生32位字作为输出。ft(B,C,D)可以如下定义

ft(B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19)

ft(B,C,D) = B XOR C XOR D             (20 <= t <= 39)

ft(B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59)

ft(B,C,D) = B XOR C XOR D                    (60 <= t <= 79).

3.5 计算消息摘要

必须使用进行了补位和补长度后的消息来计算消息摘要。计算需要两个缓冲区,每个都由5个32位的字组成,还需要一个80个32位字的缓冲区。第一个5个字的缓冲区被标识为A,B,C,D,E。第一个5个字的缓冲区被标识为H0,H1, H2, H3, H4

。80个字的缓冲区被标识为W0, W1,..., W79

另外还需要一个一个字的TEMP缓冲区。

为了产生消息摘要,在第4部分中定义的16个字的数据块M1, M2,..., Mn

会依次进行处理,处理每个数据块Mi 包含80个步骤。

在处理每个数据块之前,缓冲区{Hi} 被初始化为下面的值(16进制)

H0 = 0x67452301

H1 = 0xEFCDAB89

H2 = 0x98BADCFE

H3 = 0x10325476

H4 = 0xC3D2E1F0. 
现在开始处理M1, M2, ... , Mn为了处理 Mi,需要进行下面的步骤

(1). 将 Mi 分成 16 个字 W0, W1, ... , W15,  W0 是最左边的字

(2). 对于 t = 16 到 79 令 Wt = S1(Wt-3 XOR Wt-8 XOR Wt- 14 XOR Wt-16).

(3). 令 A = H0, B = H1, C = H2, D = H3, E = H4.

(4) 对于 t = 0 到 79,执行下面的循环

TEMP = S5(A) + ft(B,C,D) + E + Wt + Kt;

E = D; D = C; C = S30(B); B = A; A = TEMP;

(5). 令 H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E. 
在处理完所有的 Mn, 后,消息摘要是一个160位的字符串,以下面的顺序标识

H0 H1 H2 H3 H4.

对于SHA256,SHA384,SHA512。你也可以用相似的办法来计算消息摘要。对消息进行补位的算法完全是一样的。

4 参考文献

1: FIPS 180-1 Secure Hash Standard: http://www.itl.nist.gov/fipspubs/fip180-1.htm

2: Secure Hash Standard: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf

实现代码如下:

using System; 
using System.Collections; 
using System.IO; 
using System.Text;

namespace VerifySHA1
{
    public class MySHA1
    { 
        //  state variables 
        private static UInt32 Message_Digest1      = 0x67452301;
        private static UInt32 Message_Digest2      = 0xEFCDAB89;
        private static UInt32 Message_Digest3      = 0x98BADCFE;
        private static UInt32 Message_Digest4      = 0x10325476;
        private static UInt32 Message_Digest5      = 0xC3D2E1F0;

private static UInt32 SHA1CircularShift(int bits,UInt32 word) 
        {
            return ((word << bits) & 0xFFFFFFFF) | (word) >> (32-(bits));
        }

private static void SHA1_Init()
        { 
            Message_Digest1      = 0x67452301;
            Message_Digest2      = 0xEFCDAB89;
            Message_Digest3      = 0x98BADCFE;
            Message_Digest4      = 0x10325476;
            Message_Digest5      = 0xC3D2E1F0;
        }

private static UInt32[] SHA1_Append(byte[] input)
        { 
            int zeros=0; 
            int ones =1; 
            int size=0; 
            int n = input.Length; 
            int m = n%64; 
            if( m < 56 )
            { 
                zeros = 55-m; 
                size=n-m+64; 
            } 
            else if (m==56)
            { 
                zeros = 63; 
                ones = 1; 
                size=n+8+64; 
            } 
            else
            { 
                zeros = 63-m+56; 
                size=n+64-m+64; 
            }

ArrayList bs = new ArrayList(input); 
            if(ones==1)
            { 
                bs.Add( (byte)0x80 ); // 0x80 = 10000000 
            } 
            for(int i=0;i<zeros;i++)
            { 
                bs.Add( (byte)0 ); 
            }

UInt64 N = (UInt64) n * 8; 
            byte h8=(byte)(N&0xFF); 
            byte h7=(byte)((N>>8)&0xFF); 
            byte h6=(byte)((N>>16)&0xFF); 
            byte h5=(byte)((N>>24)&0xFF); 
            byte h4=(byte)((N>>32)&0xFF); 
            byte h3=(byte)((N>>40)&0xFF); 
            byte h2=(byte)((N>>48)&0xFF); 
            byte h1=(byte)(N>>56); 
            bs.Add(h1); 
            bs.Add(h2); 
            bs.Add(h3); 
            bs.Add(h4); 
            bs.Add(h5); 
            bs.Add(h6); 
            bs.Add(h7); 
            bs.Add(h8); 
            byte[] ts=(byte[])bs.ToArray(typeof(byte));

/* Decodes input (byte[]) into output (UInt32[]). Assumes len is 
             * a multiple of 4. 
             */ 
            UInt32[] output = new UInt32[size/4]; 
            for(Int64 i=0,j=0;i<size;j++,i+=4)
            {
                UInt32 temp = 0;
                temp=temp|(((UInt32)ts[i])<<24);
                temp=temp|(((UInt32)ts[i+1])<<16);
                temp=temp|(((UInt32)ts[i+2])<<8);
                temp=temp|(((UInt32)ts[i+3]));
                output[j] = temp;
            } 
            return output; 
        } 
        private static UInt32[] SHA1_Transform(UInt32[] x)
        { 
            SHA1_Init();

UInt32[] K = {
                             0x5A827999,
                             0x6ED9EBA1,
                             0x8F1BBCDC,
                             0xCA62C1D6
                         };
            int         t;                  
            UInt32    temp;              
            UInt32[]    W = new UInt32[80];             
            UInt32    A, B, C, D, E;

for(int k=0; k<x.Length; k+=16)
            {
                for(t = 0; t < 16; t++)
                {
                    W[t] = x[t+k];
                }

for(t = 16; t < 80; t++)
                {
                    W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
                }

A = Message_Digest1;
                B = Message_Digest2;
                C = Message_Digest3;
                D = Message_Digest4;
                E = Message_Digest5;

for(t = 0; t < 20; t++)
                {
                    temp =  SHA1CircularShift(5,A) +
                        ((B & C) | ((~B) & D)) + E + W[t] + K[0];
                    temp &= 0xFFFFFFFF;
                    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];
                    temp &= 0xFFFFFFFF;
                    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];
                    temp &= 0xFFFFFFFF;
                    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];
                    temp &= 0xFFFFFFFF;
                    E = D;
                    D = C;
                    C = SHA1CircularShift(30,B);
                    B = A;
                    A = temp;
                }

Message_Digest1 = (Message_Digest1 + A) & 0xFFFFFFFF;
                Message_Digest2 = (Message_Digest2 + B) & 0xFFFFFFFF;
                Message_Digest3 = (Message_Digest3 + C) & 0xFFFFFFFF;
                Message_Digest4 = (Message_Digest4 + D) & 0xFFFFFFFF;
                Message_Digest5 = (Message_Digest5 + E) & 0xFFFFFFFF;
            }
        
            return new UInt32[]{Message_Digest1,Message_Digest2,Message_Digest3,Message_Digest4,Message_Digest5}; 
        } 
        public static string SHA1Array(UInt32[] input)
        { 
            StringBuilder sb = new StringBuilder();

for(int i=0; i<input.Length; i++)
            {
                sb.Append( String.Format("{0:X8}", input[i]).ToUpper() );
            }

return sb.ToString(); 
        }

public static string MySHA1String(string message)
        { 
            char[] c = message.ToCharArray(); 
            byte[] b = new byte[c.Length]; 
            for(int i=0;i<c.Length;i++)
            { 
                b[i]=(byte)c[i]; 
            } 
            UInt32[] output = SHA1_Append( b );
            UInt32[] str = SHA1_Transform( output );
            return SHA1Array(str);
            
        } 
        public static string MySHA1File(string fileName)
        { 
            FileStream fs=File.Open(fileName,FileMode.Open,FileAccess.Read); 
            byte[] array=new byte[fs.Length]; 
            fs.Read(array,0,(int)fs.Length); 
            fs.Close(); 
            UInt32[] output = SHA1_Append( array );
            UInt32[] str = SHA1_Transform( output );
            return SHA1Array(str);
        } 
        #region Unit Test
        public static string Test(string message)
        { 
            return "\r\nSHA1 (\""+message+"\") = " + MySHA1String(message); 
        } 
        public static string TestSuite()
        {     
            string s = ""; 
            s+=Test(""); 
            s+=Test("a"); 
            s+=Test("abc"); 
            s+=Test("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); 
            s+=Test("abcdefghijklmnopqrstuvwxyz"); 
            s+=Test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); 
            s+=Test("12345678901234567890123456789012345678901234567890123456789012345678901234567890"); 
            //        StringBuilder sb = new StringBuilder();
            //        for(int i=0; i<1000000; i++)
            //            sb.Append("a");
            //        s+=Test(sb.ToString());

return s;     
        }

public static void Main()
        {
            Console.WriteLine(MySHA1.TestSuite());
            Console.ReadLine();
        }
        #endregion
    } 
}

时间: 2024-12-29 23:28:40

SHA1算法实现及详解的相关文章

【密码学】SHA1算法实现及详解

1 SHA1算法简介 安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA).对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要.当接收到消息的时候,这个消息摘要可以用来验证数据的完整性.在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要. SHA1有如下特性:不可以从消息摘要中复原信

java文本相似度计算(Levenshtein Distance算法(中文翻译:编辑距离算法))----代码和详解

算法代码实现: package com.util; public class SimFeatureUtil { private static int min(int one, int two, int three) { int min = one; if (two < min) { min = two; } if (three < min) { min = three; } return min; } public static int ld(String str1, String str2)

数据挖掘十大算法之CART详解

在2006年12月召开的 IEEE 数据挖掘国际会议上(ICDM, International Conference on Data Mining),与会的各位专家选出了当时的十大数据挖掘算法( top 10 data mining algorithms ),可以参见文献[1].本博客已经介绍过的位列十大算法之中的算法包括: [1] k-means算法(http://blog.csdn.net/baimafujinji/article/details/50570824) [2] 支持向量机SVM

数据挖掘十大算法之决策树详解(2)

在2006年12月召开的 IEEE 数据挖掘国际会议上(ICDM, International Conference on Data Mining),与会的各位专家选出了当时的十大数据挖掘算法( top 10 data mining algorithms ),可以参见文献[1].本博客已经介绍过的位列十大算法之中的算法包括: [1] k-means算法(http://blog.csdn.net/baimafujinji/article/details/50570824) [2] 支持向量机SVM

多数投票算法(Boyer-Moore Algorithm)详解

多数投票算法(Boyer-Moore Algorithm)详解写在前面:我在刷LeetCode 169 时碰到了这个问题,并且在评论区找到了这个方法,不过我发现CSDN上对其进行解读的博客大多停留在知其然而不知其所以然的层面,所以准备在此做一个较为详细的解读,重点在于介绍其原理. 问题描述给定一个无序数组,有n个元素,找出其中的一个多数元素,多数元素出现的次数大于? n/2 ?,注意数组中也可能不存在多数元素. 一般解法先对数组排序,然后取中间位置的元素,再对数据扫描一趟来判断此元素是否为多数元

使用python实现森林算法方法步骤详解

本文和大家分享的是使用python实现森林算法相关内容,一起来看看吧,希望对大家学习python有所帮助. 算法描述 随机森林算法 决策树运行的每一步都涉及到对数据集中的最优**点(best split point)进行贪婪选择(greedy selection). 这个机制使得决策树在没有被剪枝的情况下易产生较高的方差.整合通过提取训练数据库中不同样本(某一问题的不同表现形式)构建的复合树及其生成的预测值能够稳定并降低这样的高方差.这种方法被称作引导**算法(bootstrap aggrega

Dijkstra算法之 Java详解

原文引自:http://www.cnblogs.com/skywang12345/p/3711516.html 迪杰斯特拉算法介绍 迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止. 基本思想 通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算). 此外,引进两个集合S和U.S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是

数据挖掘十大算法之Apriori详解

在2006年12月召开的 IEEE 数据挖掘国际会议上(ICDM, International Conference on Data Mining),与会的各位专家选出了当时的十大数据挖掘算法( top 10 data mining algorithms ),可以参见文献[1].本博客已经介绍过的位列十大算法之中的算法包括: [1] k-means算法(http://blog.csdn.net/baimafujinji/article/details/50570824) [2] 支持向量机SVM

数据挖掘十大算法之决策树详解(1)

在2006年12月召开的 IEEE 数据挖掘国际会议上(ICDM, International Conference on Data Mining),与会的各位专家选出了当时的十大数据挖掘算法( top 10 data mining algorithms ),可以参见文献[1].本博客已经介绍过的位列十大算法之中的算法包括: [1] k-means算法(http://blog.csdn.net/baimafujinji/article/details/50570824) [2] 支持向量机SVM