// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. #include "precomp.h" // prototypes static HRESULT ConfigureCertificates( __in SCA_ACTION saAction ); static LPCWSTR StoreMapping( __in int iStore ); static HRESULT FindExistingCertificate( __in LPCWSTR wzName, __in DWORD dwStoreLocation, __in LPCWSTR wzStore, __out BYTE** prgbCertificate, __out DWORD* pcbCertificate ); static HRESULT ResolveCertificate( __in LPCWSTR wzId, __in LPCWSTR wzName, __in DWORD dwStoreLocation, __in LPCWSTR wzStoreName, __in DWORD dwAttributess, __in LPCWSTR wzData, __in LPCWSTR wzPFXPassword, __out BYTE** ppbCertificate, __out DWORD* pcbCertificate ); static HRESULT ReadCertificateFile( __in LPCWSTR wzPath, __out BYTE** prgbData, __out DWORD* pcbData ); static HRESULT CertificateToHash( __in BYTE* pbCertificate, __in DWORD cbCertificate, __in DWORD dwStoreLocation, __in LPCWSTR wzPFXPassword, __in BYTE rgbHash[], __in DWORD cbHash ); /* HRESULT ScaGetCertificateByPath(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling, INT iStore, INT iStoreLocation, LPCWSTR wzSslCertificate, LPCWSTR wzPFXPassword, BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer); HRESULT ScaGetCertificateByRequest(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling, INT iStore, INT iStoreLocation, LPCWSTR wzDistinguishedName, LPCWSTR wzCA, BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer); HRESULT ScaSslNewCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthorityOrig, BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer); HRESULT ScaSslExistingCertificateByName(LPCWSTR pwzName, INT iStore, INT iStoreLocation, BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer); HRESULT ScaSslExistingCertificateByBinaryData(INT iStore, INT iStoreLocation, BYTE* pwzData, DWORD cchData); HRESULT CreateEnroll(ICEnroll2 **hEnroll, INT iStore, INT iStoreLocation); HRESULT RequestCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthority, BSTR *pbstrCertificate); VOID ParseCertificateAuthority(__in LPCWSTR wzCertificateAuthorityOrig, __out LPWSTR *pwzBuffer, __out LPWSTR **hwzCAArray, __out int *piCAArray); */ LPCWSTR vcsCertQuery = L"SELECT `Certificate`, `Name`, `Component_`, `StoreLocation`, `StoreName`, `Attributes`, `Binary_`, `CertificatePath`, `PFXPassword` FROM `Wix4Certificate`"; enum eCertQuery { cqCertificate = 1, cqName, cqComponent, cqStoreLocation, cqStoreName, cqAttributes, cqCertificateBinary, cqCertificatePath, cqPFXPassword }; /******************************************************************** InstallCertificates - CUSTOM ACTION ENTRY POINT for installing certificates ********************************************************************/ extern "C" UINT __stdcall InstallCertificates( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; // initialize hr = WcaInitialize(hInstall, "InstallCertificates"); ExitOnFailure(hr, "Failed to initialize"); hr = ConfigureCertificates(SCA_ACTION_INSTALL); LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } /******************************************************************** UninstallCertificates - CUSTOM ACTION ENTRY POINT for uninstalling certificates ********************************************************************/ extern "C" UINT __stdcall UninstallCertificates( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; // initialize hr = WcaInitialize(hInstall, "UninstallCertificates"); ExitOnFailure(hr, "Failed to initialize"); hr = ConfigureCertificates(SCA_ACTION_UNINSTALL); LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } static HRESULT ConfigureCertificates( __in SCA_ACTION saAction ) { //AssertSz(FALSE, "debug ConfigureCertificates()."); HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; PMSIHANDLE hViewCertificate; PMSIHANDLE hRecCertificate; INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; WCHAR* pwzId = NULL; WCHAR* pwzName = NULL; WCHAR* pwzComponent = NULL; int iData = 0; DWORD dwStoreLocation = 0; LPWSTR pwzStoreName = 0; DWORD dwAttributes = 0; WCHAR* pwzData = NULL; WCHAR* pwzPFXPassword = NULL; WCHAR* pwzCaData = NULL; WCHAR* pwzRollbackCaData = NULL; BYTE* pbCertificate = NULL; DWORD cbCertificate = 0; DWORD_PTR cbPFXPassword = 0; // Bail quickly if the Certificate table isn't around. if (S_OK != WcaTableExists(L"Wix4Certificate")) { WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureCertificates() - required table not present."); ExitFunction1(hr = S_FALSE); } // Process the Certificate table. hr = WcaOpenExecuteView(vcsCertQuery, &hViewCertificate); ExitOnFailure(hr, "failed to open view on Certificate table"); while (SUCCEEDED(hr = WcaFetchRecord(hViewCertificate, &hRecCertificate))) { hr = WcaGetRecordString(hRecCertificate, cqCertificate, &pwzId); // the id is just useful to have up front ExitOnFailure(hr, "failed to get Certificate.Certificate"); hr = WcaGetRecordString(hRecCertificate, cqComponent, &pwzComponent); ExitOnFailure(hr, "failed to get Certificate.Component_"); er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzComponent, &isInstalled, &isAction); hr = HRESULT_FROM_WIN32(er); ExitOnFailure(hr, "failed to get state for component: %ls", pwzComponent); if (!(WcaIsInstalling(isInstalled, isAction) && SCA_ACTION_INSTALL == saAction) && !(WcaIsUninstalling(isInstalled, isAction) && SCA_ACTION_UNINSTALL == saAction) && !(WcaIsReInstalling(isInstalled, isAction))) { WcaLog(LOGMSG_VERBOSE, "Skipping non-action certificate: %ls", pwzId); continue; } // extract the rest of the data from the Certificate table hr = WcaGetRecordFormattedString(hRecCertificate, cqName, &pwzName); ExitOnFailure(hr, "failed to get Certificate.Name"); hr = WcaGetRecordInteger(hRecCertificate, cqStoreLocation, &iData); ExitOnFailure(hr, "failed to get Certificate.StoreLocation"); switch (iData) { case SCA_CERTSYSTEMSTORE_CURRENTUSER: dwStoreLocation = CERT_SYSTEM_STORE_CURRENT_USER; break; case SCA_CERTSYSTEMSTORE_LOCALMACHINE: dwStoreLocation = CERT_SYSTEM_STORE_LOCAL_MACHINE; break; default: hr = E_INVALIDARG; ExitOnFailure(hr, "Invalid store location value: %d", iData); } hr = WcaGetRecordString(hRecCertificate, cqStoreName, &pwzStoreName); ExitOnFailure(hr, "failed to get Certificate.StoreName"); hr = WcaGetRecordInteger(hRecCertificate, cqAttributes, reinterpret_cast(&dwAttributes)); ExitOnFailure(hr, "failed to get Certificate.Attributes"); if (dwAttributes & SCA_CERT_ATTRIBUTE_BINARYDATA) { hr = WcaGetRecordString(hRecCertificate, cqCertificateBinary, &pwzData); ExitOnFailure(hr, "failed to get Certificate.Binary_"); } else { hr = WcaGetRecordFormattedString(hRecCertificate, cqCertificatePath, &pwzData); ExitOnFailure(hr, "failed to get Certificate.CertificatePath"); } hr = WcaGetRecordFormattedString(hRecCertificate, cqPFXPassword, &pwzPFXPassword); ExitOnFailure(hr, "failed to get Certificate.PFXPassword"); // Write the common data (for both install and uninstall) to the CustomActionData // to pass data to the deferred CustomAction. hr = StrAllocString(&pwzCaData, pwzName, 0); ExitOnFailure(hr, "Failed to pass Certificate.Certificate to deferred CustomAction."); hr = WcaWriteStringToCaData(pwzStoreName, &pwzCaData); ExitOnFailure(hr, "Failed to pass Certificate.StoreName to deferred CustomAction."); hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCaData); ExitOnFailure(hr, "Failed to pass Certificate.Attributes to deferred CustomAction."); // Copy the rollback data from the deferred data because it's the same up to this point. hr = StrAllocString(&pwzRollbackCaData, pwzCaData, 0); ExitOnFailure(hr, "Failed to allocate string for rollback CustomAction."); // Finally, schedule the correct deferred CustomAction to actually do work. LPCWSTR wzAction = NULL; LPCWSTR wzRollbackAction = NULL; DWORD dwCost = 0; if (SCA_ACTION_UNINSTALL == saAction) { // Find an existing certificate one (if there is one) to so we have it for rollback. hr = FindExistingCertificate(pwzName, dwStoreLocation, pwzStoreName, &pbCertificate, &cbCertificate); ExitOnFailure(hr, "Failed to search for existing certificate with friendly name: %ls", pwzName); if (pbCertificate) { hr = WcaWriteStreamToCaData(pbCertificate, cbCertificate, &pwzRollbackCaData); ExitOnFailure(hr, "Failed to pass Certificate.Data to rollback CustomAction."); hr = WcaWriteStringToCaData(pwzPFXPassword, &pwzRollbackCaData); ExitOnFailure(hr, "Failed to pass Certificate.PFXPassword to rollback CustomAction."); hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCaData); ExitOnFailure(hr, "Failed to pass Certificate.Attributes to deferred CustomAction."); } // Pick the right action to run based on what store we're uninstalling from. if (CERT_SYSTEM_STORE_LOCAL_MACHINE == dwStoreLocation) { wzAction = CUSTOM_ACTION_DECORATION(L"DeleteMachineCertificate"); if (pbCertificate) { wzRollbackAction = L"RollbackDeleteMachineCertificate"; } } else { wzAction = CUSTOM_ACTION_DECORATION(L"DeleteUserCertificate"); if (pbCertificate) { wzRollbackAction = L"RollbackDeleteUserCertificate"; } } dwCost = COST_CERT_DELETE; } else { // Actually get the certificate, resolve it to a blob, and get the blob's hash. hr = ResolveCertificate(pwzId, pwzName, dwStoreLocation, pwzStoreName, dwAttributes, pwzData, pwzPFXPassword, &pbCertificate, &cbCertificate); ExitOnFailure(hr, "Failed to resolve certificate: %ls", pwzId); hr = WcaWriteStreamToCaData(pbCertificate, cbCertificate, &pwzCaData); ExitOnFailure(hr, "Failed to pass Certificate.Data to deferred CustomAction."); hr = WcaWriteStringToCaData(pwzPFXPassword, &pwzCaData); ExitOnFailure(hr, "Failed to pass Certificate.PFXPassword to deferred CustomAction."); // Pick the right action to run based on what store we're installing into. if (CERT_SYSTEM_STORE_LOCAL_MACHINE == dwStoreLocation) { wzAction = CUSTOM_ACTION_DECORATION(L"AddMachineCertificate"); wzRollbackAction = CUSTOM_ACTION_DECORATION(L"RollbackAddMachineCertificate"); } else { wzAction = CUSTOM_ACTION_DECORATION(L"AddUserCertificate"); wzRollbackAction = CUSTOM_ACTION_DECORATION(L"RollbackAddUserCertificate"); } dwCost = COST_CERT_ADD; } if (wzRollbackAction) { hr = WcaDoDeferredAction(wzRollbackAction, pwzRollbackCaData, dwCost); ExitOnFailure(hr, "Failed to schedule rollback certificate action '%ls' for: %ls", wzRollbackAction, pwzId); } hr = WcaDoDeferredAction(wzAction, pwzCaData, dwCost); ExitOnFailure(hr, "Failed to schedule certificate action '%ls' for: %ls", wzAction, pwzId); // Clean up for the next certificate. ReleaseNullMem(pbCertificate); } if (E_NOMOREITEMS == hr) { hr = S_OK; } LExit: if (NULL != pwzPFXPassword && SUCCEEDED(StrSize(pwzPFXPassword, &cbPFXPassword))) { SecureZeroMemory(pwzPFXPassword, cbPFXPassword); } ReleaseMem(pbCertificate); ReleaseStr(pwzCaData); ReleaseStr(pwzPFXPassword); ReleaseStr(pwzData); ReleaseStr(pwzName); ReleaseStr(pwzStoreName); ReleaseStr(pwzComponent); ReleaseStr(pwzId); return hr; } static HRESULT ResolveCertificate( __in LPCWSTR wzId, __in LPCWSTR /*wzName*/, __in DWORD dwStoreLocation, __in LPCWSTR /*wzStoreName*/, __in DWORD dwAttributes, __in LPCWSTR wzData, __in LPCWSTR wzPFXPassword, __out BYTE** ppbCertificate, __out DWORD* pcbCertificate ) { HRESULT hr = S_OK; LPWSTR pwzSql = NULL; PMSIHANDLE hView; PMSIHANDLE hRec; MSIHANDLE hCertificateHashView = NULL; MSIHANDLE hCertificateHashColumns = NULL; BYTE rgbCertificateHash[CB_CERTIFICATE_HASH] = { 0 }; WCHAR wzEncodedCertificateHash[CB_CERTIFICATE_HASH * 2 + 1] = { 0 }; PMSIHANDLE hViewCertificateRequest, hRecCertificateRequest; WCHAR* pwzDistinguishedName = NULL; WCHAR* pwzCA = NULL; BYTE* pbData = NULL; DWORD cbData = 0; if (dwAttributes & SCA_CERT_ATTRIBUTE_REQUEST) { hr = E_NOTIMPL; ExitOnFailure(hr, "Installing certificates by requesting them from a certificate authority is not currently supported"); //if (dwAttributes & SCA_CERT_ATTRIBUTE_OVERWRITE) //{ // // try to overwrite with the patch to a cert file // WcaLog(LOGMSG_VERBOSE, "ConfigureCertificates - Overwrite with SSLCERTIFICATE"); // hr = ScaGetCertificateByPath(pwzName, fIsInstalling, fIsUninstalling, // iStore, iStoreLocation, pwzData, wzPFXPassword, pbstrCertificate, pcbCertificate, pbaHashBuffer); //} //if (hr != S_OK) //{ // if (fIsUninstalling && !fIsInstalling) // { // // for uninstall, we just want to find the existing certificate // hr = ScaSslExistingCertificateByName(pwzName, iStore, iStoreLocation, pbstrCertificate, pcbCertificate, pbaHashBuffer); // ExitOnFailure(hr, "Failed Retrieving existing certificate during uninstall"); // // ok if no existing cert // if (S_OK != hr) // hr = S_OK; // } // else // { // // still no certificate // // user has request this certificate, try to locate DistinguishedName and CA // hr = WcaTableExists(L"CertificateRequest"); // ExitOnFailure(hr, "CertificateRequest is referenced but not found"); // WcaLog(LOGMSG_VERBOSE, "ConfigureCertificates - CertificateRequest table present"); // cchSQLView = 255 + lstrlenW(pwzName); // pwzSQLView = new WCHAR[cchSQLView]; // if (pwzSQLView) // { // hr = ::StringCchPrintfW(pwzSQLView, cchSQLView, L"SELECT `DistinguishedName`, `CA` FROM `CertificateRequest` WHERE `Certificate_`=\'%s\'", pwzName); // ExitOnFailure(hr, "::StringCchPrintfW failed"); // hr = WcaOpenExecuteView(pwzSQLView, &hViewCertificateRequest); // ExitOnFailure(hr, "failed to open view on CertificateRequest table"); // hr = WcaFetchSingleRecord(hViewCertificateRequest, &hRecCertificateRequest); // ExitOnFailure(hr, "failed to retrieve request from CertificateRequest table"); // hr = WcaGetRecordString(hRecCertificateRequest, 1, &pwzDistinguishedName); // ExitOnFailure(hr, "failed to get DistinguishedName"); // hr = WcaGetRecordString(hRecCertificateRequest, 2, &pwzCA); // ExitOnFailure(hr, "failed to get CA"); // if (pwzDistinguishedName && pwzCA && *pwzDistinguishedName && *pwzCA) // { // hr = ScaGetCertificateByRequest(pwzName, fIsInstalling, fIsUninstalling, iStore, iStoreLocation, pwzDistinguishedName, pwzCA, pbstrCertificate, pcbCertificate, pbaHashBuffer); // } // else // { // hr = E_FAIL; // ExitOnFailure(hr, "CertificateRequest entry is empty"); // } // } // else // { // hr = E_FAIL; // ExitOnFailure(hr, "Out of memory"); // } // } //} } else if (dwAttributes & SCA_CERT_ATTRIBUTE_BINARYDATA) { // get the binary stream in Binary hr = WcaTableExists(L"Binary"); if (S_OK != hr) { if (SUCCEEDED(hr)) { hr = E_UNEXPECTED; } ExitOnFailure(hr, "Binary was referenced but there is no Binary table."); } hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%s\'", wzData); ExitOnFailure(hr, "Failed to allocate Binary table query."); hr = WcaOpenExecuteView(pwzSql, &hView); ExitOnFailure(hr, "Failed to open view on Binary table"); hr = WcaFetchSingleRecord(hView, &hRec); ExitOnFailure(hr, "Failed to retrieve request from Binary table"); hr = WcaGetRecordStream(hRec, 1, &pbData, &cbData); ExitOnFailure(hr, "Failed to ready Binary.Data for certificate."); } else if (dwAttributes == SCA_CERT_ATTRIBUTE_DEFAULT) { hr = ReadCertificateFile(wzData, &pbData, &cbData); ExitOnFailure(hr, "Failed to read certificate from file path."); } else { hr = E_INVALIDARG; ExitOnFailure(hr, "Invalid Certificate.Attributes."); } // If we have loaded a certificate, update the Certificate.Hash column. if (pbData) { hr = CertificateToHash(pbData, cbData, dwStoreLocation, wzPFXPassword, rgbCertificateHash, countof(rgbCertificateHash)); ExitOnFailure(hr, "Failed to get SHA1 hash of certificate."); hr = StrHexEncode(rgbCertificateHash, countof(rgbCertificateHash), wzEncodedCertificateHash, countof(wzEncodedCertificateHash)); ExitOnFailure(hr, "Failed to hex encode SHA1 hash of certificate."); // Update the CertificateHash table. hr = WcaAddTempRecord(&hCertificateHashView, &hCertificateHashColumns, L"CertificateHash", NULL, 0, 2, wzId, wzEncodedCertificateHash); ExitOnFailure(hr, "Failed to add encoded has for certificate: %ls", wzId); } *ppbCertificate = pbData; *pcbCertificate = cbData; pbData = NULL; LExit: if (hCertificateHashColumns) { ::MsiCloseHandle(hCertificateHashColumns); } if (hCertificateHashView) { ::MsiCloseHandle(hCertificateHashView); } ReleaseStr(pwzDistinguishedName); ReleaseStr(pwzCA); ReleaseMem(pbData); ReleaseStr(pwzSql); return hr; } static HRESULT ReadCertificateFile( __in LPCWSTR wzPath, __out BYTE** prgbData, __out DWORD* pcbData ) { HRESULT hr = S_OK; PCCERT_CONTEXT pCertContext = NULL; DWORD dwContentType; BYTE* pbData = NULL; DWORD cbData = 0; if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE, reinterpret_cast(wzPath), CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &dwContentType, NULL, NULL, NULL, (LPCVOID*)&pCertContext)) { ExitOnFailure(hr, "Failed to read certificate from file: %ls", wzPath); } if (pCertContext) { cbData = pCertContext->cbCertEncoded; pbData = static_cast(MemAlloc(cbData, FALSE)); ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read certificate from file: %ls", wzPath); CopyMemory(pbData, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded); } else { // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. if (dwContentType & CERT_QUERY_CONTENT_PFX) { SIZE_T size = 0; hr = FileRead(&pbData, &size, wzPath); ExitOnFailure(hr, "Failed to read PFX file: %ls", wzPath); cbData = (DWORD)size; } else { hr = E_UNEXPECTED; ExitOnFailure(hr, "Unexpected certificate type read from disk."); } } *pcbData = cbData; *prgbData = pbData; pbData = NULL; LExit: ReleaseMem(pbData); return hr; } static HRESULT CertificateToHash( __in BYTE* pbCertificate, __in DWORD cbCertificate, __in DWORD dwStoreLocation, __in LPCWSTR wzPFXPassword, __in BYTE rgbHash[], __in DWORD cbHash ) { HRESULT hr = S_OK; HCERTSTORE hPfxCertStore = NULL; PCCERT_CONTEXT pCertContext = NULL; PCCERT_CONTEXT pCertContextEnum = NULL; CRYPT_DATA_BLOB blob = { 0 }; CRYPT_KEY_PROV_INFO* pPfxInfo = NULL; DWORD dwKeyset = (CERT_SYSTEM_STORE_CURRENT_USER == dwStoreLocation) ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET; DWORD dwEncodingType; DWORD dwContentType; DWORD dwFormatType; blob.pbData = pbCertificate; blob.cbData = cbCertificate; if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext)) { ExitWithLastError(hr, "Failed to process certificate as a valid certificate."); } if (!pCertContext) { // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. if (dwContentType & CERT_QUERY_CONTENT_PFX) { // If we fail and our password is blank, also try passing in NULL for the password (according to the docs) hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, dwKeyset); if (NULL == hPfxCertStore && !*wzPFXPassword) { hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, NULL, dwKeyset); } ExitOnNullWithLastError(hPfxCertStore, hr, "Failed to open PFX file."); // Find the first cert with a private key, or just use the last one for (pCertContextEnum = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContextEnum); pCertContextEnum; pCertContextEnum = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContextEnum)) { pCertContext = pCertContextEnum; if (pCertContext && CertHasPrivateKey(pCertContext, NULL)) { break; } } ExitOnNullWithLastError(pCertContext, hr, "Failed to read first certificate out of PFX file."); // Ignore failures, the worst that happens is some parts of the PFX get left behind. CertReadProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, &pPfxInfo, NULL); } else { hr = E_UNEXPECTED; ExitOnFailure(hr, "Unexpected certificate type processed."); } } DWORD cb = cbHash; if (!::CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, static_cast(rgbHash), &cb)) { ExitWithLastError(hr, "Failed to get certificate SHA1 hash property."); } AssertSz(cb == cbHash, "Did not correctly read certificate SHA1 hash."); LExit: if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } if (hPfxCertStore) { ::CertCloseStore(hPfxCertStore, 0); } if (pPfxInfo) { HCRYPTPROV hProvIgnored = NULL; // ignored on deletes. ::CryptAcquireContextW(&hProvIgnored, pPfxInfo->pwszContainerName, pPfxInfo->pwszProvName, pPfxInfo->dwProvType, dwKeyset | CRYPT_DELETEKEYSET | CRYPT_SILENT); MemFree(pPfxInfo); } return hr; } static HRESULT FindExistingCertificate( __in LPCWSTR wzName, __in DWORD dwStoreLocation, __in LPCWSTR wzStore, __out BYTE** prgbCertificate, __out DWORD* pcbCertificate ) { HRESULT hr = S_OK; HCERTSTORE hCertStore = NULL; PCCERT_CONTEXT pCertContext = NULL; BYTE* pbCertificate = NULL; DWORD cbCertificate = 0; hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwStoreLocation | CERT_STORE_READONLY_FLAG, wzStore); MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "Failed to open certificate store."); // Loop through the certificate, looking for certificates that match our friendly name. pCertContext = CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); while (pCertContext) { WCHAR wzFriendlyName[256] = { 0 }; DWORD cbFriendlyName = sizeof(wzFriendlyName); if (::CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, reinterpret_cast(wzFriendlyName), &cbFriendlyName) && CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, 0, wzName, -1, wzFriendlyName, -1)) { // If the certificate with matching friendly name is valid, let's use that. long lVerify = ::CertVerifyTimeValidity(NULL, pCertContext->pCertInfo); if (0 == lVerify) { cbCertificate = pCertContext->cbCertEncoded; pbCertificate = static_cast(MemAlloc(cbCertificate, FALSE)); ExitOnNull(pbCertificate, hr, E_OUTOFMEMORY, "Failed to allocate memory to copy out exist certificate."); CopyMemory(pbCertificate, pCertContext->pbCertEncoded, cbCertificate); break; // found a matching certificate, no more searching necessary } } // Next certificate in the store. PCCERT_CONTEXT pNext = ::CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext); // old pCertContext is freed by CertFindCertificateInStore pCertContext = pNext; } *prgbCertificate = pbCertificate; *pcbCertificate = cbCertificate; pbCertificate = NULL; LExit: ReleaseMem(pbCertificate); if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } if (hCertStore) { ::CertCloseStore(hCertStore, 0); } return hr; } /* HRESULT CreateEnroll(ICEnroll2 **hEnroll, INT iStore, INT iStoreLocation) { ICEnroll2 *pEnroll = NULL; HRESULT hr = S_OK; LONG lFlags; DWORD dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; // create IEntroll hr = CoCreateInstance( CLSID_CEnroll, NULL, CLSCTX_INPROC_SERVER, IID_ICEnroll2, (void **)&pEnroll ); if (FAILED(hr)) return hr; switch (iStore) { case SCA_CERT_STORENAME_MY: pEnroll->get_MyStoreFlags(&lFlags); lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; lFlags |= dwFlags; // following call will change Request store flags also pEnroll->put_MyStoreFlags(lFlags); break; case SCA_CERT_STORENAME_CA: pEnroll->get_CAStoreFlags(&lFlags); lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; lFlags |= dwFlags; // following call will change Request store flags also pEnroll->put_CAStoreFlags(lFlags); break; case SCA_CERT_STORENAME_REQUEST: pEnroll->get_RequestStoreFlags(&lFlags); lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; lFlags |= dwFlags; // following call will change Request store flags also pEnroll->put_RequestStoreFlags(lFlags); break; case SCA_CERT_STORENAME_ROOT: pEnroll->get_RootStoreFlags(&lFlags); lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; lFlags |= dwFlags; // following call will change Request store flags also pEnroll->put_RootStoreFlags(lFlags); break; default: hr = E_FAIL; return hr; } pEnroll->get_GenKeyFlags(&lFlags); lFlags |= CRYPT_EXPORTABLE; pEnroll->put_GenKeyFlags(lFlags); pEnroll->put_KeySpec(AT_KEYEXCHANGE); pEnroll->put_ProviderType(PROV_RSA_SCHANNEL); pEnroll->put_DeleteRequestCert(TRUE); *hEnroll = pEnroll; return hr; } HRESULT RequestCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthority, BSTR *pbstrCertificate) { if (pbstrCertificate == NULL) return E_INVALIDARG; HRESULT hr; ICEnroll2 *pEnroll = NULL; ICertRequest *pCertRequest = NULL; BSTR bstrRequest = NULL; LONG nDisposition; BSTR bstrCertificateUsage = NULL; BSTR bstrCertificateAttributes = NULL; BSTR bstrCertificateAuthority = NULL; // equivalent to: sprintf(bstrDistinguishedName, L"%s,CN=%s", wzDistinguishedName, wzComputerName); DWORD cchComputerName = lstrlenW(wzComputerName); DWORD cchDistinguishedName = lstrlenW(wzDistinguishedName); CONST DWORD cchbstrDistinguishedName = 5 + cchComputerName + cchDistinguishedName; BSTR bstrDistinguishedName = SysAllocStringLen(NULL, cchbstrDistinguishedName); ExitOnNull(bstrDistinguishedName, hr, E_OUTOFMEMORY, "Failed to allocate space for distinguished name."); ::StringCchCopyW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, wzDistinguishedName); ::StringCchCatW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, L",CN="); ::StringCchCatW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, wzComputerName); bstrCertificateUsage = SysAllocString(WIDE(szOID_PKIX_KP_SERVER_AUTH)); ExitOnNull(bstrCertificateUsage, hr, E_OUTOFMEMORY, "Failed to allocate space for Certificate Usage."); bstrCertificateAttributes = SysAllocString(L"CertificateTemplate:WebServer"); bstrCertificateAuthority = SysAllocString(wzCertificateAuthority); ExitOnNull(bstrCertificateAuthority, hr, E_OUTOFMEMORY, "Failed to allocate space for Certificate Authority."); hr = CreateEnroll(&pEnroll, iStore, iStoreLocation); ExitOnFailure(hr, "failed CoCreateInstance IEnroll"); hr = pEnroll->createPKCS10(bstrDistinguishedName, bstrCertificateUsage, &bstrRequest); ExitOnFailure(hr, "failed createPKCS10"); hr = CoCreateInstance(CLSID_CCertRequest, NULL, CLSCTX_INPROC_SERVER, IID_ICertRequest, (void **)&pCertRequest); ExitOnFailure(hr, "failed CoCreateInstance ICertRequest"); hr = pCertRequest->Submit(CR_IN_BASE64 | CR_IN_PKCS10, bstrRequest, bstrCertificateAttributes, bstrCertificateAuthority, &nDisposition); ExitOnFailure(hr, "failed ICertRequest.Submit"); hr = (nDisposition == CR_DISP_ISSUED) ? S_OK : E_FAIL; ExitOnFailure(hr, "failed CR_DISP_ISSUED"); hr = pCertRequest->GetCertificate(CR_OUT_BASE64, pbstrCertificate); ExitOnFailure(hr, "failed ICertRequest.GetCertificate"); // save the certificate in place, cannot be passed to a deferred custom action hr = pEnroll->acceptPKCS7(*pbstrCertificate); ExitOnFailure(hr, "failed accept certificate into MY store"); LExit: ReleaseObject(pCertRequest); ReleaseBSTR(bstrRequest); ReleaseObject(pEnroll); ReleaseBSTR(bstrCertificateAuthority); ReleaseBSTR(bstrCertificateAttributes); ReleaseBSTR(bstrDistinguishedName); return hr; } VOID ParseCertificateAuthority(__in LPCWSTR wzCertificateAuthorityOrig, __out LPWSTR *pwzBuffer, __out LPWSTR **hwzCAArray, __out int *piCAArray) { // @asAuthorities = split /;/, $sAuthority; CONST WCHAR wchDelimiter = L';'; // copy constant into a buffer Assert(wzCertificateAuthorityOrig); INT cchCA = lstrlenW(wzCertificateAuthorityOrig) + 1; WCHAR* wzBuffer = new WCHAR[cchCA]; if (!wzBuffer) return; ::StringCchCopyW(wzBuffer, cchCA, wzCertificateAuthorityOrig); // determine the number of strings in the field int iCAArray = 1; int i; for (i = 0; i < cchCA; ++i) { if (wzBuffer[i] == wchDelimiter) ++iCAArray; } LPWSTR *pwzCAArray = (LPWSTR*) new BYTE[iCAArray * sizeof(LPWSTR)]; if (!pwzCAArray) { return; } pwzCAArray[0] = wzBuffer; iCAArray = 0; for (i = 0; i < cchCA; ++i) { if (wzBuffer[i] != wchDelimiter) continue; wzBuffer[i] = 0; // convert buffer into MULTISZ pwzCAArray[iCAArray] = &wzBuffer[i+1]; ++iCAArray; } *pwzBuffer = wzBuffer; *hwzCAArray = pwzCAArray; *piCAArray = iCAArray; } HRESULT ScaSslExistingCertificateByBinaryData(INT iStore, INT iStoreLocation, BYTE* pwzData, DWORD cchData) { HRESULT hr = S_FALSE; HCERTSTORE hCertStore = NULL; PCCERT_CONTEXT pCertCtx = NULL, pCertCtxExisting = NULL; DWORD dwFlags = 0; LPCWSTR wzStore = StoreMapping(iStore); CERT_BLOB blob; dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, wzStore); MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store, OK on uninstall"); blob.pbData = pwzData; blob.cbData = cchData; if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, NULL, NULL, NULL, NULL, (LPCVOID*)&pCertCtx)) ExitOnLastError(hr, "failed to parse the certificate blob, OK on uninstall"); pCertCtxExisting = CertFindCertificateInStore( hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_EXISTING, pCertCtx, NULL); if (pCertCtxExisting) { hr = S_OK; } LExit: if (pCertCtx) { CertFreeCertificateContext(pCertCtx); pCertCtx = NULL; } if (pCertCtxExisting) { CertFreeCertificateContext(pCertCtxExisting); pCertCtxExisting = NULL; } if (hCertStore) { CertCloseStore(hCertStore, 0); hCertStore = NULL; } return hr; } HRESULT ScaSslExistingCertificateByName(LPCWSTR pwzName, INT iStore, INT iStoreLocation, BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) { HRESULT hr = S_FALSE; HCERTSTORE hSystemStore = NULL; PCCERT_CONTEXT pTargetCert = NULL; WCHAR wzFriendlyName[MAX_PATH] = {0}; DWORD dwFriendlyNameLen = sizeof(wzFriendlyName); // Call CertOpenStore to open the CA store. hSystemStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY, 0, NULL, (iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT) | CERT_STORE_OPEN_EXISTING_FLAG, StoreMapping(iStore)); if (hSystemStore == NULL) ExitFunction(); // Get a particular certificate using CertFindCertificateInStore. pTargetCert = CertFindCertificateInStore( hSystemStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); while (pTargetCert != NULL) { if ((CertGetCertificateContextProperty(pTargetCert, CERT_FRIENDLY_NAME_PROP_ID, (BYTE*)wzFriendlyName, &dwFriendlyNameLen)) && lstrcmpW(wzFriendlyName, pwzName) == 0) { // pTargetCert is a pointer to the desired certificate. // Check the certificate's validity. switch (CertVerifyTimeValidity( NULL, pTargetCert->pCertInfo)) { case 1: // Certificate is expired WcaLog(LOGMSG_STANDARD, "The SSL certificate has expired"); // always remove it { PCCERT_CONTEXT pDupCertContext = CertDuplicateCertificateContext(pTargetCert); if (pDupCertContext && CertDeleteCertificateFromStore(pDupCertContext)) { WcaLog(LOGMSG_STANDARD, "A SSL certificate has removed"); } } break; case 0: // Certificate is valid WcaLog(LOGMSG_STANDARD, "The SSL certificate is valid"); hr = S_OK; if (pbaHashBuffer) { // if the certificate already exists and is valid, use that one DWORD dwHashSize = CB_CERTIFICATE_HASH; hr = CertGetCertificateContextProperty(pTargetCert, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) ? S_OK : E_FAIL; ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); Assert(pbstrCertificate); Assert(pcbCertificate); ReleaseBSTR(*pbstrCertificate); *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pTargetCert->pbCertEncoded), pTargetCert->cbCertEncoded); *pcbCertificate = pTargetCert->cbCertEncoded; } ExitFunction(); break; default: // Certificate not valid yet, ignore it WcaLog(LOGMSG_STANDARD, "The SSL certificate is not valid"); break; } } pTargetCert = CertFindCertificateInStore( hSystemStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pTargetCert); wzFriendlyName[0] = 0; dwFriendlyNameLen = sizeof(wzFriendlyName); } LExit: // Clean up memory and quit. if (pTargetCert) { CertFreeCertificateContext(pTargetCert); pTargetCert = NULL; } if (hSystemStore) { CertCloseStore(hSystemStore, CERT_CLOSE_STORE_CHECK_FLAG); hSystemStore = NULL; } return hr; } HRESULT ScaSslNewCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthorityOrig, BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) { if (pbstrCertificate == NULL) return E_INVALIDARG; HRESULT hr = S_OK; LPWSTR wzCABuffer = NULL; LPWSTR *wzCAArray = NULL; int iCAArray = 0; // otherwise call the CA for one ParseCertificateAuthority(wzCertificateAuthorityOrig, &wzCABuffer, &wzCAArray, &iCAArray); // try each authority three times for (int i = 0; i < 3 * iCAArray; ++i) { LPCWSTR wzCA = wzCAArray[i % iCAArray]; if (NULL == wzCA || NULL == wzCA[0]) continue; WcaLog(LOGMSG_STANDARD, "Requesting SSL certificate from %ls", wzCA); hr = RequestCertificate(pwzName, iStore, iStoreLocation, wzComputerName, wzDistinguishedName, wzCA, pbstrCertificate); if (hr == S_OK && pbstrCertificate) { // set the friendly name CRYPT_HASH_BLOB hblob; CERT_BLOB blob; HCERTSTORE hCertStore = NULL; PCCERT_CONTEXT pCertCtxExisting = NULL; blob.pbData = (BYTE*)pwzName; blob.cbData = (lstrlenW(pwzName) + 1) * sizeof(pwzName[0]); // including terminating null *pcbCertificate = SysStringByteLen(*pbstrCertificate); hr = CertificateToHash(*pbstrCertificate, pbaHashBuffer); ExitOnFailure(hr, "failed to CertificateToHash for an existing certificate"); hblob.pbData = pbaHashBuffer; hblob.cbData = CB_CERTIFICATE_HASH; hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, (iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT), StoreMapping(iStore)); MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store"); pCertCtxExisting = CertFindCertificateInStore( hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_HASH, &hblob, NULL); if (pCertCtxExisting) { CertSetCertificateContextProperty( pCertCtxExisting, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob); } if (pCertCtxExisting) { CertFreeCertificateContext(pCertCtxExisting); pCertCtxExisting = NULL; } if (hCertStore) { CertCloseStore(hCertStore, 0); hCertStore = NULL; } ExitFunction(); } if (pbstrCertificate && *pbstrCertificate) { SysFreeString(*pbstrCertificate); pbstrCertificate = NULL; } } hr = E_FAIL; ExitOnFailure(hr, "failed to RequestCertificate"); LExit: if (wzCABuffer) { delete wzCABuffer; } if (wzCAArray) { delete wzCAArray; } return hr; } HRESULT ScaGetCertificateByRequest(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling, INT iStore, INT iStoreLocation, LPCWSTR wzDistinguishedName, LPCWSTR wzCA, BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) { HRESULT hr = S_OK; WCHAR wzComputerName[MAX_COMPUTER_NAME] = {0}; WCHAR* pwzData = NULL; DWORD cchData = 0; // override %COMPUTERNAME% with DOMAINNAME property hr = WcaGetProperty( L"DOMAINNAME", &pwzData); ExitOnFailure(hr, "Failed to get Property DOMAINNAME"); if (*pwzData) { // if DOMAINNAME is set, use it ::StringCchCopyW(wzComputerName, MAX_COMPUTER_NAME, pwzData); } else { // otherwise get the intranet name given by %COMPUTERNAME% GetEnvironmentVariableW(L"COMPUTERNAME", wzComputerName, MAX_COMPUTER_NAME); } hr = ScaSslExistingCertificateByName(pwzName, iStore, iStoreLocation, pbstrCertificate, pcbCertificate, pbaHashBuffer); ExitOnFailure(hr, "Failed ScaSslExistingCertificateByName"); if (S_OK != hr) { if (!fIsUninstalling && fIsInstalling) { // if no existing cert and not on uninstall, hit the authority WcaLog(LOGMSG_STANDARD, "Adding certificate: requested, %ls", wzDistinguishedName); hr = ScaSslNewCertificate(pwzName, iStore, iStoreLocation, wzComputerName, wzDistinguishedName, wzCA, pbstrCertificate, pcbCertificate, pbaHashBuffer); ExitOnFailure(hr, "Failed ScaSslNewCertificate"); } else { // if no existing cert and uninstall hr = S_OK; } } LExit: ReleaseStr(pwzData); return hr; } HRESULT ScaInstallCertificateByContext(LPCWSTR pwzName, INT iStore, INT iStoreLocation, PCCERT_CONTEXT pCertContext) { HRESULT hr = S_OK; HCERTSTORE hCertStore = NULL; DWORD dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; CERT_BLOB blob; hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, StoreMapping(iStore)); if (hCertStore == NULL) MessageExitOnLastError(hr, msierrCERTFailedOpen, "failed to open certificate store"); blob.pbData = (BYTE*)pwzName; blob.cbData = (lstrlenW(pwzName) + 1) * sizeof(pwzName[0]); // including terminating null CertSetCertificateContextProperty( pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob); if (!CertAddCertificateContextToStore( hCertStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) { hr = E_FAIL; MessageExitOnLastError(hr, msierrCERTFailedAdd, "failed to add certificate to the store"); } LExit: if (hCertStore) { CertCloseStore(hCertStore, 0); hCertStore = NULL; } return hr; } HRESULT ScaGetCertificateByPath(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling, INT iStore, INT iStoreLocation, LPCWSTR wzSslCertificate, LPCWSTR wzPFXPassword, BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) { Assert(wzSslCertificate); HRESULT hr = S_OK; PCCERT_CONTEXT pCertContext = NULL; DWORD dwEncodingType = 0; DWORD dwContentType = 0; DWORD dwFormatType = 0; DWORD dwHashSize = CB_CERTIFICATE_HASH; HANDLE hPfxFile = INVALID_HANDLE_VALUE; CRYPT_DATA_BLOB blob; blob.pbData = NULL; blob.cbData = 0; if (wzSslCertificate && wzSslCertificate[0] != 0) { if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE, (LPVOID)wzSslCertificate, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext)) hr = fIsUninstalling ? S_FALSE : HRESULT_FROM_WIN32(::GetLastError()); // don't fail on uninstall ExitOnFailure(hr, "failed CryptQueryObject"); } else { hr = S_FALSE; ExitFunction(); } if (!pCertContext) { // this is a pfx? // make sure to exit this block of code properly for clean up blob.pbData if (dwContentType & CERT_QUERY_CONTENT_PFX) { DWORD iSize = 0, iReadSize = 0; HCERTSTORE hPfxCertStore = NULL; hPfxFile = ::CreateFileW(wzSslCertificate, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); hr = (hPfxFile != INVALID_HANDLE_VALUE) ? S_OK : E_FAIL; ExitOnFailure(hr, "failed CryptQueryObject, file handle is null"); iSize = ::GetFileSize(hPfxFile, NULL); hr = (iSize > 0) ? S_OK : E_FAIL; ExitOnFailure(hr, "failed CryptQueryObject, file size is 0"); blob.pbData = new BYTE[iSize]; blob.cbData = iSize; hr = (blob.pbData) ? S_OK : E_FAIL; ExitOnFailure(hr, "out of memory for blob"); if (::ReadFile(hPfxFile, (LPVOID)blob.pbData, iSize, &iReadSize, NULL)) { hPfxCertStore = PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, (iStoreLocation == SCA_CERTSYSTEMSTORE_CURRENTUSER) ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET); if (hPfxCertStore) { pCertContext = CertEnumCertificatesInStore(hPfxCertStore, NULL); // work only with the first certificate in pfx if (pCertContext) { hr = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) ? S_OK : E_FAIL; ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); ReleaseBSTR(*pbstrCertificate); *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded); *pcbCertificate = pCertContext->cbCertEncoded; if (fIsInstalling) { // install the certificate, cannot defer because the data required cannot be passed hr = ScaInstallCertificateByContext(pwzName, iStore, iStoreLocation, pCertContext); } } else hr = E_FAIL; } else hr = E_FAIL; } else hr = E_FAIL; } else { ExitOnFailure(hr = E_FAIL, "failed CryptQueryObject, unknown data"); } } else { // return cert and its hash hr = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) ? S_OK : E_FAIL; ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); ReleaseBSTR(*pbstrCertificate); *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded); *pcbCertificate = pCertContext->cbCertEncoded; if (fIsInstalling) { // install the certificate, cannot defer because the data required cannot be passed hr = ScaInstallCertificateByContext(pwzName, iStore, iStoreLocation, pCertContext); } } LExit: if (pCertContext) { CertFreeCertificateContext(pCertContext); pCertContext = NULL; } if (hPfxFile != INVALID_HANDLE_VALUE) { CloseHandle(hPfxFile); hPfxFile = INVALID_HANDLE_VALUE; } if (blob.pbData) { delete [] blob.pbData; blob.pbData = NULL; blob.cbData = 0; } return hr; } HRESULT ScaInstallCertificateByBinaryData(BOOL fAddCert, INT iStore, INT iStoreLocation, LPCWSTR wzName, BYTE* pwzData, DWORD cchData, LPCWSTR wzPFXPassword) { Assert(wzName); Assert(pwzData); Assert(cchData); HRESULT hr = S_OK; HCERTSTORE hCertStore = NULL, hPfxCertStore = NULL; PCCERT_CONTEXT pCertCtx = NULL, pCertCtxExisting = NULL; DWORD dwFlags, dwEncodingType, dwContentType, dwFormatType; CERT_BLOB blob; LPCWSTR wzStore = StoreMapping(iStore); dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, wzStore); MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store"); blob.pbData = pwzData; blob.cbData = cchData; if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertCtx)) ExitOnLastError(hr, "failed to parse the certificate blob"); ExitOnNull(pCertCtx, hr, E_UNEXPECTED, "failed to parse the certificate blob"); blob.pbData = (BYTE*)wzName; blob.cbData = (lstrlenW(wzName) + 1) * sizeof(wzName[0]); // including terminating null CertSetCertificateContextProperty( pCertCtx, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob); if (fAddCert) { // Add WcaLog(LOGMSG_STANDARD, "Adding certificate: binary name, %ls", wzName); if (!CertAddCertificateContextToStore( hCertStore, pCertCtx, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) { hr = E_FAIL; MessageExitOnLastError(hr, msierrCERTFailedAdd, "failed to add certificate to the store"); } } else { // Delete WcaLog(LOGMSG_STANDARD, "Deleting certificate provided: binary name, %ls", wzName); pCertCtxExisting = CertFindCertificateInStore( hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_EXISTING, pCertCtx, NULL); if (pCertCtxExisting) { if (!CertDeleteCertificateFromStore(pCertCtxExisting)) { ExitOnLastError(hr, "failed to delete certificate"); } else { pCertCtxExisting = NULL; } } } LExit: if (pCertCtx) { CertFreeCertificateContext(pCertCtx); pCertCtx = NULL; } if (pCertCtxExisting) { CertFreeCertificateContext(pCertCtxExisting); pCertCtxExisting = NULL; } // order is important for store if (hCertStore) { CertCloseStore(hCertStore, 0); hCertStore = NULL; } if (hPfxCertStore) { CertCloseStore(hPfxCertStore, 0); hPfxCertStore = NULL; } return hr; } */