SHA-1
與MD5算法一樣,也屬於是一種哈希算法,並非是密碼算法,也是因爲其不可逆性,導致其在密碼界常有應用,他與MD5的區別:
- 輸出定長爲20字節
- 安全強度更強
- 算法計算過程略有不同
算法實現過程
- 與其他哈希算法一樣,首先肯定是對原文數據進行填充,輸入不定長度的原文,要使其長度滿足: “長度 mod 512bit =448bit”
- 不管其長度是否滿足,都要在其後至少添加一個十進制數128即0x80,其二進制形式爲1000 0000,如果長度不滿足就在添加完0x80後繼續填充0,一致填充到滿足"長度 mod 512bit =448bit "爲止
- 在添加完0x80後添加原文數據長度,記住原文數據長度要佔八字節,最後記得初始化五組32bit的幻數,分別爲:
A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476,E=0xc3d2e1f0
,可以看出比MD5算法多出了一個E - 在進行完上述步驟後,不出錯的話,此時填充後的數據長度剛好是512bit即64byte的倍數,然後開始每512bit一算
- 現在我們每組能獲取一個64字節的數據,但是要注意SHA-1算法與MD5算法不同就在於,MD5算法是再將這64字節分爲4組每組4個字節共16個字節共四輪運算,4*16最後將這64個字節處理完,而SHA-1則也是分爲四組每組4個字節,一共四輪計算,但不同的是因爲其輸出長度固定爲20字節,所以我們要將填充後的數據長度再進行處理,處理過程:
for (int i = 0; i < 16; i++)
{
int j = 4 * i;
ulong_data[i] = ((unsigned int)char_data[j]) << 24 |
((unsigned int)char_data[1 + j]) << 16 |
((unsigned int)char_data[2 + j]) << 8 |
((unsigned int)char_data[3 + j]) << 0;
}
for (int i = 16; i < 80; i++)
{
ulong_data[i] = ulong_data[i - 16] ^ ulong_data[i - 14] ^
ulong_data[i - 8] ^ ulong_data[i - 3];
ulong_data[i] = (ulong_data[i] << 1) | (ulong_data[i] >> 31);
}
首先是將原本的64個字節的字符數據統一處理成一組四個字節的無符號整型數據,然後再在其後填充,一填充到滿足80個無符號整形數據位置
6. 現在處理就完成了,我們開始用80/20剛好得4組每組四個字節
7. 然後將ABCDE五組幻數進行移動,D->E,C->E,然後對C進行計算處理,A->B然後對A進行計算處理,一次循環計算
8. 最終將每輪計算的ABCDE相加得出最後結果
代碼實現
首先初始化一下我們要使用到的一些常量比如五組幻數:
unsigned int k = 0, f = 0;
unsigned int temp_ul_text[80];
unsigned int temp_A = 0;
unsigned int h[] = { 0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0 };
然後開始填充原數據
/*
*開始填充原文
*/
size_t n_len = ((len + 8) / 64) * 64 + 56 + 8;
unsigned char*n_text = (unsigned char*)malloc(n_len);
memset(n_text, 0x00, n_len);
memcpy(n_text, text, len);
n_text[len] = 0x80;
unsigned char c_lens[8];
memset(c_lens, 0x00, 8);
unsigned int temp_len = (unsigned int)(len * 8);
memcpy(c_lens, &temp_len, sizeof(unsigned long));
/*
*顛倒數據長度存儲的端序
*/
for (int i = 7; i >= 4; i--)
{
int y = c_lens[i];
c_lens[i] = c_lens[7-i];
c_lens[7-i] = y;
}
memcpy(n_text + (n_len - 8), c_lens, 8);
這裏要注意要將長度位置顛倒一下,把第一個字節放到最後一個,第二個放到倒二…以此類推,我不知道爲什麼SHA-1算法必須要這樣處理,而MD5不需要,但經過我的測試,如果SHA-1不這麼做最後結果是錯誤的
現在開始64字節一組開始計算
/*
*分組計算,512bits一組
*/
for (int i = 0; i < n_len; i += 64)
{
//A=H[0],B=H[1],C=H[2],D=H[3],E=H[4]
unsigned int H[5] = { 0,0,0,0,0 };
unsigned char temp_text[64];
memset(temp_text, 0x00, 64);
memset(temp_ul_text, 0x00, 80*sizeof(unsigned int));
memcpy(H, h, 5 * (sizeof(unsigned int)));
memcpy(temp_text, (n_text + i), 64);
CharToUlong(temp_text, temp_ul_text);
for (int j = 0; j < 80; j++)
{
switch ((int)j/20)
{
case 0:
k = 0x5a827999;
f = (H[1] & H[2]) | ((~H[1]) & H[3]);
break;
case 1:
k = 0x6ed9eba1;
f = H[1] ^ H[2] ^ H[3];
break;
case 2:
k = 0x8f1bbcdc;
f = (H[1] & H[2]) | (H[1] & H[3]) | (H[2] & H[3]);
break;
case 3:
k = 0xca62c1d6;
f = H[1] ^ H[2] ^ H[3];
break;
default:
break;
}
//ABCDE位置移動
temp_A = ((H[0] << 5) | (H[0] >> 27)) + f + H[4] + temp_ul_text[j] + k;
H[4] = H[3];
H[3] = H[2];
H[2] = (H[1] << 30) | (H[1] >> 2);
H[1] = H[0];
H[0] = temp_A;
}
//ABCDE累加
for (int k = 0; k < 5; k++)
h[k] += H[k];
}
在這裏還有一個CharToUlong函數,他的實現爲:
inline void SHA1::CharToUlong
(
_In_ const unsigned char* char_data,
_Inout_ unsigned int* ulong_data
)
{
for (int i = 0; i < 16; i++)
{
int j = 4 * i;
ulong_data[i] = ((unsigned int)char_data[j]) << 24 |
((unsigned int)char_data[1 + j]) << 16 |
((unsigned int)char_data[2 + j]) << 8 |
((unsigned int)char_data[3 + j]) << 0;
}
for (int i = 16; i < 80; i++)
{
ulong_data[i] = ulong_data[i - 16] ^ ulong_data[i - 14] ^
ulong_data[i - 8] ^ ulong_data[i - 3];
ulong_data[i] = (ulong_data[i] << 1) | (ulong_data[i] >> 31);
}
}
最後將我們之前申請的內存釋放,並將結果轉換爲字符串即可
free(n_text);
//字符串格式化返回輸出結果
for (int o = 0; o < 5; o++)
{
sprintf_s(outData, 40, "%08x", h[o]);
outData += 8;
}
outData[40] = '\0';