Date: 2021dec2
OS: Windows
Language: C++
Keywords: cryptography
Q. Windows: MD5 class using native crypto library
A. Here is a C++ class that uses Windows crypto library to calculate an MD5 digest. Warning, the crypto library is deprecated.
--- nativemd5.h ----------
// Native MD5 access
// Copyright © 2021, davekb
#pragma once
#include <Wincrypt.h>
class NativeMD5 {
static HCRYPTPROV m_hProvider;
HCRYPTHASH m_hHash;
void FreeHash();
public:
enum { MD5LEN = 16, MD5STRLEN = MD5LEN * 2 + 1 };
NativeMD5();
~NativeMD5();
DWORD Update(const BYTE *buf, const DWORD bufsize);
DWORD GetResultBytes(BYTE *hash, DWORD *cbHash);
DWORD GetResultStr(LPSTR szHash, const size_t);
};
--- nativemd5.cpp ----------
// Native MD5 access
// Copyright © 2021, davekb
#include "stdafx.h"
#include "Debug.h"
#include "NativeMd5.h"
// For speed keep the provider between instances
HCRYPTPROV NativeMD5::m_hProvider = NULL;
NativeMD5::NativeMD5() {
DWORD dwStatus = 0;
m_hHash = NULL;
if (m_hProvider == NULL) {
if (!CryptAcquireContext(&m_hProvider,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
{
dwStatus = GetLastError();
Debug("CryptAcquireContext failed: %d\n", dwStatus);
return;
}
}
if (!CryptCreateHash(m_hProvider, CALG_MD5, 0, 0, &m_hHash))
{
dwStatus = GetLastError();
Debug("CryptAcquireContext failed: %d\n", dwStatus);
FreeHash();
}
}
void NativeMD5::FreeHash() {
if (m_hHash != NULL) {
CryptDestroyHash(m_hHash);
m_hHash = NULL;
}
}
NativeMD5::~NativeMD5() {
FreeHash();
}
DWORD NativeMD5::Update(const BYTE *data, const DWORD datasize) {
DWORD dwStatus = 0;
if (m_hHash == NULL) {
Debug(__FUNCTION__ ": m_hHash is NULL\n");
return -1;
}
if (!CryptHashData(m_hHash, data, datasize, 0))
{
dwStatus = GetLastError();
Debug("CryptHashData failed: %d\n", dwStatus);
FreeHash();
return dwStatus;
}
return dwStatus;
}
DWORD NativeMD5::GetResultBytes(BYTE *hash, DWORD *cbHash) {
DWORD dwStatus = 0;
*cbHash = MD5LEN;
if (m_hHash == NULL) {
Debug(__FUNCTION__ ": m_hHash is NULL\n");
return -1;
}
if (!CryptGetHashParam(m_hHash, HP_HASHVAL, hash, cbHash, 0))
{
dwStatus = GetLastError();
Debug("CryptGetHashParam failed: %d\n", dwStatus);
return dwStatus;
}
return dwStatus;
}
DWORD NativeMD5::GetResultStr(LPSTR szHash, const size_t size) {
BYTE hash[MD5LEN];
DWORD cbHash;
if (size < MD5STRLEN){
szHash[0] = '\0';
return -1;
}
const DWORD dwStatus = GetResultBytes(hash, &cbHash);
LPSTR p = szHash;
const static char szDigits[] = "0123456789abcdef";
for (DWORD i = 0; i < cbHash; i++)
{
*p++ = szDigits[hash[i] >> 4];
*p++ = szDigits[hash[i] & 0xf];
}
*p = '\0';
return dwStatus;
}
Example use:
#include "nativemd5.h"
// Test string from https://en.wikipedia.org/wiki/MD5
LPCSTR szData = "The quick brown fox jumps over the lazy dog";
NativeMD5 nativeMd5;
nativeMd5.Update((LPBYTE)szData, lstrlen(szData));
char szHash[NativeMD5::MD5STRLEN];
nativeMd5.GetResultStr(szHash, sizeof(szHash));
printf("Native MD5 of %d bytes is %s\n", lstrlen(szData), szHash);
Based on
https://docs.microsoft.com/en-us/windows/win32/seccrypto/example-c-program--creating-an-md-5-hash-from-file-content