【OpenSSL】MD5:计算字符串和文件的哈希值

tech2022-09-28  62

说明

测试平台系统:Ubuntu18.04 X86_64 测试语言:C++11 测试编译器:GCC version 7.5.0 测试内容:字符串和文件的MD5哈希值计算方法

一、MD5简介

关于16位和32位MD5值:       MD5得到的是一个16字节的哈希值(或称散列值),每个字节格式化位16进制(0x**),取数值部分的两个字符,连起来得到一个32个字符的字符串。这就是所说的32位MD5值。16位MD5值就是取的32位MD5值的中间段,即第9至24位(如果从0开始作为第一位,则是第8至23位)。

二、OpenSSL计算MD5介绍

      OpenSSL计算MD5分为两种方式:

1. 非连续缓冲区的字符串计算:当有大量的数据块(比如文件)需要计算其MD5值时,使用此方法

涉及的OpenSSL函数:

int MD5_Init(MD5_CTX *c); int MD5_Update(MD5_CTX *c, const void *data, size_t len); int MD5_Final(unsigned char *md, MD5_CTX *c);

计算方法: 首先调用MD5_Init初始化,然后多次调用MD5_Update只至所有数据处理完毕,最后调用MD5_Final函数从其第一个参数得到最终16字节的md5哈希值,最终转换为16进制后得到32位MD5字符串。

 

2. 连续缓冲区的字符串计算:当只有一个字符串需要计算其MD5值时,使用此方法

涉及的OpenSSL函数:

unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md);

计算方法: 直接调用此函数即可得到字符串对应的MD5哈希值,然后转换为16进制即可得到32位MD5字符串。其实此函数也是调用了“MD_Init/MD5_Update/MD5_Final”这三个函数实现。

 

三、OpenSSL开发库安装

sudo apt-get install libssl-dev # Debian / Ubuntu系统下libssl即openssl库

补充:

在基于 Redhat / Fedora 的系统上安装这些软件包方法

sudo dnf install openssl-devel

 

四、借助C++11实现基于OpenSSL库的MD5计算

Utils.cpp

#include <openssl/evp.h> #include <openssl/hmac.h> #include <openssl/md5.h> #ifdef OPENSSL_IS_BORINGSSL #include <openssl/base64.h> #endif #include <algorithm> #include <cstring> #include <sstream> #include <iomanip> #include "Utils.h" std::string CUtils::ComputeContentMD5(const std::string& data, EMD5Len md5len) { return ComputeContentMD5(data.c_str(), data.size(), md5len); } std::string CUtils::ComputeContentMD5(const char * data, size_t size, EMD5Len md5len) { if (!data) { return ""; } unsigned char md[MD5_DIGEST_LENGTH]; MD5(reinterpret_cast<const unsigned char*>(data), size, (unsigned char*)&md); std::string strMD5 = HexToString(md, MD5_DIGEST_LENGTH); if(EMD5Len::Len16 == md5len) { return strMD5.substr(8, 16); } return strMD5; } std::string CUtils::ComputeContentMD5(std::istream& stream, EMD5Len md5len) { auto ctx = EVP_MD_CTX_create(); unsigned char md_value[EVP_MAX_MD_SIZE]; unsigned int md_len = 0; EVP_MD_CTX_init(ctx); #ifndef OPENSSL_IS_BORINGSSL EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); #endif EVP_DigestInit_ex(ctx, EVP_md5(), nullptr); auto currentPos = stream.tellg(); if (currentPos == static_cast<std::streampos>(-1)) { currentPos = 0; stream.clear(); } stream.seekg(0, stream.beg); char streamBuffer[2048]; while (stream.good()) { stream.read(streamBuffer, 2048); auto bytesRead = stream.gcount(); if (bytesRead > 0) { EVP_DigestUpdate(ctx, streamBuffer, static_cast<size_t>(bytesRead)); } } EVP_DigestFinal_ex(ctx, md_value, &md_len); EVP_MD_CTX_destroy(ctx); stream.clear(); stream.seekg(currentPos, stream.beg); std::string strMD5 = HexToString(md_value, md_len); if(EMD5Len::Len16 == md5len) { return strMD5.substr(8, 16); } return strMD5; } std::string CUtils::HexToString(const unsigned char *data, size_t size) { static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; std::stringstream ss; for (size_t i = 0; i < size; i++) ss << hex[(data[i] >> 4)] << hex[(data[i] & 0x0F)]; return ss.str(); }

Utils.h

class CUtils { enum class EMD5Len { Len32, Len16, }; #define UNUSED_PARAM(x) ((void)(x)) public: std::string ComputeContentMD5(const std::string& data, EMD5Len md5len = EMD5Len::Len32); std::string ComputeContentMD5(const char *data, size_t size, EMD5Len md5len = EMD5Len::Len32); std::string ComputeContentMD5(std::istream & stream, EMD5Len md5len = EMD5Len::Len32); std::string HexToString(const unsigned char *data, size_t size); };

main.cpp

#include <string> #include <iostream> #include <fstream> #include <memory> #include <openssl/md5.h> #include "Utils.h" int main() { std::cout << MD5_DIGEST_LENGTH << std::endl; std::cout << CUtils::ComputeContentMD5("hello hello heelo", CUtils::EMD5Len::Len32) << std::endl; std::cout << CUtils::ComputeContentMD5("hello hello heelo", CUtils::EMD5Len::Len16) << std::endl; std::string filePath = "/home/dog/Dev/C++/utils/Url.h"; std::shared_ptr<std::iostream> content = std::make_shared<std::fstream>(filePath, std::ios::in | std::ios::binary); // 读取文件内容 std::cout << CUtils::ComputeContentMD5(*content, CUtils::EMD5Len::Len32) << std::endl; std::cout << CUtils::ComputeContentMD5(*content, CUtils::EMD5Len::Len16) << std::endl; return 0; }

Build: 

$ g++ -std=c++11 -o main main.cpp Utils.cpp -I./ -lcrypto

Run:

 

参考

1. 在线计算MD5:http://www.metools.info/other/o21.html 2. OpenSSL计算MD5:https://www.cnblogs.com/binchen-china/p/5653337.html                                       https://blog.csdn.net/weixin_34162401/article/details/93024139

最新回复(0)