Skip to content

Instantly share code, notes, and snippets.

@RChehowski
Created March 31, 2020 19:01
Show Gist options
  • Save RChehowski/305e8ec4068e53473acc53238a058bbc to your computer and use it in GitHub Desktop.
Save RChehowski/305e8ec4068e53473acc53238a058bbc to your computer and use it in GitHub Desktop.
/**
* It is unsafe to expose Win32 api into the user's UE4 C++. This may cause various
* cryptic compilation errors and chaotic rebuilds.
* The obvious solution is to use engine API to blindly load Crypt32.dll and use it
* without referencing any of Win32 API.
*/
class FCryptoUtilsWindows
{
static const TCHAR* Crypt32LibraryName = TEXT("Crypt32");
typedef unsigned long Win32Dword;
typedef unsigned char Win32Byte;
typedef char* Win32LPStr;
typedef const char* Win32LPCStr;
struct Win32CertContext
{
Win32Dword dwCertEncodingType;
Win32Byte* pbCertEncoded;
Win32Dword cbCertEncoded;
void* /* PCERT_INFO */ pCertInfo;
void* /* HCERTSTORE */ hCertStore;
};
typedef const Win32CertContext* Win32PCCertContext;
typedef void*(__stdcall* TCertOpenSystemStoreA)(void*, Win32LPCStr);
typedef int(__stdcall* TCertCloseStore)(void*, Win32Dword);
typedef Win32PCCertContext(__stdcall* TCertEnumCertificatesInStore)(void*, Win32PCCertContext);
typedef int(__stdcall* TCertFreeCertificateContext)(Win32PCCertContext);
typedef int(__stdcall* TCryptBinaryToStringA)(const Win32Byte*, Win32Dword, Win32Dword, Win32LPStr, Win32Dword*);
static const Win32Dword _CU_CRYPT_STRING_BASE64HEADER = (Win32Dword) 0;
// Fields
void* _Crypt32LibraryHandle;
bool bInitialized;
const TCertOpenSystemStoreA _CertOpenSystemStoreA;
const TCertCloseStore _CertCloseStore;
const TCertEnumCertificatesInStore _CertEnumCertificatesInStore;
const TCertFreeCertificateContext _CertFreeCertificateContext;
const TCryptBinaryToStringA _CryptBinaryToStringA;
FORCEINLINE const void* GetConstProcAddres(const TCHAR* ProcName)
{
if (!_Crypt32LibraryHandle)
return NULL;
if (!ProcName)
return NULL;
void* ProcAddress = FPlatformProcess::GetDllExport(_Crypt32LibraryHandle, ProcName);
// This will permanently toggle `bInitialized` to false if ProcAddress is NULL.
bInitialized &= (ProcAddress != NULL);
return ProcAddress;
}
public:
FCryptoUtilsWindows() :
_Crypt32LibraryHandle(FPlatformProcess::GetDllHandle(Crypt32LibraryName)),
bInitialized(true),
_CertOpenSystemStoreA((TCertOpenSystemStoreA) GetConstProcAddres(TEXT("CertOpenSystemStoreA"))),
_CertCloseStore((TCertCloseStore) GetConstProcAddres(TEXT("CertCloseStore"))),
_CertEnumCertificatesInStore((TCertEnumCertificatesInStore) GetConstProcAddres(TEXT("CertEnumCertificatesInStore"))),
_CertFreeCertificateContext((TCertFreeCertificateContext) GetConstProcAddres(TEXT("CertFreeCertificateContext"))),
_CryptBinaryToStringA((TCryptBinaryToStringA) GetConstProcAddres(TEXT("CryptBinaryToStringA")))
{
}
~FCryptoUtilsWindows()
{
if (_Crypt32LibraryHandle)
{
FPlatformProcess::FreeDllHandle(_Crypt32LibraryHandle);
}
}
const FString GetRootsPem() const
{
FString RootsPem;
// It is unsafe to whatever if helper initialization failed.
if (!bInitialized)
{
return MoveTemp(RootsPem);
}
void* CertStore = _CertOpenSystemStoreA(0, "ROOT");
if (!CertStore)
{
return MoveTemp(RootsPem);
}
Win32PCCertContext Context = NULL;
while ((Context = _CertEnumCertificatesInStore(CertStore, Context)) != NULL)
{
// Detect length
Win32Dword StringWithHeaderLength = 0;
const int ComputeSizeRet = _CryptBinaryToStringA(Context->pbCertEncoded, Context->cbCertEncoded,\
_CU_CRYPT_STRING_BASE64HEADER, NULL, &StringWithHeaderLength);
if (!ComputeSizeRet)
{
break;
}
// Copy data
Win32LPStr CertWideString = (Win32LPStr) FMemory::Malloc((size_t) StringWithHeaderLength);
const int WriteStringRet = _CryptBinaryToStringA(Context->pbCertEncoded, Context->cbCertEncoded,\
_CU_CRYPT_STRING_BASE64HEADER, CertWideString, &StringWithHeaderLength);
if (!WriteStringRet)
{
FMemory::Free(CertWideString);
break;
}
RootsPem.Append(ANSI_TO_TCHAR(CertWideString));
RootsPem.Append(LINE_TERMINATOR);
FMemory::Free(CertWideString);
}
// Free the Context if any. Non-zero context means we've broken the loop above.
// This signifies an error.
if (Context)
{
_CertFreeCertificateContext(Context);
RootsPem.Empty();
}
const int CertCloseStore_ret = _CertCloseStore(CertStore, 0);
if (CertCloseStore_ret == 0)
{
UE_LOG(LogCertStore, Error, TEXT("Failed to close the CertStore: %s returned %d"),\
TEXT("CertCloseStore"), CertCloseStore_ret);
}
return MoveTemp(RootsPem);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment