C语言实现SHA1摘要算法
程序员文章站
2022-05-12 20:41:53
...
这两天用C语言写了一个基于sha1算法的摘要工具,可以对字符串或者文件进行数字签名,运行结果如下:
说明:
输入字符串:abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg,签名得到15f1b9863855123bb3f7d26739e5b140774abc26;
输入文件路径,这是一个txt文件,里面有内容"你好,CSDN"7个字(两个汉字,一个中文逗号,四个字母),签名得到:59512c6e1981eff0fca12f88bcdbd447f03241a8。
具体C代码附上:
(PS:当宏定义Annotation设为1时可以看到sha1摘要算法的整个运行过程的输出内容,包括512位的数据块的值,16分块以后的数据值,80分块以后的数据值)
// 基于SHA1算法的摘要工具.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#define Annotation 0
#define CHUNK 1024
typedef struct
{
uint32_t IterationValue[5];//存放5个迭代数
}SHA1Values;
typedef struct
{
int filesize;//文件长度
uint8_t* file_str;//文件数据首地址
}FileInfo;
int ft(uint32_t B, uint32_t C, uint32_t D, uint32_t num);
void SHA1Encrypt(uint8_t* str, char* data);
void blocksProcess(uint8_t* bytes, SHA1Values* sha1Values);
void IterationValueInit(SHA1Values* sha1Values);
char* readinput();
int fileread(char* filepath, FileInfo* fileinfo);
size_t ustrlen(uint8_t* ustr);
const uint32_t K[] = { 0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6 };
int main(int argc, char* argv[])
{
char SHA1_data[160] = { 0 };//sha1算法摘要值
FileInfo fileinfo = { 0 };
int input_num, i = 0;
uint8_t* str = NULL;
for (i = 0; i < 3; i++)
printf("\n");
printf("encryption object:\n");
printf("\t1 string\n");
printf("\t2 file\n");
printf("\n");
while (1)
{
printf("please input the number to select the encryption object:");
input_num = getchar();
input_num -= 48;
if (input_num == 1)
{
printf("please input the string for SHA1 encryption:\n");
//读取输入的数据,并将最终结果的字符首地址赋给str
str = (uint8_t*)readinput();
if (str == NULL)
{
printf("memory allocation failed!\n");
continue;
}
SHA1Encrypt(str, SHA1_data);
free(str);
}
else if (input_num == 2)
{
printf("please input the file path:\n");
//读取文件路径,并将最终结果的字符首地址赋给str
char* path = readinput();
if (path == NULL)
{
printf("memory allocation failed!\n");
continue;
}
if (fileread(path, &fileinfo))
{
str = fileinfo.file_str;
//for(i=0;i<ustrlen(str);i++)
// printf("%x\n",str[i]);
SHA1Encrypt(str, SHA1_data);
free(str);
}
}
else
printf("input error!\n");
printf("\n");
}
return 0;
}
//对str进行SHA1摘要计算,并把结果赋给data
void SHA1Encrypt(uint8_t* str, char* data)
{
SHA1Values sha1Values = { 0 };
IterationValueInit(&sha1Values);//对5个迭代常数进行初始化
long str_size = ustrlen(str);//获取无符号字符数组的长度
uint8_t bytes_512bits[64] = {0};
int packets = (int)ceil(str_size * 8.0 / 512);//以512比特为单位获取数据的分块个数
int left_packets = packets;//剩余未处理的分块个数
long str_index = 0;//数据索引位置
#if Annotation
printf("packets: %d str_size:%ld\n", packets, str_size);
#endif
while (left_packets > 0)
{
//当剩余未处理的分块个数为1是执行以下代码
if (left_packets == 1)
{
int j = 0;
#if Annotation
printf("str_index:%d\n", str_index);
#endif
//按512位的长度进行分组
//要注意处理的特例是str_size等于64,它为64时不进入下面的for循环
if (str_size % 64 != 0)
for (; j < str_size % 64; j++)
{
bytes_512bits[j] = str[str_index];
str_index++;
}
else
{
for (; j < 64; j++)
{
bytes_512bits[j] = str[str_index];
str_index++;
}
blocksProcess(bytes_512bits, &sha1Values);
j = 0;
}
//将最后一组数据添入数组以后,判断这组数据的长度,如果小于56,那么直接处理这些数据以后就结束了
//如果大于等于56,则说明还需要再多处理一组数据,因为根据算法流程,需要在最后一组数据后先添1,
//然后补零直至数据长度对512求余后为448,如果最后一组数据长度大于等于56,添1以后补0会超过当前
//64字节的数据块,所以要多处理一组数据。
if (j >= 56)
{
bytes_512bits[j++] = 0x80;
for (; j < 64; j++)
bytes_512bits[j] = 0x00;
blocksProcess(bytes_512bits, &sha1Values);
for (j = 0; j < 56; j++)
bytes_512bits[j] = 0x00;
}
else
{
bytes_512bits[j++] = 0x80;
for (; j < 56; j++)
bytes_512bits[j] = 0x00;
}
//将有效数据长度用8字节表示填充到数据末尾
for (j = 63; j >= 56; j--)
{
long str_size1 = str_size * 8;
for (int k = 0; k < 63 - j; k++)
str_size1 >>= 8;
bytes_512bits[j] = str_size1 % 256;
}
blocksProcess(bytes_512bits, &sha1Values);
}
//当数据分块个数超过1个是执行以下代码
else
{
//以512比特(64字节)为单位对数组进行赋值
for (int j = 0; j < 64; j++)
{
bytes_512bits[j] = str[str_index];
str_index++;
}
//对64字节空间的数组进行处理,将迭代值赋给sha1Values
blocksProcess(bytes_512bits, &sha1Values);
}
left_packets--;
}
sprintf_s(data,41, "%08x%08x%08x%08x%08x",
sha1Values.IterationValue[0],
sha1Values.IterationValue[1],
sha1Values.IterationValue[2],
sha1Values.IterationValue[3],
sha1Values.IterationValue[4]);
printf("fanal SHA1 value: %s\n", data);
}
//64字节数据块处理
void blocksProcess(uint8_t* bytes, SHA1Values* sha1Values)
{
static int iteration_times = 1;
int i = 0;
uint32_t A = sha1Values->IterationValue[0];
uint32_t B = sha1Values->IterationValue[1];
uint32_t C = sha1Values->IterationValue[2];
uint32_t D = sha1Values->IterationValue[3];
uint32_t E = sha1Values->IterationValue[4];
//512位的明文分组打印
#if Annotation
printf("512位的明文分组打印\n");
for (i = 0; i < 64; i++)
{
printf("0x%02x ", bytes[i]);
if ((i + 1) % 8 == 0)
printf("\n");
}
#endif
//对于每个512位的明文分组,SHA1将其再分成16份更小的明文分组,M[t](t= 0, 1,……15)
int bytes_divided_512bits[16] = { 0 };
//int bytes_divided_512bits_size = sizeof(bytes_divided_512bits)/sizeof(bytes_divided_512bits[0]);
for (i = 0; i < 16; i++)
{
for (int j = 0; j < 4; j++)
{
bytes_divided_512bits[i] <<= 8;
bytes_divided_512bits[i] += bytes[j + 4 * i];
}
}
//16份明文分组打印
#if Annotation
printf("16份明文分组打印\n");
for (i = 0; i < 16; i++)
{
printf("0x%08x ", bytes_divided_512bits[i]);
if ((i + 1) % 4 == 0)
printf("\n");
}
#endif
//将这16个子明文分组扩充到80个子明文分组,记为W[t](t= 0, 1,……79)
int bytes_divided2_512bits[80] = { 0 };
//当0<t<15时,Wt = Mt
for (i = 0; i < 16; i++)
{
bytes_divided2_512bits[i] = bytes_divided_512bits[i];
}
//当16<t<79时,Wt = ( Wt-3 ⊕ Wt-8⊕ Wt-14⊕ Wt-16) <<< 1
for (i = 16; i < 80; i++)
{
bytes_divided2_512bits[i] = _rotl(
bytes_divided2_512bits[i - 3] ^ bytes_divided2_512bits[i - 8] ^
bytes_divided2_512bits[i - 14] ^ bytes_divided2_512bits[i - 16], 1);
}
//80个子明文分组打印
#if Annotation
printf("80个子明文分组打印\n");
for (i = 0; i < 80; i++)
{
printf("0x%08x ", bytes_divided2_512bits[i]);
if ((i + 1) % 4 == 0)
printf("\n");
}
#endif
//80次迭代,分4组进行,每组迭代常数和逻辑函数组合不同
for (i = 0; i < 80; i++)
{
uint32_t temp = _rotl(A, 5) + E + bytes_divided2_512bits[i] + K[i / 20] + ft(B, C, D, i / 20);
E = D;
D = C;
C = _rotl(B, 30);
B = A;
A = temp;
}
sha1Values->IterationValue[0] += A;
sha1Values->IterationValue[1] += B;
sha1Values->IterationValue[2] += C;
sha1Values->IterationValue[3] += D;
sha1Values->IterationValue[4] += E;
#if Annotation
printf("iteration_times: %d ,the SHA1 value is %08x%08x%08x%08x%08x\n", iteration_times++,
sha1Values->IterationValue[0],
sha1Values->IterationValue[1],
sha1Values->IterationValue[2],
sha1Values->IterationValue[3],
sha1Values->IterationValue[4]);
#endif
}
//迭代数初始化
void IterationValueInit(SHA1Values* sha1Values)
{
sha1Values->IterationValue[0] = 0x67452301;
sha1Values->IterationValue[1] = 0xEFCDAB89;
sha1Values->IterationValue[2] = 0x98BADCFE;
sha1Values->IterationValue[3] = 0x10325476;
sha1Values->IterationValue[4] = 0xC3D2E1F0;
}
//逻辑函数
int ft(uint32_t B, uint32_t C, uint32_t D, uint32_t num)
{
switch (num)
{
case 0:
return (B & C) | (~B & D);
case 1:
return B ^ C ^ D;
case 2:
return (B & C) | (B & D) | (C & D);
case 3:
return B ^ C ^ D;
}
return 0;
}
//接收用户输入,为防止溢出,给指针动态分配内存
char* readinput()
{
char* old_input = NULL;
char* new_input = NULL;
char tempbuf[CHUNK];
size_t inputlen = 0, templen = 0;
rewind(stdin);
do {
fgets(tempbuf, CHUNK, stdin);
templen = strlen(tempbuf);
new_input = (char*)realloc(old_input, inputlen + templen + 1);
if (new_input == NULL)
{
return NULL;
}
old_input = new_input;
memcpy(new_input + inputlen, tempbuf, templen + 1);
inputlen += templen;
} while (templen == CHUNK - 1 && tempbuf[CHUNK - 2] != '\n');
new_input[strlen(new_input) - 1] = '\0';
return new_input;
}
//文件读取函数,读取文件路径,将内容传递给fileinfo结构体
int fileread(char* filepath, FileInfo* fileinfo)
{
FILE* fp = NULL;
uint8_t* new_file = NULL;
uint8_t* old_file = NULL;
uint8_t buf[10];
int read_count, filelen = 0;
fopen_s(&fp,filepath, "r");
if (fp == NULL)
{
//打开文件失败
printf("open error!\n");
return 0;
}
do
{
read_count = fread(buf, 1, sizeof(buf), (FILE*)fp);
if (read_count == 0)
{
printf("the file is empty!\n");
return 0;
break;
}
new_file = (uint8_t*)realloc(old_file, read_count + filelen + 1);
if (new_file == NULL)
{
free(old_file);
return 0;
}
old_file = new_file;
memcpy(new_file + filelen, buf, read_count);
filelen += read_count;
//for(int i=0;i<read_count;i++)
// printf("%x\n",buf[i]);
} while (read_count == sizeof(buf));
fclose(fp);
new_file[filelen] = '\0';
fileinfo->file_str = new_file;
fileinfo->filesize = filelen;
return 1;
}
//获取无符号字节数组的长度,ustr是无符号字节数组的首地址指针
size_t ustrlen(uint8_t* ustr)
{
size_t size = 0;
while (ustr[size++] != '\0');
size--;
return size;
}
上一篇: 远程桌面连接的多媒体重定向功能的作用及其主要优势介绍
下一篇: SHA1散列算法及其C++实现