添砖加瓦:几种常见的数据摘要算法(MD5、CRC32、SHA1和SHA256)
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]);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具