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

说明

测试平台系统: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

猜你喜欢

转载自blog.csdn.net/qq2399431200/article/details/108376788