程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> C語言入門知識 >> SHA1校驗算法C語言實現

SHA1校驗算法C語言實現

編輯:C語言入門知識

SHA1安全哈希算法:對於長度小於2^64位的消息(1M = 1024k,1K = 1024字節,1BYTE = 8bit 可以想象一下2的63次方位可以表示一個多大的數據文件),SHA1會產生一個160位的消息摘要。當接收到消息的時候,這個消息摘要可以用來驗證數據的完整性。在傳輸的過程中,數據很可能會發生變化,那麼這時候就會產生不同的消息摘要。 SHA1有如下特性:不可以從消息摘要中復原信息(不可逆性);兩個不同的消息不會產生同樣的消息摘要,(但會有1x10 ^ 48分之一的機率出現相同的消息摘要,一般使用時忽略)。

SHA1和MD5的算法都是從MD4算法改進而來的2種算法,基本思路都是將信息分成N個分組,每組64個字節,每個分組都進行摘要運算。當一個分組的摘要運算完畢後,將上一個分組的結果也用於下一個分組的運算。信息的長度(注意是bit位長度,不是字節長度)用64位表示,也要參加信息摘要運算,而且是放在最後一個分組的末尾,所以長度信息要占據8個字節(一個字節為8位)。如果信息數據最後一個分組長度小於64個字節,在後面添加0x80標志結束,如果此時數據+結束標志已經<=56個字節,還可以放入長度數據,就在結束標志到第56個字節補0,然後放入長度,如果此時信息數據+結束標志已經大於56字節,那麼這個分組後面補0,進行一次摘要運算,然後再建立一個分組,前面全部補0,最後16個字節放長度,再進行一次摘要。

需要注意的地方如下:

SHA1是20個字節(也就是前面說的160位)。SHA1的分組信息運算,SHA1的算法認為DWORD是BIG-ENDIAN的。所以在不同字節序的主機上要進行轉換。放入最後一個分組的長度信息,是原始數據長度,而且是BIT位長度,其是一個uint64_t,SHA1算法則要求這個長度是BIG-ENDIAN的。不同的平台要進行轉換。當然生成的結果,SHA1要求結果是BIG-ENDIAN的,不同的平台還是要進行轉換。

貼一個摘要處理過程的分組信息,幫助理解。例如要處理的數據是3個字節字符串”abc”:

這個字符串在SHA1算法中,只有一個分組,如下,十六進制的18標識24個bit3個字節。(0X18表示長度 "abc"三個8位的字符也就是24位,24的十六進制也就是0X18)

 

61 62 63 80 00 00 00 00 00 00 00 00 00 00 00 00

 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

 

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18

還是先來體驗以下結果:(網上下載一個生成SHA1校驗碼的軟件,然後和程序生成的校驗碼對比看看是否一樣)

建立一個test.txt文件,輸入abc三個字符,生成校驗碼:

\

程序運行生成校驗碼截圖:(輸入的數據也是字符“abc”)

\

程序的C代碼也不是我本人寫的,這個也是直接在CSDN上找的資源!這裡借前輩大神寫的代碼,簡要的分析學習一下。

直接上代碼(英文注釋寫的比較詳細):

 

/*
* sha1.h
*
* Description:
* This is the header file for code which implements the Secure
* Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
* April 17, 1995.
*
* Many of the variable names in this code, especially the
* single character names, were used because those were the names
* used in the publication.
*
* Please read the file sha1.c for more information.
*
*/

#ifndef _SHA1_H_
#define _SHA1_H_
#include 
/*
* If you do not have the ISO standard stdint.h header file, then you
* must typdef the following:
* name meaning
* uint32_t unsigned 32 bit integer
* uint8_t unsigned 8 bit integer (i.e., unsigned char)
* int_least16_t integer of >= 16 bits
*
*/
#ifndef _SHA_enum_
#define _SHA_enum_
enum
{
	shaSuccess = 0,
	shaNull,         /* Null pointer parameter */
	shaInputTooLong, /* input data too long */
	shaStateError    /* called Input after Result */
};
#endif
#define SHA1HashSize 20
/*
* This structure will hold context information for the SHA-1
* hashing operation
*/
typedef struct SHA1Context
{
	uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
	uint32_t Length_Low; /* Message length in bits */
	uint32_t Length_High; /* Message length in bits */
	/* Index into message block array */
	int_least16_t Message_Block_Index;
	uint8_t Message_Block[64]; /* 512-bit message blocks */
	int Computed; /* Is the digest computed? */
	int Corrupted; /* Is the message digest corrupted? */
} SHA1Context;


/*
* Function Prototypes
*/

int SHA1Reset( SHA1Context *);
int SHA1Input( SHA1Context *,const uint8_t *,unsigned int);
int SHA1Result( SHA1Context *,uint8_t Message_Digest[SHA1HashSize]);

#endif

SHA1.C

 

 

/*
* sha1.c
*
* Description:
* This file implements the Secure Hashing Algorithm 1 as
* defined in FIPS PUB 180-1 published April 17, 1995.
*
* The SHA-1, produces a 160-bit message digest for a given
* data stream. It should take about 2**n steps to find a
* message with the same digest as a given message and
* 2**(n/2) to find any two messages with the same digest,
* when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code
* uses  (included via "sha1.h" to define 32 and 8
* bit unsigned integer types. If your C compiler does not
* support 32 bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits
* long. Although SHA-1 allows a message digest to be generated
* for messages of any number of bits less than 2^64, this
* implementation only works with messages with a length that is
* a multiple of the size of an 8-bit character.
*
*/

#include "SHA1.h"

#ifdef __cplusplus
extern "C"
{
#endif

/*
* Define the SHA1 circular left shift macro
*/
#define SHA1CircularShift(bits,word) \
	(((word) << (bits)) | ((word) >> (32-(bits))))
/* Local Function Prototyptes */
void SHA1PadMessage(SHA1Context *);
void SHA1ProcessMessageBlock(SHA1Context *);
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new SHA1 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Reset(SHA1Context *context)//初始化狀態
{
	if (!context)
	{
		return shaNull;
	}
	context->Length_Low = 0;
	context->Length_High = 0;
	context->Message_Block_Index = 0;
	context->Intermediate_Hash[0] = 0x67452301;//取得的HASH結果(中間數據)
	context->Intermediate_Hash[1] = 0xEFCDAB89;
	context->Intermediate_Hash[2] = 0x98BADCFE;
	context->Intermediate_Hash[3] = 0x10325476;
	context->Intermediate_Hash[4] = 0xC3D2E1F0;
	context->Computed = 0;
	context->Corrupted = 0;
	return shaSuccess;
}


/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array provided by the caller.
* NOTE: The first octet of hash is stored in the 0th element,
* the last octet of hash in the 19th element.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*
*/
int SHA1Result( SHA1Context *context,uint8_t Message_Digest[SHA1HashSize])
{
	int i;
	if (!context || !Message_Digest)
	{
		return shaNull;
	}
	if (context->Corrupted)
	{
		return context->Corrupted;
	}
	if (!context->Computed)
	{
		SHA1PadMessage(context);
		for(i=0; i<64; ++i)
		{
			/* message may be sensitive, clear it out */
			context->Message_Block[i] = 0;
		}
		context->Length_Low = 0; /* and clear length */
		context->Length_High = 0;
		context->Computed = 1;
	}
	for(i = 0; i < SHA1HashSize; ++i)
	{
		Message_Digest[i] = context->Intermediate_Hash[i>>2]
		>> 8 * ( 3 - ( i & 0x03 ) );
	}
	return shaSuccess;
}


/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* message_array: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*
*/

int SHA1Input( SHA1Context *context,const uint8_t *message_array,unsigned length)
{
	if (!length)
	{
		return shaSuccess;
	}
	if (!context || !message_array)
	{
		return shaNull;
	}
	if (context->Computed)
	{
		context->Corrupted = shaStateError;
		return shaStateError;
	}
	if (context->Corrupted)
	{
		return context->Corrupted;
	}
	while(length-- && !context->Corrupted)
	{
		context->Message_Block[context->Message_Block_Index++] =
			(*message_array & 0xFF);
		context->Length_Low += 8;
		if (context->Length_Low == 0)
		{
			context->Length_High++;
			if (context->Length_High == 0)
			{
				/* Message is too long */
				context->Corrupted = 1;
			}
		}
		if (context->Message_Block_Index == 64)
		{
			SHA1ProcessMessageBlock(context);
		}
		message_array++;
	}
	return shaSuccess;
}

/*
* SHA1ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the publication.
*
*/

void SHA1ProcessMessageBlock(SHA1Context *context)
{
	const uint32_t K[] = { /* Constants defined in SHA-1 */
		0x5A827999,
		0x6ED9EBA1,
		0x8F1BBCDC,
		0xCA62C1D6
	};
	int t; /* Loop counter */
	uint32_t temp; /* Temporary word value */
	uint32_t W[80]; /* Word sequence */
	uint32_t A, B, C, D, E; /* Word buffers */
	/*
	* Initialize the first 16 words in the array W
	*/
	for(t = 0; t < 16; t++)
	{
		W[t] = context->Message_Block[t * 4] << 24;
		W[t] |= context->Message_Block[t * 4 + 1] << 16;
		W[t] |= context->Message_Block[t * 4 + 2] << 8;
		W[t] |= context->Message_Block[t * 4 + 3];
	}
	for(t = 16; t < 80; t++)
	{
		W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
	}
	A = context->Intermediate_Hash[0];
	B = context->Intermediate_Hash[1];
	C = context->Intermediate_Hash[2];
	D = context->Intermediate_Hash[3];
	E = context->Intermediate_Hash[4];
	for(t = 0; t < 20; t++)
	{
                temp = SHA1CircularShift(5,A) +
                        ((B & C) | ((~B) & D)) + E + W[t] + K[0];
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 20; t < 40; t++)
	{
		temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 40; t < 60; t++)
	{
		temp = SHA1CircularShift(5,A) +
			((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 60; t < 80; t++)
	{
		temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	context->Intermediate_Hash[0] += A;
	context->Intermediate_Hash[1] += B;
	context->Intermediate_Hash[2] += C;
	context->Intermediate_Hash[3] += D;
	context->Intermediate_Hash[4] += E;
	context->Message_Block_Index = 0;
}


/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a ’1’. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call the ProcessMessageBlock function
* provided appropriately. When it returns, it can be assumed that
* the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
* ProcessMessageBlock: [in]
* The appropriate SHA*ProcessMessageBlock function
* Returns:
* Nothing.
*
*/

void SHA1PadMessage(SHA1Context *context)
{
	/*
	* Check to see if the current message block is too small to hold
	* the initial padding bits and length. If so, we will pad the
	* block, process it, and then continue padding into a second
	* block.
	*/
	if (context->Message_Block_Index > 55)
	{
		context->Message_Block[context->Message_Block_Index++] = 0x80;
		while(context->Message_Block_Index < 64)
		{
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
		SHA1ProcessMessageBlock(context);
		while(context->Message_Block_Index < 56)
		{
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
	}
	else
	{
		context->Message_Block[context->Message_Block_Index++] = 0x80;
		while(context->Message_Block_Index < 56)
		{
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
	}

	/*
	* Store the message length as the last 8 octets
	*/
	context->Message_Block[56] = context->Length_High >> 24;
	context->Message_Block[57] = context->Length_High >> 16;
	context->Message_Block[58] = context->Length_High >> 8;
	context->Message_Block[59] = context->Length_High;
	context->Message_Block[60] = context->Length_Low >> 24;
	context->Message_Block[61] = context->Length_Low >> 16;
	context->Message_Block[62] = context->Length_Low >> 8;
	context->Message_Block[63] = context->Length_Low;
	SHA1ProcessMessageBlock(context);
}


#ifdef __cplusplus
}
#endif

除去注釋,代碼並不多!代碼還是很精練的!

 

Makefile文件

 

# -fPIC : position-independent code,suitable for dynamic linking and avoiding any limit on the size of the global offset table.

#arm-hisiv100nptl-linux-

CROSS_COMPILE=
CC = $(CROSS_COMPILE)gcc
STRIP = $(CROSS_COMPILE)strip
#LIBS = -L. -lstdc++  #移植到開發板上需要主動鏈接libstdc++.so 這個動態庫,暫時沒有更好的解決方法,只能手動鏈接
CFLAGS = -Wall -g -Os
INCLUDE = -I ./


OBJ := SHA1.o 
OBJ += test.o 

TARGET = sha 

all: $(OBJ)
	$(CC) $(CFLAGS) $(OBJ) -o $(TARGET) $(LIBS) $(INCLUDE)
	$(STRIP) --strip-unneeded $(TARGET)

# --strip-unneeded 

SHA1.o : SHA1.c 
	$(CC) $(CFLAGS) -c $< -o $@ 

test.o : test.c 
	$(CC) $(CFLAGS) -c $< -o $@ 

clean:
	rm -rf $(OBJ)
測試文件test.c

 

 

/*
* sha1test.c
*
* Description:
* This file will exercise the SHA-1 code performing the three
* tests documented in FIPS PUB 180-1 plus one which calls
* SHA1Input with an exact multiple of 512 bits, plus a few
* error test checks.
*
* Portability Issues:
* None.
*
*/

#include 
#include 
#include 
#include "SHA1.h"
/*
* Define patterns for testing
*/
#define TEST1 "abc"
#define TEST2a "abcdbcdecdefdefgefghfghighijhi"

#define TEST2b "jkijkljklmklmnlmnomnopnopq"
#define TEST2 TEST2a TEST2b
#define TEST3 "a"
#define TEST4a "01234567012345670123456701234567"
#define TEST4b "01234567012345670123456701234567"
/* an exact multiple of 512 bits */
#define TEST4 TEST4a TEST4b
char *testarray[4] =
{
	TEST1,
	TEST2,
	TEST3,
	TEST4
};

long int repeatcount[4] = { 1, 1, 1000000, 10 };
char *resultarray[4] =
{
	"A9 99 3E 36 47 06 81 6A BA 3E 25 71 78 50 C2 6C 9C D0 D8 9D",
	"84 98 3E 44 1C 3B D2 6E BA AE 4A A1 F9 51 29 E5 E5 46 70 F1",
	"34 AA 97 3C D4 C4 DA A4 F6 1E EB 2B DB AD 27 31 65 34 01 6F",
	"DE A3 56 A2 CD DD 90 C7 A7 EC ED C5 EB B5 63 93 4F 46 04 52"
};

int main()
{
	SHA1Context sha;
	int i, j, err;
	uint8_t Message_Digest[20];
	/*
	* Perform SHA-1 tests
	*/
	for(j = 0; j < 4; ++j)
	{
		//printf( "\nTest %d: %ld, ’%s’\n",j+1,repeatcount[j],testarray[j]);
		err = SHA1Reset(&sha);
		//if (err)
		//{
			//fprintf(stderr, "SHA1Reset Error %d.\n", err );
			//break; /* out of for j loop */
		//}
		for(i = 0; i < repeatcount[j]; ++i)
		{
			err = SHA1Input(&sha,
				(const unsigned char *) testarray[j],
				strlen(testarray[j]));
			if (err)
			{
				fprintf(stderr, "SHA1Input Error %d.\n", err );
				break; /* out of for i loop */
			}
		}
		err = SHA1Result(&sha, Message_Digest);
		if (err)
		{
			fprintf(stderr,
				"SHA1Result Error %d, could not compute message digest.\n",
				err );
		}
		else
		{
			printf("\t");
			for(i = 0; i < 20 ; ++i)
			{
				printf("%02X ", Message_Digest[i]);
			}
			printf("\n");
		}
		printf("Should match:\n");
		printf("\t%s\n", resultarray[j]);
	}

	/* Test some error returns */
	err = SHA1Input(&sha,(const unsigned char *) testarray[1], 1);
	printf ("\nError %d. Should be %d.\n", err, shaStateError );
	err = SHA1Reset(0);
	printf ("\nError %d. Should be %d.\n", err, shaNull );
	return 0;
}
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved