C语言实现SHA1摘要算法

这两天用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;
}

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值