Using Cryptography Next Generation instead of OpenSSL
OpenSSL adds about 1.5 mb on top of your exe, which is quite a lot for very small apps. Especially if everything you need to do is calculating hashes. If you are only targeting Windows, there’s CNG (Cryptography Next Generation) API which have zero overhead as they are built into Windows itself. Here’s a C++ wrapper I wrote with samples to calculate MD5 and SHA-256:
cng.h
#pragma once
#include <string>
#include <Windows.h>
namespace win32 {
/**
* @brief Uses Windows CNG (Crypto Next Gen) (to avoid heavy dependencies like OpenSSL)
*/
class cng {
public:
static std::string md5(const std::string& s);
static std::string sha256(const std::string& s);
static std::string sha512(const std::string& s);
private:
static std::string hash(const std::string& s, LPCWSTR pszAlgId);
};
}
cng.cpp
#include "cng.h"
#include <Windows.h>
#include <bcrypt.h>
#include <vector>
#pragma comment(lib, "bcrypt.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
using namespace std;
namespace win32 {
// see https://learn.microsoft.com/en-us/windows/win32/seccng/creating-a-hash-with-cng
std::string cng::md5(const std::string& s) {
return hash(s, BCRYPT_MD5_ALGORITHM);
}
std::string cng::sha256(const std::string& s) {
return hash(s, BCRYPT_SHA256_ALGORITHM);
}
std::string cng::sha512(const std::string& s) {
return hash(s, BCRYPT_SHA512_ALGORITHM);
}
std::string cng::hash(const std::string& s, LPCWSTR pszAlgId) {
string r;
BCRYPT_ALG_HANDLE hAlg = NULL;
DWORD cbHashObject{ 0 };
DWORD cbData{ 0 };
PBYTE pbHashObject{ NULL };
BCRYPT_HASH_HANDLE hHash{ NULL };
PBYTE pbHash{ NULL };
DWORD cbHash{ 0 };
NTSTATUS status = STATUS_UNSUCCESSFUL;
//open an algorithm handle
if (NT_SUCCESS(::BCryptOpenAlgorithmProvider(
&hAlg,
pszAlgId,
NULL,
0))) {
//calculate the size of the buffer to hold the hash object
if (NT_SUCCESS(::BCryptGetProperty(
hAlg,
BCRYPT_OBJECT_LENGTH,
(PBYTE)&cbHashObject,
sizeof(DWORD),
&cbData,
0))) {
//allocate the hash object on the heap
pbHashObject = (PBYTE)::HeapAlloc(::GetProcessHeap(), 0, cbHashObject);
if (pbHashObject) {
// calculate the length of the hash
if (NT_SUCCESS(::BCryptGetProperty(
hAlg,
BCRYPT_HASH_LENGTH,
(PBYTE)&cbHash,
sizeof(DWORD),
&cbData,
0))) {
//allocate the hash buffer on the heap
pbHash = (PBYTE)::HeapAlloc(::GetProcessHeap(), 0, cbHash);
if (pbHash) {
//create a hash
if (NT_SUCCESS(status = ::BCryptCreateHash(
hAlg,
&hHash,
pbHashObject,
cbHashObject,
NULL,
0,
0))) {
//hash some data
if (NT_SUCCESS(::BCryptHashData(
hHash,
(PBYTE)&s[0],
s.size(),
0))) {
// close the hash
if (NT_SUCCESS(::BCryptFinishHash(
hHash,
pbHash,
cbHash,
0))) {
// fit into safe C++
r.resize(cbHash * 2);
for (unsigned int i = 0; i < cbHash; ++i)
std::sprintf(&r[i * 2], "%02X", pbHash[i]);
::HeapFree(::GetProcessHeap(), 0, pbHash);
}
}
::BCryptDestroyHash(hHash);
}
}
}
::HeapFree(::GetProcessHeap(), 0, pbHashObject);
}
}
::BCryptCloseAlgorithmProvider(hAlg, 0);
}
return r;
}
}
Hey, I really appreciate your interest in my work. If you want to reach out to me directly, you can send me an email anytime. I'd love to hear from you. But if you have something to say that others might benefit from, feel free to leave a comment below. I'll try to reply as soon as I can. Like what I do? Please: