Programming Tips - Windows: MD5 class using native crypto library

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