欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言实现SHA1摘要算法

程序员文章站 2022-05-12 20:41:53
...

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

相关标签: c语言 sha1