adapters/x509_schannel.c (517 lines of code) (raw):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "windows.h"
#include "azure_c_shared_utility/gballoc.h"
#include "azure_c_shared_utility/x509_schannel.h"
#include "azure_c_shared_utility/xlogging.h"
#include "azure_c_shared_utility/safe_math.h"
#if _MSC_VER > 1500
#include <ncrypt.h>
#endif
#define KEY_NAME L"AzureAliasKey"
#define ECC_256_MAGIC_NUMBER 0x20
#define ECC_384_MAGIC_NUMBER 0x30
typedef enum x509_CERT_TYPE_TAG
{
x509_TYPE_RSA,
x509_TYPE_ECC,
x509_TYPE_UNKNOWN,
} x509_CERT_TYPE;
typedef struct X509_SCHANNEL_HANDLE_DATA_TAG
{
HCRYPTPROV hProv;
HCRYPTKEY x509hcryptkey;
PCCERT_CONTEXT x509certificate_context;
x509_CERT_TYPE cert_type;
} X509_SCHANNEL_HANDLE_DATA;
static const char end_certificate_in_pem[] = "-----END CERTIFICATE-----";
static const size_t end_certificate_in_pem_length = sizeof(end_certificate_in_pem) - 1;
static const char pem_crlf_value[] = "\r\n";
static const size_t pem_crlf_value_length = sizeof(pem_crlf_value) - 1;
static unsigned char* convert_cert_to_binary(const char* crypt_value, DWORD crypt_value_in_len, DWORD* crypt_length)
{
unsigned char* result = NULL;
DWORD result_length = 0;
if (!CryptStringToBinaryA(crypt_value, crypt_value_in_len, CRYPT_STRING_ANY, NULL, &result_length, NULL, NULL))
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("Failed determine crypt value size");
result = NULL;
}
else
{
if ((result = (unsigned char*)malloc(result_length)) == NULL)
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogError("unable to allocate memory for crypt value");
}
else
{
if (!CryptStringToBinaryA(crypt_value, 0, CRYPT_STRING_ANY, result, &result_length, NULL, NULL))
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("Failed convert crypt value to binary");
free(result);
result = NULL;
}
else
{
if (crypt_length != NULL)
{
*crypt_length = result_length;
}
}
}
}
return result;
}
static unsigned char* decode_crypt_object(unsigned char* private_key, DWORD key_length, DWORD* blob_size, x509_CERT_TYPE* cert_type)
{
unsigned char* result;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4306)
#endif // _MSC_VER
LPCSTR key_type = PKCS_RSA_PRIVATE_KEY;
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
DWORD private_key_blob_size = 0;
/*Codes_SRS_X509_SCHANNEL_02_004: [ x509_schannel_create shall decode the private key by calling CryptDecodeObjectEx. ]*/
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, key_type, private_key, key_length, 0, NULL, NULL, &private_key_blob_size))
{
#if _MSC_VER > 1500
key_type = X509_ECC_PRIVATE_KEY;
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, key_type, private_key, key_length, 0, NULL, NULL, &private_key_blob_size))
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("Failed to CryptDecodeObjectEx x509 private key");
*cert_type = x509_TYPE_UNKNOWN;
}
else
{
*cert_type = x509_TYPE_ECC;
}
#else
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("Failed to CryptDecodeObjectEx x509 private key");
*cert_type = x509_TYPE_UNKNOWN;
#endif
}
else
{
*cert_type = x509_TYPE_RSA;
}
if (*cert_type != x509_TYPE_UNKNOWN)
{
if ((result = (unsigned char*)malloc(private_key_blob_size)) == NULL)
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogError("unable to malloc for x509 private key blob");
}
else
{
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, key_type, private_key, key_length, 0, NULL, result, &private_key_blob_size))
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("Failed to CryptDecodeObjectEx x509 private key");
free(result);
result = NULL;
}
else
{
if (blob_size != NULL)
{
*blob_size = private_key_blob_size;
}
}
}
}
else
{
result = NULL;
}
return result;
}
static int set_ecc_certificate_info(X509_SCHANNEL_HANDLE_DATA* x509_handle, unsigned char* x509privatekeyBlob)
{
int result;
#if _MSC_VER > 1500
BCRYPT_ECCKEY_BLOB* pKeyBlob;
SECURITY_STATUS status;
CRYPT_BIT_BLOB* pPubKeyBlob = &x509_handle->x509certificate_context->pCertInfo->SubjectPublicKeyInfo.PublicKey;
CRYPT_ECC_PRIVATE_KEY_INFO* pPrivKeyInfo = (CRYPT_ECC_PRIVATE_KEY_INFO*)x509privatekeyBlob;
DWORD pubSize = pPubKeyBlob->cbData - 1;
DWORD privSize = pPrivKeyInfo->PrivateKey.cbData;
size_t keyBlobSize = safe_add_size_t(safe_add_size_t(sizeof(BCRYPT_ECCKEY_BLOB), pubSize), privSize);
BYTE* pubKeyBuf = pPubKeyBlob->pbData + 1;
BYTE* privKeyBuf = pPrivKeyInfo->PrivateKey.pbData;
if (keyBlobSize == SIZE_MAX ||
(pKeyBlob = (BCRYPT_ECCKEY_BLOB*)malloc(keyBlobSize)) == NULL)
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogError("Failed to malloc NCrypt private key blob, size:%zu", keyBlobSize);
result = MU_FAILURE;
}
else
{
pKeyBlob->dwMagic = privSize == ECC_256_MAGIC_NUMBER ? BCRYPT_ECDSA_PRIVATE_P256_MAGIC
: privSize == ECC_384_MAGIC_NUMBER ? BCRYPT_ECDSA_PRIVATE_P384_MAGIC
: BCRYPT_ECDSA_PRIVATE_P521_MAGIC;
pKeyBlob->cbKey = privSize;
memcpy((BYTE*)(pKeyBlob + 1), pubKeyBuf, pubSize);
memcpy((BYTE*)(pKeyBlob + 1) + pubSize, privKeyBuf, privSize);
/* Codes_SRS_X509_SCHANNEL_02_005: [ x509_schannel_create shall call CryptAcquireContext. ] */
/* at this moment, both the private key and the certificate are decoded for further usage */
/* NOTE: As no WinCrypt key storage provider supports ECC keys, NCrypt is used instead */
status = NCryptOpenStorageProvider(&x509_handle->hProv, MS_KEY_STORAGE_PROVIDER, 0);
if (status != ERROR_SUCCESS)
{
/* Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogError("NCryptOpenStorageProvider failed with error 0x%08X", status);
result = MU_FAILURE;
}
else
{
SECURITY_STATUS status2;
NCryptBuffer ncBuf = { sizeof(KEY_NAME), NCRYPTBUFFER_PKCS_KEY_NAME, KEY_NAME };
NCryptBufferDesc ncBufDesc;
ncBufDesc.ulVersion = 0;
ncBufDesc.cBuffers = 1;
ncBufDesc.pBuffers = &ncBuf;
CRYPT_KEY_PROV_INFO keyProvInfo = { KEY_NAME, MS_KEY_STORAGE_PROVIDER, 0, 0, 0, NULL, 0 };
/*Codes_SRS_X509_SCHANNEL_02_006: [ x509_schannel_create shall import the private key by calling CryptImportKey. ] */
/*NOTE: As no WinCrypt key storage provider supports ECC keys, NCrypt is used instead*/
status = NCryptImportKey(x509_handle->hProv, 0, BCRYPT_ECCPRIVATE_BLOB, &ncBufDesc, &x509_handle->x509hcryptkey, (BYTE*)pKeyBlob, (DWORD)keyBlobSize, NCRYPT_OVERWRITE_KEY_FLAG);
if (status == ERROR_SUCCESS)
{
status2 = NCryptFreeObject(x509_handle->x509hcryptkey);
if (status2 != ERROR_SUCCESS)
{
LogError("NCryptFreeObject for key handle failed with error 0x%08X", status2);
}
else
{
x509_handle->x509hcryptkey = 0;
}
}
status2 = NCryptFreeObject(x509_handle->hProv);
if (status2 != ERROR_SUCCESS)
{
LogError("NCryptFreeObject for provider handle failed with error 0x%08X", status2);
}
else
{
x509_handle->hProv = 0;
}
if (status != ERROR_SUCCESS)
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogError("NCryptImportKey failed with error 0x%08X", status);
result = MU_FAILURE;
}
else if (!CertSetCertificateContextProperty(x509_handle->x509certificate_context, CERT_KEY_PROV_INFO_PROP_ID, 0, &keyProvInfo))
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("CertSetCertificateContextProperty failed to set NCrypt key handle property");
result = MU_FAILURE;
}
else
{
result = 0;
}
}
free(pKeyBlob);
}
#else
(void)x509_handle;
(void)x509privatekeyBlob;
LogError("SChannel ECC is not supported in this compliation");
result = MU_FAILURE;
#endif
return result;
}
static int set_rsa_certificate_info(X509_SCHANNEL_HANDLE_DATA* x509_handle, unsigned char* x509privatekeyBlob, DWORD x509privatekeyBlobSize)
{
int result;
/*Codes_SRS_X509_SCHANNEL_02_005: [ x509_schannel_create shall call CryptAcquireContext. ]*/
/*at this moment, both the private key and the certificate are decoded for further usage*/
if (!CryptAcquireContext(&(x509_handle->hProv), NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("CryptAcquireContext failed");
result = MU_FAILURE;
}
else
{
/*Codes_SRS_X509_SCHANNEL_02_006: [ x509_schannel_create shall import the private key by calling CryptImportKey. ] */
if (!CryptImportKey(x509_handle->hProv, x509privatekeyBlob, x509privatekeyBlobSize, (HCRYPTKEY)NULL, 0, &(x509_handle->x509hcryptkey)))
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("CryptImportKey for private key failed");
if (!CryptReleaseContext(x509_handle->hProv, 0))
{
LogErrorWinHTTPWithGetLastErrorAsString("unable to CryptReleaseContext");
}
result = MU_FAILURE;
}
else
{
/*Codes_SRS_X509_SCHANNEL_02_008: [ x509_schannel_create shall call set the certificate private key by calling CertSetCertificateContextProperty. ]*/
if (!CertSetCertificateContextProperty(x509_handle->x509certificate_context, CERT_KEY_PROV_HANDLE_PROP_ID, 0, (void*)x509_handle->hProv))
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("unable to CertSetCertificateContextProperty");
if (!CryptDestroyKey(x509_handle->x509hcryptkey))
{
LogErrorWinHTTPWithGetLastErrorAsString("unable to CryptDestroyKey");
}
if (!CryptReleaseContext(x509_handle->hProv, 0))
{
LogErrorWinHTTPWithGetLastErrorAsString("unable to CryptReleaseContext");
}
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
return result;
}
X509_SCHANNEL_HANDLE x509_schannel_create(const char* x509certificate, const char* x509privatekey)
{
X509_SCHANNEL_HANDLE_DATA* result;
/*this is what happens with the x509 certificate and the x509 private key in this function*/
/*
step 1: they are converted to binary form.
step 1.1: the size of the binary form is computed
step 1.2: the conversion happens
step 2: the binary form is decoded
step 2.1: the decoded form needed size is computed
step 2.2: the decoded form is actually decoded
step 3: a crypto provider is created
step 4: the x509 private key is associated with the crypto provider
step 5: a certificate context is created
step 6: the certificate context is linked to the crypto provider
*/
/*Codes_SRS_X509_SCHANNEL_02_001: [ If x509certificate or x509privatekey are NULL then x509_schannel_create shall fail and return NULL. ]*/
if (
(x509certificate == NULL) ||
(x509privatekey == NULL)
)
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogError("invalid argument const char* x509certificate=%p, const char* x509privatekey=%p", x509certificate, x509privatekey);
result = NULL;
}
else
{
unsigned char* binaryx509Certificate;
unsigned char* binaryx509privatekey;
unsigned char* x509privatekeyBlob;
DWORD binaryx509certificateSize;
DWORD binaryx509privatekeySize;
DWORD x509privatekeyBlobSize;
result = (X509_SCHANNEL_HANDLE_DATA*)malloc(sizeof(X509_SCHANNEL_HANDLE_DATA));
if (result == NULL)
{
LogError("unable to malloc");
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
/*return as is*/
}
else
{
memset(result, 0, sizeof(X509_SCHANNEL_HANDLE_DATA));
/*Codes_SRS_X509_SCHANNEL_02_002: [ x509_schannel_create shall convert the certificate to binary form by calling CryptStringToBinaryA. ]*/
if ((binaryx509Certificate = convert_cert_to_binary(x509certificate, 0, &binaryx509certificateSize)) == NULL)
{
LogError("Failure converting x509 certificate");
free(result);
result = NULL;
}
/*Codes_SRS_X509_SCHANNEL_02_003: [ x509_schannel_create shall convert the private key to binary form by calling CryptStringToBinaryA. ]*/
/*at this moment x509 certificate is ready to be used in CertCreateCertificateContext*/
else if ((binaryx509privatekey = convert_cert_to_binary(x509privatekey, 0, &binaryx509privatekeySize)) == NULL)
{
LogError("Failure converting x509 certificate");
free(binaryx509Certificate);
free(result);
result = NULL;
}
else if ((x509privatekeyBlob = decode_crypt_object(binaryx509privatekey, binaryx509privatekeySize, &x509privatekeyBlobSize, &result->cert_type)) == NULL)
{
LogError("Failure decoding x509 private key");
free(binaryx509Certificate);
free(binaryx509privatekey);
free(result);
result = NULL;
}
else
{
/*Codes_SRS_X509_SCHANNEL_02_007: [ x509_schannel_create shall create a cerficate context by calling CertCreateCertificateContext. ]*/
PCCERT_CONTEXT cert_ctx = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, binaryx509Certificate, binaryx509certificateSize);
if ((result->x509certificate_context = cert_ctx) == NULL)
{
/*Codes_SRS_X509_SCHANNEL_02_010: [ Otherwise, x509_schannel_create shall fail and return a NULL X509_SCHANNEL_HANDLE. ]*/
LogErrorWinHTTPWithGetLastErrorAsString("unable to CertCreateCertificateContext");
free(result);
result = NULL;
}
else
{
/* Codes_SRS_X509_SCHANNEL_07_001: [ x509_schannel_create shall determine whether the certificate is of type RSA or ECC. ] */
if (result->cert_type == x509_TYPE_RSA)
{
if (set_rsa_certificate_info(result, x509privatekeyBlob, x509privatekeyBlobSize) != 0)
{
(void)CertFreeCertificateContext(result->x509certificate_context);
free(result);
result = NULL;
}
}
else
{
if (set_ecc_certificate_info(result, x509privatekeyBlob) != 0)
{
(void)CertFreeCertificateContext(result->x509certificate_context);
free(result);
result = NULL;
}
}
}
free(x509privatekeyBlob);
free(binaryx509privatekey);
free(binaryx509Certificate);
}
}
}
return result;
}
void x509_schannel_destroy(X509_SCHANNEL_HANDLE x509_schannel_handle)
{
/*Codes_SRS_X509_SCHANNEL_02_011: [ If parameter x509_schannel_handle is NULL then x509_schannel_destroy shall do nothing. ]*/
if (x509_schannel_handle != NULL)
{
/*Codes_SRS_X509_SCHANNEL_02_012: [ Otherwise, x509_schannel_destroy shall free all used resources. ]*/
X509_SCHANNEL_HANDLE_DATA* x509crypto = (X509_SCHANNEL_HANDLE_DATA*)x509_schannel_handle;
if (x509crypto->cert_type == x509_TYPE_RSA)
{
if (!CryptDestroyKey(x509crypto->x509hcryptkey))
{
LogErrorWinHTTPWithGetLastErrorAsString("unable to CryptDestroyKey");
}
if (!CryptReleaseContext(x509crypto->hProv, 0))
{
LogErrorWinHTTPWithGetLastErrorAsString("unable to CryptReleaseContext");
}
}
else
{
#if _MSC_VER > 1500
if (x509crypto->x509hcryptkey != 0)
{
(void)NCryptFreeObject(x509crypto->x509hcryptkey);
}
if (x509crypto->hProv != 0)
{
(void)NCryptFreeObject(x509crypto->hProv);
}
#endif
}
if (!CertFreeCertificateContext(x509crypto->x509certificate_context))
{
LogErrorWinHTTPWithGetLastErrorAsString("unable to CertFreeCertificateContext");
}
free(x509crypto);
}
}
PCCERT_CONTEXT x509_schannel_get_certificate_context(X509_SCHANNEL_HANDLE x509_schannel_handle)
{
PCCERT_CONTEXT result;
if (x509_schannel_handle == NULL)
{
/*Codes_SRS_X509_SCHANNEL_02_013: [ If parameter x509_schannel_handle is NULL then x509_schannel_get_certificate_context shall return NULL. ]*/
result = NULL;
}
else
{
/*Codes_SRS_X509_SCHANNEL_02_014: [ Otherwise, x509_schannel_get_certificate_context shall return a non-NULL PCCERT_CONTEXT pointer. ]*/
X509_SCHANNEL_HANDLE_DATA* handleData = (X509_SCHANNEL_HANDLE_DATA*)x509_schannel_handle;
result = handleData->x509certificate_context;
}
return result;
}
// For each certificate specified in trustedCertificate, add to hCertStore. Windows API's (namely
// CryptStringToBinaryA & CertAddEncodedCertificateToStore) do not handle multiple certificates
// at a time in a single call, so add_certificates_to_store() parses the PEM (delimited by "-----END CERTIFICATE-----")
// to call Windows API a cert at a time.
static int add_certificates_to_store(const char* trustedCertificate, HCERTSTORE hCertStore)
{
int result = 0;
int numCertificatesAdded = 0;
const char* trustedCertCurrentRead = trustedCertificate;
unsigned char* trustedCertificateEncoded = NULL;
while (result == 0)
{
const char* endCertificateCurrentRead;
DWORD trustedCertificateEncodedLen;
DWORD lastError;
if ((endCertificateCurrentRead = strstr(trustedCertCurrentRead, end_certificate_in_pem)) == NULL)
{
if (numCertificatesAdded == 0)
{
LogError("Certificate missing closing %s. No certificates can be added to stare", end_certificate_in_pem);
result = MU_FAILURE;
}
break;
}
endCertificateCurrentRead += end_certificate_in_pem_length;
if (strncmp(endCertificateCurrentRead, pem_crlf_value, pem_crlf_value_length) == 0)
{
endCertificateCurrentRead += pem_crlf_value_length;
}
if ((trustedCertificateEncoded = convert_cert_to_binary(trustedCertCurrentRead, (DWORD)(endCertificateCurrentRead - trustedCertCurrentRead), &trustedCertificateEncodedLen)) == NULL)
{
LogError("Cannot encode trusted certificate");
result = MU_FAILURE;
}
else if (CertAddEncodedCertificateToStore(hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, trustedCertificateEncoded, trustedCertificateEncodedLen, CERT_STORE_ADD_NEW, NULL) != TRUE)
{
lastError = GetLastError();
LogError("CertAddEncodedCertificateToStore failed with error 0x%08x", lastError);
result = MU_FAILURE;
}
if (trustedCertificateEncoded != NULL)
{
free(trustedCertificateEncoded);
}
trustedCertCurrentRead = endCertificateCurrentRead;
numCertificatesAdded++;
}
return result;
}
// x509_verify_certificate_in_chain determines whether the certificate in pCertContextToVerify
// chains up to the PEM represented by trustedCertificate or not.
int x509_verify_certificate_in_chain(const char* trustedCertificate, PCCERT_CONTEXT pCertContextToVerify)
{
int result;
HCERTSTORE hCertStore = NULL;
HCERTCHAINENGINE hChainEngine = NULL;
PCCERT_CHAIN_CONTEXT pChainContextToVerify = NULL;
DWORD lastError;
if ((trustedCertificate == NULL) || (pCertContextToVerify == NULL))
{
result = MU_FAILURE;
}
// Creates an in-memory certificate store that is destroyed at end of this function.
else if (NULL == (hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL)))
{
lastError = GetLastError();
LogError("CertOpenStore failed with error 0x%08x", lastError);
result = MU_FAILURE;
}
else if (add_certificates_to_store(trustedCertificate, hCertStore) != 0)
{
LogError("Cannot add certificates to store");
result = MU_FAILURE;
}
else
{
CERT_CHAIN_ENGINE_CONFIG EngineConfig;
memset(&EngineConfig, 0, sizeof(EngineConfig));
EngineConfig.cbSize = sizeof(EngineConfig);
EngineConfig.dwFlags = CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE | CERT_CHAIN_ENABLE_SHARE_STORE;
EngineConfig.hExclusiveRoot = hCertStore;
CERT_CHAIN_PARA ChainPara;
memset(&ChainPara, 0, sizeof(ChainPara));
ChainPara.cbSize = sizeof(ChainPara);
CERT_CHAIN_POLICY_PARA PolicyPara;
memset(&PolicyPara, 0, sizeof(PolicyPara));
PolicyPara.cbSize = sizeof(PolicyPara);
CERT_CHAIN_POLICY_STATUS PolicyStatus;
memset(&PolicyStatus, 0, sizeof(PolicyStatus));
PolicyStatus.cbSize = sizeof(PolicyStatus);
if (CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine) != TRUE)
{
lastError = GetLastError();
LogError("CertCreateCertificateChainEngine failed with error 0x%08x", lastError);
result = MU_FAILURE;
}
else if (CertGetCertificateChain(hChainEngine, pCertContextToVerify, NULL, pCertContextToVerify->hCertStore, &ChainPara, 0, NULL, &pChainContextToVerify) != TRUE)
{
lastError = GetLastError();
LogError("CertGetCertificateChain failed with error 0x%08x", lastError);
result = MU_FAILURE;
}
else if (CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContextToVerify, &PolicyPara, &PolicyStatus) != TRUE)
{
lastError = GetLastError();
LogError("CertVerifyCertificateChainPolicy failed with error 0x%08x", lastError);
result = MU_FAILURE;
}
else if (PolicyStatus.dwError != 0)
{
LogError("CertVerifyCertificateChainPolicy sets certificateStatus = 0x%08x", PolicyStatus.dwError);
result = MU_FAILURE;
}
else
{
result = 0;
}
}
if (pChainContextToVerify != NULL)
{
CertFreeCertificateChain(pChainContextToVerify);
}
if (hChainEngine != NULL)
{
CertFreeCertificateChainEngine(hChainEngine);
}
if (hCertStore != NULL)
{
CertCloseStore(hCertStore, 0);
}
return result;
}