C++中从文件获取MD5哈希值

81
如何在C++中获取文件的MD5哈希值?

1
@silky - 这并不是一个有帮助的评论 :) ..从头开始实现MD5是接触加密算法和协议的好方法,而且由于它是“已知”的,您可以立即验证您的代码是否正确,与md5sum或类似工具进行比较。 - warren
1
@Noon Silk 我认为在这里为文件创建唯一签名的目的上,md5 应该足够了。 - bobobobo
@Noon Silk,使用长递归检查sha1会太慢! - Will03uk
12个回答

56
这是一个直接实现命令行md5sum计算并显示文件MD5的方法。它需要与OpenSSL库链接(gcc md5.c -o md5 -lssl)才能正常工作。它是纯C语言编写的,但你应该能够很容易地将其适配到你的C++应用程序中。
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/md5.h>

unsigned char result[MD5_DIGEST_LENGTH];

// Print the MD5 sum as hex-digits.
void print_md5_sum(unsigned char* md) {
    int i;
    for(i=0; i <MD5_DIGEST_LENGTH; i++) {
            printf("%02x",md[i]);
    }
}

// Get the size of the file by its file descriptor
unsigned long get_size_by_fd(int fd) {
    struct stat statbuf;
    if(fstat(fd, &statbuf) < 0) exit(-1);
    return statbuf.st_size;
}

int main(int argc, char *argv[]) {
    int file_descript;
    unsigned long file_size;
    char* file_buffer;

    if(argc != 2) { 
            printf("Must specify the file\n");
            exit(-1);
    }
    printf("using file:\t%s\n", argv[1]);

    file_descript = open(argv[1], O_RDONLY);
    if(file_descript < 0) exit(-1);

    file_size = get_size_by_fd(file_descript);
    printf("file size:\t%lu\n", file_size);

    file_buffer = mmap(0, file_size, PROT_READ, MAP_SHARED, file_descript, 0);
    MD5((unsigned char*) file_buffer, file_size, result);
    munmap(file_buffer, file_size); 

    print_md5_sum(result);
    printf("  %s\n", argv[1]);

    return 0;
}

1
在32位平台上,您的mmap有一个限制,即文件的大小不能太大,尽管这是解决问题的一种优雅方法。例如,在32位Windows上,您无法使用此代码对DVD进行MD5处理。 - Chris K
@ChrisKaminski 在32位平台上,您可以滑动4GB的内存映射文件窗口。 - expert
14
非常好的回答,它对我非常有帮助。但是,你没有在之后调用munmap。由于程序立即结束,所以你不会发生内存泄漏,但如果像我这样的傻瓜复制了代码并没有放入munmap,我们的程序就会出现内存泄漏 ;)解决方案:munmap(file_buffer, file_size); - Bob Miller
13
对我来说,在Ubuntu 14.04上,gcc md5.c -o md5 -lcrypto 这个命令行比 -lssl 命令行更有效。 - RajaRaviVarma
2
依赖像 OpenSSL 这样复杂且庞大的库仅仅为了实现一个简单的 MD5 算法,这对我来说不是一个好主意。 - Timmmm
这是一个非常古老的问题,似乎得到了很多评论,所以我想添加一些更多的信息。这段代码是我从大约2005年左右给学生的作业中转录的MIPS汇编代码。我确保C版本可以编译/链接而没有错误,但仅此而已。所以它不是最健壮的,只是个想法(所以32/64位之类的)。而且这是10多年前的事情了。在2016年及以后,我肯定会寻找一个更小的md5库,但再次强调,它最初是为学生准备的,我想遵循他们熟悉的md5sum的方式,并且当时是基于openssl的。 - D'Nabre

22
您可以自己实现MD5算法(网络上有很多示例),或者链接到OpenSSL库并使用OpenSSL的摘要函数。 这是一个获取字节数组的MD5的示例:
#include <openssl/md5.h>
QByteArray AESWrapper::md5 ( const QByteArray& data) {
    unsigned char * tmp_hash;
    tmp_hash = MD5((const unsigned char*)data.constData(), data.length(), NULL);
    return QByteArray((const char*)tmp_hash, MD5_DIGEST_LENGTH);
}

26
在使用Qt时(就像你所做的那样),我更愿意只做return QCryptographicHash::hash(data, QCryptographicHash::Md5);作为函数的主体... - akira
5
在涉及到安全相关的事情时,如果互联网上已经有足够的资源,就不要自己编写实现。而且MD4/5的每一个可能的实现都已经存在于网络上,所以没有必要自己编写。 - Mahmoud Al-Qudsi
1
@MahmoudAl-Qudsi 有的,我的教授不允许我抄袭代码。 - arkon
2
@MahmoudAl-Qudsi 当涉及到安全相关的事情时,永远不要使用MD5。MD5不是一个加密强度哈希。 - uliwitness
2
@uliwitness md5 不是我的想法。把 MD5 当作一个中等快速的非加密哈希是可以的,但我同意它作为加密哈希是完全破碎的(在速度和非加密哈希方面有更好的选择)。 - Mahmoud Al-Qudsi
显示剩余3条评论

14

如果您从"https://stackoverflow.com/questions/4393017/md5-implementation-in-c"重定向到此处,是因为它错误地被标记为重复。

这里的示例可以正常工作:

http://www.zedwood.com/article/cpp-md5-function

如果您正在使用VC++2010编译,则需要将其main.cpp更改为以下内容:

#include <iostream> //for std::cout
#include <string.h> //for std::string
#include "MD5.h"

using std::cout; using std::endl;

int main(int argc, char *argv[])
{
    std::string Temp =  md5("The quick brown fox jumps over the lazy dog");
    cout << Temp.c_str() << endl;

    return 0;
}

如果您想要读取char *数组而不是字符串来回答这个页面上的问题,那么您需要稍微修改MD5类。

编辑:

显然修改MD5库并不清楚,因此在此为您提供完整的VC++2010解决方案,以便包括char *:

https://github.com/alm4096/MD5-Hash-Example-VS

这里有一些解释:

#include <iostream> //for std::cout
#include <string.h> //for std::string
#include <fstream>
#include "MD5.h"

using std::cout; using std::endl;

int main(int argc, char *argv[])
{
    //Start opening your file
    ifstream inBigArrayfile;
    inBigArrayfile.open ("Data.dat", std::ios::binary | std::ios::in);

    //Find length of file
    inBigArrayfile.seekg (0, std::ios::end);
    long Length = inBigArrayfile.tellg();
    inBigArrayfile.seekg (0, std::ios::beg);    

    //read in the data from your file
    char * InFileData = new char[Length];
    inBigArrayfile.read(InFileData,Length);

    //Calculate MD5 hash
    std::string Temp =  md5(InFileData,Length);
    cout << Temp.c_str() << endl;

    //Clean up
    delete [] InFileData;

    return 0;
}

我只是在MD5库中加入了以下内容:

MD5.cpp:

MD5::MD5(char * Input, long length)
{
  init();
  update(Input, length);
  finalize();
}

MD5.h:

std::string md5(char * Input, long length);

那是针对字符串的,不是针对文件 - Brock Hensley
2
答案修改以包括文件 - ALM865
1
你的一些链接已经失效了。 - 463035818_is_not_a_number
2
请问您能否更新VC++2010解决方案链接。 - Jonas
1
链接已更新到Git。 - ALM865

10

我刚刚需要一个跨平台解决方案,适用于c++11、boost和openssl。我以D'Nabre的方案为起点,将其精简为以下内容:

#include <openssl/md5.h>
#include <iomanip>
#include <sstream>
#include <boost/iostreams/device/mapped_file.hpp>

const std::string md5_from_file(const std::string& path)
{
    unsigned char result[MD5_DIGEST_LENGTH];
    boost::iostreams::mapped_file_source src(path);
    MD5((unsigned char*)src.data(), src.size(), result);

    std::ostringstream sout;
    sout<<std::hex<<std::setfill('0');
    for(auto c: result) sout<<std::setw(2)<<(int)c;

    return sout.str();
}

一个快速测试可执行文件的示例:

#include <iostream>

int main(int argc, char *argv[]) {
    if(argc != 2) {
        std::cerr<<"Must specify the file\n";
        exit(-1);
    }
    std::cout<<md5_from_file(argv[1])<<"  "<<argv[1]<<std::endl;
    return 0;
}

一些链接的注意事项: Linux: -lcrypto -lboost_iostreams Windows: -DBOOST_ALL_DYN_LINK libeay32.lib ssleay32.lib


谢谢。if(!exists(boost::filesystem::path(path))) { - Abdul Ahad

10
QFile file("bigimage.jpg");

if (file.open(QIODevice::ReadOnly))
{
    QByteArray fileData = file.readAll();

    QByteArray hashData = QCryptographicHash::hash(fileData,QCryptographicHash::Md5); // or QCryptographicHash::Sha1
    qDebug() << hashData.toHex();  // 0e0c2180dfd784dd84423b00af86e2fc

}

10
不适合处理文件大小为GB级别的文件 :) - quickly_now

7

md5.h还有MD5_*函数,非常适用于大文件。

#include <openssl/md5.h>
#include <fstream>
.......

std::ifstream file(filename, std::ifstream::binary);
MD5_CTX md5Context;
MD5_Init(&md5Context);
char buf[1024 * 16];
while (file.good()) {
    file.read(buf, sizeof(buf));
    MD5_Update(&md5Context, buf, file.gcount());
}
unsigned char result[MD5_DIGEST_LENGTH];
MD5_Final(result, &md5Context);

非常简单,不是吗?将其转换为字符串也非常简单:
#include <sstream>
#include <iomanip>
.......

std::stringstream md5string;
md5string << std::hex << std::uppercase << std::setfill('0');
for (const auto &byte: result)
    md5string << std::setw(2) << (int)byte;

return md5string.str();

对我来说运行良好! - Maggnetix

4

我之前使用过Botan执行此操作和其他操作。AraK指出了Crypto++。我想两个库都是完全有效的。现在就看你选择哪一个了 :-).


4

1
它返回的哈希值与其他MD5实现不同。例如,当应该是d41d8cd98f00b204e9800998ecf8427e时,它将空字符串哈希为e4c23762ed2823a27e62a64b95c024e7。这里有一个相关的问题:http://stackoverflow.com/q/33989390/2436687 - user31389

2

@D'Nabre对C++的实现进行了重新制作。不要忘记在最后使用-lcrypto编译:gcc md5.c -o md5 -lcrypto

#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>

#include <openssl/md5.h>
using namespace std;

unsigned char result[MD5_DIGEST_LENGTH];

// function to print MD5 correctly
void printMD5(unsigned char* md, long size = MD5_DIGEST_LENGTH) {
    for (int i=0; i<size; i++) {
        cout<< hex << setw(2) << setfill('0') << (int) md[i];
    }
}

int main(int argc, char *argv[]) {

if(argc != 2) {
    cout << "Specify the file..." << endl;
    return 0;
}

ifstream::pos_type fileSize;
char * memBlock;

ifstream file (argv[1], ios::ate);

//check if opened
if (file.is_open() ) { cout<< "Using file\t"<< argv[1]<<endl; }
else {
    cout<< "Unnable to open\t"<< argv[1]<<endl;
    return 0;
}

//get file size & copy file to memory
//~ file.seekg(-1,ios::end); // exludes EOF
fileSize = file.tellg();
cout << "File size \t"<< fileSize << endl;
memBlock = new char[fileSize];
file.seekg(0,ios::beg);
file.read(memBlock, fileSize);
file.close();

//get md5 sum
MD5((unsigned char*) memBlock, fileSize, result);

//~ cout << "MD5_DIGEST_LENGTH = "<< MD5_DIGEST_LENGTH << endl;
printMD5(result);
cout<<endl;

return 0;
}

2
使用 Crypto++,您可以执行以下操作:
#include <sha.h>
#include <iostream> 

SHA256 sha; 
while ( !f.eof() ) { 
   char buff[4096];
   int numchars = f.read(...); 
   sha.Update(buff, numchars); 
}
char hash[size]; 
sha.Final(hash); 
cout << hash <<endl; 

我需要的是类似的东西,因为我不能读取多GB的文件来计算哈希值。理论上我可以对它们进行内存映射,但我必须支持32位平台——对于大文件仍然存在问题。


2
请注意:sha256!= md5 - c00000fd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接