与MD5算法一样,也属于是一种哈希算法,并非是密码算法,也是因为其不可逆性,导致其在密码界常有应用,他与MD5的区别:
A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476,E=0xc3d2e1f0
,可以看出比MD5算法多出了一个Efor (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';
完整代码地址:
Linux GCC:github
Windows MSVC:github