因为分组方式和MD5一样,但是大小端字节不一样??所以处理的时候需要对MD5分组方式作出调整。
原理:
位,字节和字
SHA1始终把消息当成一个位(bit)字符串来处理。本文中,一个字(Word)是32位,而一个字节(Byte)是8位。比如,字符串“abc”可以被转换成一个位字符串:01100001 01100010 01100011。它也可以被表示成16进制字符串: 0x616263.
运算符和符号
下面的逻辑运算符都被运用于“字”(Word)
X & Y = X, Y逻辑与
X | Y = X, Y逻辑或
X ^ 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)
在SHA1算法中,我们必须把原始消息(字符串,文件等)转换成位字符串。SHA1算法只接受位作为输入。假设我们对字符串“abc”产生
消息摘要。首先,我们将它转换成位字符串如下:
01100001 01100010 01100011
―――――――――――――
‘a’=97 ‘b’=98 ‘c’=99
这个位字符串的长度为24。下面我们需要5个步骤来计算消息摘要MAC。
补位
消息必须进行补位,以使其长度在对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了,我们可以进行下一步操作。
补长度
所谓的补长度是将
原始数据的长度补到已经进行了补
位操作的消息后面。通常用一个64位的数据来表示原始消息的长度。如果消息长度不大于2^64,那么第一个字就是0。在进行了补长度的操作以后,整个消息就变成下面这样了(16进制格式)
61626380 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000018
使用的常量
一系列的常量字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).
使用的函数
在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).
计算消息摘要
必须使用进行了补位和补长度后的消息来计算消息摘要。计算需要两个
缓冲区,每个都由5个32位的字组成,还需要一个80个32位字的缓冲区。第一个5个字的缓冲区被标识为A,B,C,D,E。第二个5个字的缓冲区被标识为H0, H1, H2, H3, H4
。80个字的缓冲区被标识为W0, W1,..., W79
另外还需要一个一个字的TEMP缓冲区。
会依次进行处理,处理每个数据块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 令
W[t] = S1(W[t-3] XOR W[t-8] XOR W[t-14] XOR W[t-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。你也可以用相似的办法来计算消息摘要。对消息进行补位的算法完全是一样的。
SHA1在许多安全协议中广为使用,包括TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被视为是
MD5(更早之前被广为使用的散列函数)的后继者。
import java.util.Scanner;
public class SHA1
{
private final int A=0x67452301;
private final int B=0xefcdab89;
private final int C=0x98badcfe;
private final int D=0x10325476;
private final int E=0xc3d2e1f0;
private int Atemp,Btemp,Ctemp,Dtemp,Etemp;
private void init()
{
Atemp = A;
Btemp = B;
Ctemp = C;
Dtemp = D;
Etemp = E;
}
private int shift(int a , int s) //循环左移
{
return (a << s) | (a >>> (32 - s));
}
private int[] add(String str) //字符串转换为int并完成添加
{
int num = ((str.length() + 8) / 64) + 1;
int strByte[] = new int[num*16];
for(int i = 0 ; i < num*16 ; i ++)
{
strByte[i] = 0;
}
int len = str.length() , i;
for(i = 0 ; i < len ; i ++)
{
strByte[i>>2] |= (str.charAt(i) & 0xff )<<(((i^3) % 4)*8); //四个字符一个int,每个字符的8位放到对应位置
}
strByte[i>>2] |= 0x80 << (((i^3) % 4)*8); //最后位加1后补0
strByte[num*16 - 1] = str.length() * 8; //保存长度
return strByte;
}
private void Mainloop(int num[]) //循环加密
{
int w[] = new int[80];
for(int i = 0 ; i < 16 ; i ++) w[i] = num[i];
for(int i = 16 ; i < 80 ; i ++)
{
w[i] = shift(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
}
int F , k; //每轮的循环运算顺序有g决定,F是决定abc的
int a = Atemp , b = Btemp , c = Ctemp , d = Dtemp , e = Etemp;
for(int i = 0 ; i < 80 ; i ++) //四轮运算
{
if(i < 20)
{
F = (b & c) | (( ~b ) & d);
k = 0x5a827999;
}
else if(i < 40)
{
F = b ^ c ^ d ;
k = 0x6ed9eba1;
}
else if(i < 60)
{
F = (b & c) | (b & d) | ( c & d);
k = 0x8f1bbcdc;
}
else
{
F = b ^ c ^ d ;
k = 0xca62c1d6;
}
int tmp = shift(a , 5) + e + w[i] + k + F;
e = d ;
d = c ;
c = shift(b , 30) ;
b = a;
a = tmp;
}
Atemp = a + Atemp; //保存当前组结果
Btemp = b + Btemp;
Ctemp = c + Ctemp;
Dtemp = d + Dtemp;
Etemp = e + Etemp;
}
private String changeHex(int a) //转换为字符串
{
String str = "";
for(int i = 0 ; i < 4 ; i ++)
{
str += String.format("%2s" , Integer.toHexString(((a >> (i^3)*8) % (1<<8)) & 0xff)).replace(' ', '0') ;
}
return str;
}
public String Md5(String str)
{
init();
int strByte[] = add(str);
for(int i = 0 ; i < strByte.length / 16 ; i ++)
{
int num[] = new int[16];
for(int j = 0 ; j < 16 ; j ++)
{
num[j] = strByte[i*16+j];
//System.out.println(num[j]);
}
Mainloop(num);
}
return changeHex(Atemp) + changeHex(Btemp) + changeHex(Ctemp) + changeHex(Dtemp) + changeHex(Etemp);
}
private static SHA1 instance;
public static SHA1 getInstance()
{
if(instance == null)
{
instance = new SHA1();
}
return instance;
}
private SHA1(){};
public static void main(String[] args) throws Exception
{
Scanner cin = new Scanner(System.in);
String str = new String();
while(cin.hasNext())
{
str = cin.next();
String str2 = SHA1.getInstance().Md5(str);
System.out.println(str2);
}
//System.out.println(md5(str));
}
}