Created
March 31, 2020 19:01
-
-
Save RChehowski/305e8ec4068e53473acc53238a058bbc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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