1、算法概述
數據摘要算法是密碼學算法中非常重要的一個分支,它通過對所有數據提取指紋信息以實現數據簽名、數據完整性校驗等功能,由於其不可逆性,有時候會被用做敏感信息的加密。數據摘要算法也被稱為哈希(Hash)算法或散列算法。
1.1、CRC8、CRC16、CRC32
CRC(Cyclic Redundancy Check,循環冗余校驗)算法出現時間較長,應用也十分廣泛,尤其是通訊領域,現在應用最多的就是 CRC32 算法,它產生一個4字節(32位)的校驗值,一般是以8位十六進制數,如FA 12 CD 45等。CRC算法的優點在於簡便、速度快,嚴格的來說,CRC更應該被稱為數據校驗算法,但其功能與數據摘要算法類似,因此也作為測試的可選算法。
在 WinRAR、WinZIP 等軟件中,也是以 CRC32 作為文件校驗算法的。一般常見的簡單文件校驗(Simple File Verify – SFV)也是以 CRC32算法為基礎,它通過生成一個后綴名為 .SFV 的文本文件,這樣可以任何時候可以將文件內容 CRC32運算的結果與 .SFV 文件中的值對比來確定此文件的完整性。
與 SFV 相關工具軟件有很多,如MagicSFV、MooSFV等。
1.2、MD2 、MD4、MD5
這是應用非常廣泛的一個算法家族,尤其是 MD5(Message-Digest Algorithm 5,消息摘要算法版本5),它由MD2、MD3、MD4發展而來,由Ron Rivest(RSA公司)在1992年提出,被廣泛應用於數據完整性校驗、數據(消息)摘要、數據加密等。MD2、MD4、MD5 都產生16字節(128位)的校驗值,一般用32位十六進制數表示。MD2的算法較慢但相對安全,MD4速度很快,但安全性下降,MD5比MD4更安全、速度更快。
在互聯網上進行大文件傳輸時,都要得用MD5算法產生一個與文件匹配的、存儲MD5值的文本文件(后綴名為 .md5或.md5sum),這樣接收者在接收到文件后,就可以利用與 SFV 類似的方法來檢查文件完整性,絕大多數大型軟件公司或開源組織都是以這種方式來校驗數據完整性,而且部分操作系統也使用此算法來對用戶密碼進行加密,另外,它也是目前計算機犯罪中數據取證的最常用算法。
與MD5 相關的工具有很多,如 WinMD5等。
1.3、SHA1、SHA256、SHA384、SHA512
SHA(Secure Hash Algorithm)是由美國專門制定密碼算法的標准機構—— 美國國家標准技術研究院(NIST)制定的,SHA系列算法的摘要長度分別為:SHA為20字節(160位)、SHA256為32字節(256位)、 SHA384為48字節(384位)、SHA512為64字節(512位),由於它產生的數據摘要的長度更長,因此更難以發生碰撞,因此也更為安全,它是未來數據摘要算法的發展方向。由於SHA系列算法的數據摘要長度較長,因此其運算速度與MD5相比,也相對較慢。
SHA1的應用較為廣泛,主要應用於CA和數字證書中,另外在互聯網中流行的BT軟件中,也是使用SHA1來進行文件校驗的。
1.4、RIPEMD、PANAMA、TIGER、ADLER32 等
RIPEMD是Hans Dobbertin等3人在對MD4,MD5缺陷分析基礎上,於1996年提出來的,有4個標准128、160、256和320,其對應輸出長度分別為16字節、20字節、32字節和40字節。IGER由Ross在1995年提出。Tiger號稱是最快的Hash算法,專門為64位機器做了優化。
注:來源百度百科:摘要算法
2、算法測試
基於openssl中提供的MD5、SHA1、SHA256和zlib中提供的CRC32,測試平台:OSX10.12
check.h

#ifndef HEADER_CHECK_H # define HEADER_CHECK_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/md5.h> #include <openssl/sha.h> #include "zlib.h" #ifdef __cplusplus extern "C"{ #endif #define BUFSIZE 1024*1024*2 /* * 使用OpenSSL提供的MD5相關函數計算字符串和大文件的MD5值 */ int calcBufMD5(unsigned char *src, size_t len,unsigned char *dst); int calcFileMD5(const char *inFile,unsigned char *dst); /* * 使用OpenSSL提供的SHA1 SAH256函數分別計算字符串和大文件的SAH1 SAH256值 */ int calcBufSHA1(unsigned char *src, size_t len,unsigned char *dst); int calcFileSHA1(const char *inFile,unsigned char *dst); int calcBufSHA256(unsigned char *src, size_t len,unsigned char *dst); int calcFileSHA256(const char *inFile,unsigned char *dst); /* * 自實現CRC32校驗(查表法) * 用以計算字符串和大文件的CRC32值 */ unsigned int calcBufCRC32(unsigned int crc,unsigned char *buf,size_t len); unsigned int calcFileCRC32(const char *inFile); # ifdef __cplusplus } # endif # endif
check.c

#include "check.h" #include <stdlib.h> //計算字符串的MD5 int calcBufMD5(unsigned char *src,size_t len,unsigned char *dst) { if(NULL == src || NULL == dst) { fprintf(stderr,"%s\n","input parameter error"); return -1; } MD5(src,len,dst); return 0; } //計算大文件的MD5值 int calcFileMD5(const char *inFile,unsigned char *dst) { if(NULL == inFile || NULL == dst) { fprintf(stderr,"%s\n","input parameter error"); return -1; } char buf[BUFSIZE] = {0}; int nread; MD5_CTX ctx; FILE *fin = fopen(inFile,"r"); if(NULL == fin) { fprintf(stderr,"%s\n","open file error"); return -1; } MD5_Init(&ctx); while((nread = fread(buf,1,BUFSIZE,fin)) > 0) { MD5_Update(&ctx,buf,nread); } MD5_Final(dst,&ctx); return 0; } //計算字符串的SHA1 int calcBufSHA1(unsigned char *src, size_t len,unsigned char *dst) { if(NULL == src || NULL == dst) { fprintf(stderr,"%s\n","input parameter error"); return -1; } unsigned char sha[20] = {0}; char tmp[3] = {0}; int i; SHA1(src,len,dst); for(i = 0; i < 20; i++) { sprintf(tmp,"%02x",sha[i]); strcat((char*)dst,tmp); } return 0; } //計算大文件的SAH1 int calcFileSHA1(const char *inFile,unsigned char *dst) { if(NULL == inFile || NULL == dst) { fprintf(stderr,"%s\n","input parameter error"); return -1; } char buf[BUFSIZE] = {0}; unsigned char sha[20] = {0}; char tmp[3] = {0}; int i,nread; SHA_CTX ctx; FILE *fin = fopen(inFile,"r"); if(NULL == fin) { fprintf(stderr,"%s\n","open file error"); return -1; } SHA1_Init(&ctx); while((nread = fread(buf,1,BUFSIZE,fin)) > 0) { SHA1_Update(&ctx,buf,nread); } SHA1_Final(dst,&ctx); fclose(fin); return 0; } //計算字符串的SAH256 int calcBufSHA256(unsigned char *src, size_t len,unsigned char *dst) { if(NULL == src || NULL == dst) { fprintf(stderr,"%s\n","input parameter error"); return -1; } unsigned char sha[32] = {0}; char tmp[3] = {0}; int i; SHA256(src,len,dst); return 0; } //計算大文件的SAH256 int calcFileSHA256(const char *inFile,unsigned char *dst) { if(NULL == inFile || NULL == dst) { fprintf(stderr,"%s\n","input parameter error"); return -1; } char buf[BUFSIZE] = {0}; unsigned char sha[32] = {0}; char tmp[3] = {0}; int i,nread; SHA256_CTX ctx; FILE *fin = fopen(inFile,"r"); if(NULL == fin) { fprintf(stderr,"%s\n","open file error"); return -1; } SHA256_Init(&ctx); while((nread = fread(buf,1,BUFSIZE,fin)) > 0) { SHA256_Update(&ctx,buf,nread); } SHA256_Final(dst,&ctx); fclose(fin); return 0; } //計算字符串的CRC32 unsigned int calcBufCRC32(unsigned int crc,unsigned char *buf,size_t len) { return crc32(crc,buf,len); } //計算大文件的CRC32 unsigned int calcFileCRC32(const char *inFile) { int nread; unsigned char buf[BUFSIZE] = {0}; unsigned int crc = 0; FILE *fin = fopen(inFile,"rb"); if(NULL == fin) { fprintf(stderr,"%s\n","open file error"); return -1; } while((nread = fread(buf,1,BUFSIZE,fin)) > 0) crc = calcBufCRC32(crc,buf,nread); fclose(fin); return crc; }
test.c

#include <stdio.h> #include <sys/time.h> #include "check.h" int main(int argc,char *argv[]) { unsigned char *data = "123"; struct timeval start; struct timeval end; double diff; unsigned char *md = (unsigned char *)malloc(64); if(NULL == md) { fprintf(stderr,"%s\n","malloc error"); return -1; } memset(md,0,sizeof(md)); gettimeofday(&start,NULL); calcFileMD5(argv[1],md); gettimeofday(&end,NULL); diff = (end.tv_sec - start.tv_sec)*1000000 + (end.tv_usec - start.tv_usec); printf("%s MD5:%s\n",argv[1],md); for(int i = 0; i< 16;i++) printf("%02x",md[i]); printf("spend time :%fs\n\n",diff/1000000); diff =0; gettimeofday(&start,NULL); unsigned crc = calcFileCRC32(argv[1]); gettimeofday(&end,NULL); diff = (end.tv_sec - start.tv_sec)*1000000 + (end.tv_usec - start.tv_usec); printf("%s CRC32:%u\n",argv[1],crc); printf("spend time :%fs\n\n",diff/1000000); diff = 0; memset(md,0,sizeof(md)); gettimeofday(&start,NULL); calcFileSHA1(argv[1],md); gettimeofday(&end,NULL); diff = (end.tv_sec - start.tv_sec)*1000000 + (end.tv_usec - start.tv_usec); printf("%s SHA1:%s\n",argv[1],md); printf("spend time :%fs\n\n",diff/1000000); diff =0; memset(md,0,sizeof(md)); gettimeofday(&start,NULL); calcFileSHA256(argv[1],md); gettimeofday(&end,NULL); diff = (end.tv_sec - start.tv_sec)*1000000 + (end.tv_usec - start.tv_usec); printf("%s SHA256:%s\n",argv[1],md); printf("spend time :%fs\n\n",diff/1000000); free(md); return 0; }
說明:經過MD5、SHA1和SHA256計算出的校驗值需要再經過一層轉換,才能成為可識別的字符串,以MD5為例:
for(int i = 0; i< 16;i++) printf("%02x",md[i]);