參考資料,按字節的角度(加密的數據是字節數組,不是位數組),理解大致過程是:
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);
}
}