SHA1校验算法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; }
上一篇: Html和CSS总结
下一篇: c语言:0的按位取反结果