参考资料,按字节的角度(加密的数据是字节数组,不是位数组),理解大致过程是:
1. 原字节数组补字节,使得字节数为64的倍数,且补的部分第一个字节是0x80,最后的8个字节存储原字节数组的总位数。
2. 补后的字节数组,每64字节为一块,循环计算。
3. 每64字节的一块再分16份,每份4字节,这16份会扩展计算,和自身的16分,组成80份,再循环计算。
4. 这80份的每份,按公式计算得到结果。
5. 之前各处计算的结果不断累计,即为结果。
import org.apache.commons.codec.digest.DigestUtils;
public class MySHA1Test {
public static void main(String[] args) {
String str = "hello";
// System.out.println(DigestUtils.md5Hex("hello"));
System.out.println(DigestUtils.shaHex(str));
byte[] data = str.getBytes();
// data = new byte[448];
System.out.println(hexToString(sha(data)));
}
public static String sha(String data) {
return hexToString(sha(data.getBytes()));
}
public static byte[] sha(byte[] bytes_data) {
byte[] bytes_all = appendBytes(bytes_data);
return computeBytes(bytes_all);
}
public static byte[] appendBytes(byte[] bytes_src) {
// 原始数据补字节,补后整体是64字节(512位)的整数倍。
// 补的部分,第一bit是1,后面至少有8个字节(64位)用来存放原始数据长度,这样至少补9字节
int length_src = bytes_src.length;
int length_append = 64 - length_src % 64;
if (length_append < 9) {
length_append = 64 + length_append;
}
// 补的数据
byte[] bytes_append = new byte[length_append];
// 第一位是1,其他位是0
bytes_append[0] = (byte) 0x80;
// 后面8字节(64位)存储的是按bit算的长度
long length_src1 = length_src * 8;
bytes_append[length_append - 8] = (byte)(length_src1 >>> 56);
bytes_append[length_append - 7] = (byte)(length_src1 >>> 48);
bytes_append[length_append - 6] = (byte)(length_src1 >>> 40);
bytes_append[length_append - 5] = (byte)(length_src1 >>> 32);
bytes_append[length_append - 4] = (byte)(length_src1 >>> 24);
bytes_append[length_append - 3] = (byte)(length_src1 >>> 16);
bytes_append[length_append - 2] = (byte)(length_src1 >>> 8);
bytes_append[length_append - 1] = (byte)(length_src1 >>> 0);
// 组装完整数据
byte[] bytes_all = new byte[length_src + length_append];
System.arraycopy(bytes_src, 0, bytes_all, 0, length_src);
System.arraycopy(bytes_append, 0, bytes_all, length_src, length_append);
// System.out.println("原始:" + hexToString(bytes_data));
// System.out.println("扩充:" + hexToString(bytes_append));
// System.out.println("结果:" + hexToString(bytes_all));
return bytes_all;
}
public static byte[] computeBytes(byte[] bytes_all) {
int k0 = 0x5A827999; // (0 <= t <= 19)
int k1 = 0x6ED9EBA1; // (20 <= t <= 39)
int k2 = 0x8F1BBCDC; // (40 <= t <= 59)
int k3 = 0xCA62C1D6; // (60 <= t <= 79).
int H0 = 0x67452301;
int H1 = 0xEFCDAB89;
int H2 = 0x98BADCFE;
int H3 = 0x10325476;
int H4 = 0xC3D2E1F0;
int A = 0;
int B = 0;
int C = 0;
int D = 0;
int E = 0;
int A1 = 0;
// 整体数据,按每块64字节512位切分,循环处理每个分块
int block_size = 64;
int block_count = bytes_all.length / block_size;
byte[] block_data = new byte[block_size];
int[] W = new int[80];
for (int block_index = 0; block_index < block_count; block_index++) {
// 取得分块数据
System.arraycopy(bytes_all, block_index * block_size, block_data, 0, block_size);
// 分块数据转为80个子分块
for (int t = 0; t < 80; t++) {
if (t < 16) {
// 前16个子分块,切分16分直接转换得到
int tmp = t * 4;
W[t] = (
((block_data[0 + tmp] << 24) & 0xff000000)
+ ((block_data[1 + tmp] << 16) & 0xff0000)
+ ((block_data[2 + tmp] << 8) & 0xff00)
+ ((block_data[3 + tmp] << 0) & 0xff)
);
} else {
// 后面的子分块,计算得到
// W[t] = S1(W[t-3] XOR W[t-8] XOR W[t-14] XOR W[t-16]).
W[t] = leftShiftLoop((W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]), 1);
}
}
// 80个子分块计算ABCDE,并累加
A = H0;
B = H1;
C = H2;
D = H3;
E = H4;
for (int t = 0; t < 80; t++) {
// 计算ABCDE:A, B, C, D, E ← [(A<<<5)+ ft(B,C,D)+E+Wt+Kt], A, (B<<<30), C, D
if (t < 20) {
A1 = leftShiftLoop(A, 5)
+ ((B & C) | ((~B) & D)) // (B AND C) or ((NOT B) AND D)
+ E + W[t] + k0;
} else if (t < 40) {
A1 = leftShiftLoop(A, 5)
+ (B ^ C ^ D) // B XOR C XOR D
+ E + W[t] + k1;
} else if (t < 60) {
A1 = leftShiftLoop(A, 5)
+ ((B & C) | (B & D) | (C & D)) // (B AND C) or (B AND D) or (C AND D)
+ E + W[t] + k2;
} else {
A1 = leftShiftLoop(A, 5)
+ (B ^ C ^ D) // B XOR C XOR D
+ E + W[t] + k3;
}
E = D;
D = C;
C = leftShiftLoop(B, 30);
B = A;
A = A1;
}
H0 = H0 + A;
H1 = H1 + B;
H2 = H2 + C;
H3 = H3 + D;
H4 = H4 + E;
}
// ABCDE转为结果
byte[] bytes_ret = new byte[20];
int index_ret = 0;
A1 = H0;
bytes_ret[index_ret++] = (byte) ((A1 >>> 24) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 16) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 8) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 0) & 0xFF);
A1 = H1;
bytes_ret[index_ret++] = (byte) ((A1 >>> 24) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 16) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 8) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 0) & 0xFF);
A1 = H2;
bytes_ret[index_ret++] = (byte) ((A1 >>> 24) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 16) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 8) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 0) & 0xFF);
A1 = H3;
bytes_ret[index_ret++] = (byte) ((A1 >>> 24) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 16) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 8) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 0) & 0xFF);
A1 = H4;
bytes_ret[index_ret++] = (byte) ((A1 >>> 24) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 16) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 8) & 0xFF);
bytes_ret[index_ret++] = (byte) ((A1 >>> 0) & 0xFF);
//
return bytes_ret;
}
public static int leftShiftLoop(int d, int n) {
return (d << n) | (d >>> (32 - n));
}
//
private static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public static String hexToString(byte[] data) {
int l = data.length;
char[] out = new char[l << 1];
int i = 0;
for (int j = 0; i < l; ++i) {
out[(j++)] = digits[((0xF0 & data[i]) >>> 4)];
out[(j++)] = digits[(0xF & data[i])];
}
return new String(out);
}
}