/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Microsoft Windows, Copyright (C) Microsoft Corporation, 2000 File: Certificates.cpp Contents: Implementation of CCertificates class for collection of ICertificate objects. Remarks: This object is not creatable by user directly. It can only be created via property/method of other CAPICOM objects. The collection container is implemented usign STL::map of STL::pair of BSTR and ICertificate.. See Chapter 9 of "BEGINNING ATL 3 COM Programming" for algorithm adopted in here. History: 11-15-99 dsie created ------------------------------------------------------------------------------*/ #include "StdAfx.h" #include "CAPICOM.h" #include "Certificates.h" #include "Common.h" #include "Convert.h" #include "CertHlpr.h" #include "MsgHlpr.h" #include "PFXHlpr.h" #include "Policy.h" #include "Settings.h" //////////////////////////////////////////////////////////////////////////////// // // Exported functions. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CreateCertificatesObject Synopsis : Create an ICertificates collection object, and load the object with certificates from the specified source. Parameter: CAPICOM_CERTIFICATES_SOURCE ccs - Source where to get the certificates. DWORD dwCurrentSafety - Current safety setting. BOOL bIndexedByThumbprint - TRUE to index by thumbprint. ICertificates2 ** ppICertificates - Pointer to pointer to ICertificates to receive the interface pointer. Remark : ------------------------------------------------------------------------------*/ HRESULT CreateCertificatesObject (CAPICOM_CERTIFICATES_SOURCE ccs, DWORD dwCurrentSafety, BOOL bIndexedByThumbprint, ICertificates2 ** ppICertificates) { HRESULT hr = S_OK; CComObject * pCCertificates = NULL; DebugTrace("Entering CreateCertificatesObject().\n"); // // Sanity check. // ATLASSERT(ppICertificates); try { // // Create the object. Note that the ref count will still be 0 // after the object is created. // if (FAILED(hr = CComObject::CreateInstance(&pCCertificates))) { DebugTrace("Error [%#x]: CComObject::CreateInstance() failed.\n", hr); goto ErrorExit; } // // Initialize object. // if (FAILED(hr = pCCertificates->Init(ccs, dwCurrentSafety, bIndexedByThumbprint))) { DebugTrace("Error [%#x]: pCCertificates->Init() failed.\n", hr); goto ErrorExit; } // // Return interface pointer to caller. // if (FAILED(hr = pCCertificates->QueryInterface(ppICertificates))) { DebugTrace("Error [%#x]: pCCertificates->QueryInterface() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } CommonExit: DebugTrace("Leaving CreateCertificatesObject().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (pCCertificates) { delete pCCertificates; } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindTimeValidCallback Synopsis : Callback for find-time-valid. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindTimeValidCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedCert, LPVOID pvCallbackData) { LONG lResult = 0; BOOL bInclude = TRUE; DebugTrace("Entering FindTimeValidCallback().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pvCallbackData); // // Skip it if not yet valid or expired. // if (0 != (lResult = ::CertVerifyTimeValidity((LPFILETIME) pvCallbackData, pCertContext->pCertInfo))) { bInclude = FALSE; DebugTrace("Info: Time is not valid (lResult = %d)\n", lResult); DebugTrace(" Time (High = %#x, Low = %#x).\n", ((LPFILETIME) pvCallbackData)->dwHighDateTime, ((LPFILETIME) pvCallbackData)->dwLowDateTime); DebugTrace(" NotBefore (High = %#x, Low = %#x).\n", pCertContext->pCertInfo->NotBefore.dwHighDateTime, pCertContext->pCertInfo->NotBefore.dwLowDateTime); DebugTrace(" NotAfter (High = %#x, Low = %#x).\n", pCertContext->pCertInfo->NotAfter.dwHighDateTime, pCertContext->pCertInfo->NotAfter.dwLowDateTime); } DebugTrace("Leaving FindTimeValidCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindNotBeforeCallback Synopsis : Callback for find-by-not-before. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindNotBeforeCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedCert, LPVOID pvCallbackData) { BOOL bInclude = TRUE; DebugTrace("Entering FindNotBeforeCallback().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pvCallbackData); // // Skip it if time valid or expired. // if (!(-1 == ::CertVerifyTimeValidity((LPFILETIME) pvCallbackData, pCertContext->pCertInfo))) { bInclude = FALSE; DebugTrace("Info: time (High = %#x, Low = %#x) is either valid or expired.\n", ((LPFILETIME) pvCallbackData)->dwHighDateTime, ((LPFILETIME) pvCallbackData)->dwLowDateTime); } DebugTrace("Leaving FindNotBeforeCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindNotAfterCallback Synopsis : Callback for find-by-not-after. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindNotAfterCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedCert, LPVOID pvCallbackData) { BOOL bInclude = TRUE; DebugTrace("Entering FindNotAfterCallback().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pvCallbackData); // // Skip it if not expired. // if (!(1 == ::CertVerifyTimeValidity((LPFILETIME) pvCallbackData, pCertContext->pCertInfo))) { bInclude = FALSE; DebugTrace("Info: time (High = %#x, Low = %#x) is not expired.\n", ((LPFILETIME) pvCallbackData)->dwHighDateTime, ((LPFILETIME) pvCallbackData)->dwLowDateTime); } DebugTrace("Leaving FindNotAfterCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindTemplateCallback Synopsis : Callback for find-by-template. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindTemplateCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedCert, LPVOID pvCallbackData) { HRESULT hr = S_OK; BOOL bInclude = FALSE; DATA_BLOB CertTypeBlob = {0, NULL}; DATA_BLOB TemplateBlob = {0, NULL}; PCERT_EXTENSION pCertType = NULL; PCERT_EXTENSION pCertTemp = NULL; DebugTrace("Entering FindTemplateCallback().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pvCallbackData); // // Skip it if we don't have either szOID_ENROLL_CERTTYPE_EXTENSION or // szOID_CERTIFICATE_TEMPLATE extension. // if (!(pCertType = ::CertFindExtension(szOID_ENROLL_CERTTYPE_EXTENSION, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension)) && !(pCertTemp = ::CertFindExtension(szOID_CERTIFICATE_TEMPLATE, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension))) { DebugTrace("Info: could not find both szOID_ENROLL_CERTTYPE_EXTENSION and szOID_CERTIFICATE_TEMPLATE.\n"); goto CommonExit; } // // Check cert type template name if found. // if (pCertType) { PCERT_NAME_VALUE pNameValue = NULL; // // Decode the extension. // if (FAILED(hr = ::DecodeObject(X509_UNICODE_ANY_STRING, pCertType->Value.pbData, pCertType->Value.cbData, &CertTypeBlob))) { DebugTrace("Info [%#x]: DecodeObject() failed.\n", hr); goto CommonExit; } pNameValue = (PCERT_NAME_VALUE) CertTypeBlob.pbData; if (0 == ::_wcsicmp((LPWSTR) pNameValue->Value.pbData, (LPWSTR) pvCallbackData)) { bInclude = TRUE; } } // // Look into cert template extension, if necessary. // if (!bInclude && pCertTemp) { PCCRYPT_OID_INFO pOidInfo = NULL; PCERT_TEMPLATE_EXT pTemplate = NULL; // // Decode the extension. // if (FAILED(hr = ::DecodeObject(szOID_CERTIFICATE_TEMPLATE, pCertTemp->Value.pbData, pCertTemp->Value.cbData, &TemplateBlob))) { DebugTrace("Info [%#x]: DecodeObject() failed.\n", hr); goto CommonExit; } pTemplate = (PCERT_TEMPLATE_EXT) TemplateBlob.pbData; // // Convert to OID if user passed in friendly name. // if (pOidInfo = ::CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, pvCallbackData, CRYPT_TEMPLATE_OID_GROUP_ID)) { if (0 == ::strcmp(pTemplate->pszObjId, pOidInfo->pszOID)) { bInclude = TRUE; } } else { CComBSTR bstrOID; if (!(bstrOID = pTemplate->pszObjId)) { DebugTrace("Info: bstrOID = pTemplate->pszObjId failed.\n", hr); goto CommonExit; } if (0 == ::wcscmp(bstrOID, (LPWSTR) pvCallbackData)) { bInclude = TRUE; } } } CommonExit: // // Free resources. // if (TemplateBlob.pbData) { ::CoTaskMemFree((LPVOID) TemplateBlob.pbData); } if (CertTypeBlob.pbData) { ::CoTaskMemFree((LPVOID) CertTypeBlob.pbData); } DebugTrace("Leaving FindTemplateCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindExtensionCallback Synopsis : Callback for find-by-extension. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindExtensionCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedCert, LPVOID pvCallbackData) { BOOL bInclude = TRUE; PCERT_EXTENSION pExtension = NULL; DebugTrace("Entering FindExtensionCallback().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pvCallbackData); // // Skip it if we can't find the specified extension. // if (!(pExtension = ::CertFindExtension((LPSTR) pvCallbackData, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension))) { bInclude = FALSE; DebugTrace("Info: extension (%s) could not be found.\n", pvCallbackData); } DebugTrace("Leaving FindExtensionCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindRootNameCallback Synopsis : Callback for find-by-rootname. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindRootNameCallback (PCCERT_CHAIN_CONTEXT pChainContext, BOOL * pfInitialSelectedChain, LPVOID pvCallbackData) { BOOL bInclude = FALSE; HCERTSTORE hCertStore = NULL; PCCERT_CONTEXT pRootContext = NULL; PCERT_SIMPLE_CHAIN pSimpleChain; DebugTrace("Entering FindRootNameCallback().\n"); // // Sanity check. // ATLASSERT(pChainContext); ATLASSERT(pvCallbackData); // // Skip if we don't have a complete chain (we don't have a root cert). // if (CERT_TRUST_IS_PARTIAL_CHAIN & pChainContext->TrustStatus.dwErrorStatus) { DebugTrace("Info: certificate has only partial chain.\n"); goto CommonExit; } // // Open a new temporary memory store. // if (!(hCertStore = ::CertOpenStore(CERT_STORE_PROV_MEMORY, CAPICOM_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL))) { DebugTrace("Info [%#x]: CertOpenStore() failed.\n", HRESULT_FROM_WIN32(::GetLastError())); goto CommonExit; } // // Copy the root cert of simple chain to memory store. // pSimpleChain = pChainContext->rgpChain[0]; if (!::CertAddCertificateContextToStore(hCertStore, pSimpleChain->rgpElement[pSimpleChain->cElement - 1]->pCertContext, CERT_STORE_ADD_ALWAYS, NULL)) { DebugTrace("Info [%#x]: CertAddCertificateContextToStore() failed.\n", HRESULT_FROM_WIN32(::GetLastError())); goto CommonExit; } // // Does it match? // if (!(pRootContext = ::CertFindCertificateInStore(hCertStore, CAPICOM_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, pvCallbackData, pRootContext))) { DebugTrace("Info [%#x]: CertFindCertificateInStore() failed.\n", HRESULT_FROM_WIN32(::GetLastError())); goto CommonExit; } // // We have a match. // bInclude = TRUE; CommonExit: // // Free resources. // if (pRootContext) { ::CertFreeCertificateContext(pRootContext); } if (hCertStore) { ::CertCloseStore(hCertStore, 0); } DebugTrace("Leaving FindRootNameCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindApplicationPolicyCallback Synopsis : Callback for find-by-application-policy. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindApplicationPolicyCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedChain, LPVOID pvCallbackData) { BOOL bInclude = FALSE; int cNumOIDs = 0; DWORD cbOIDs = 0; LPSTR * rghOIDs = NULL; DebugTrace("Entering FindApplicationPolicyCallback().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pvCallbackData); // // Get all the valid application usages. // if (!::CertGetValidUsages(1, &pCertContext, &cNumOIDs, NULL, &cbOIDs)) { DebugTrace("Info [%#x]: CertGetValidUsages() failed.\n", HRESULT_FROM_WIN32(::GetLastError())); goto CommonExit; } if (!(rghOIDs = (LPSTR *) ::CoTaskMemAlloc(cbOIDs))) { DebugTrace("Info: out of memory..\n"); goto CommonExit; } if (!::CertGetValidUsages(1, &pCertContext, &cNumOIDs, rghOIDs, &cbOIDs)) { DebugTrace("Info [%#x]: CertGetValidUsages() failed.\n", HRESULT_FROM_WIN32(::GetLastError())); goto CommonExit; } // // No EKU is consider good for all. // if (-1 == cNumOIDs) { bInclude = TRUE; } else { // // See if we can find it in the array. // while (cNumOIDs--) { if (0 == ::strcmp((LPSTR) pvCallbackData, rghOIDs[cNumOIDs])) { bInclude = TRUE; break; } } } CommonExit: // // Free resources. // if (rghOIDs) { ::CoTaskMemFree((LPVOID) rghOIDs); } DebugTrace("Leaving FindApplicationPolicyCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindCertificatePolicyCallback Synopsis : Callback for find-by-certificate-policy. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindCertificatePolicyCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedChain, LPVOID pvCallbackData) { HRESULT hr = S_OK; BOOL bInclude = FALSE; DWORD dwIndex = 0; DATA_BLOB DataBlob = {0, NULL}; PCERT_EXTENSION pExtension = NULL; PCERT_POLICIES_INFO pInfo = NULL; DebugTrace("Entering FindCertificatePolicyCallback().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pvCallbackData); // // Find the szOID_CERT_POLICIES extension. // if (!(pExtension = ::CertFindExtension(szOID_CERT_POLICIES, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension))) { DebugTrace("Info [%#x]: CertFindExtension() failed.\n", HRESULT_FROM_WIN32(::GetLastError())); goto CommonExit; } // // Decode the extension. // if (FAILED(hr = ::DecodeObject(szOID_CERT_POLICIES, pExtension->Value.pbData, pExtension->Value.cbData, &DataBlob))) { DebugTrace("Info [%#x]: DecodeObject() failed.\n", hr); goto CommonExit; } pInfo = (PCERT_POLICIES_INFO) DataBlob.pbData; dwIndex = pInfo->cPolicyInfo; // // Try to find a match. // while (dwIndex--) { if (0 == ::strcmp(pInfo->rgPolicyInfo[dwIndex].pszPolicyIdentifier, (LPSTR) pvCallbackData)) { bInclude = TRUE; break; } } CommonExit: // // Free resources. // if (DataBlob.pbData) { ::CoTaskMemFree((LPVOID) DataBlob.pbData); } DebugTrace("Leaving FindCertificatePolicyCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindKeyUsageCallback Synopsis : Callback for find-by-key-usage. Parameter: See CryptUI.h. Remark : ------------------------------------------------------------------------------*/ static BOOL WINAPI FindKeyUsageCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedChain, LPVOID pvCallbackData) { HRESULT hr = S_OK; BOOL bInclude = FALSE; DWORD dwActualUsages = 0; DWORD dwCheckUsages = 0; DebugTrace("Entering FindKeyUsageCallback().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pvCallbackData); // // Check the key usage. // if (!::CertGetIntendedKeyUsage(CAPICOM_ASN_ENCODING, pCertContext->pCertInfo, (BYTE *) &dwActualUsages, sizeof(dwActualUsages))) { // // Could be extension not present or an error. // if (FAILED(hr = HRESULT_FROM_WIN32(::GetLastError()))) { DebugTrace("Error [%#x]: CertGetIntendedKeyUsage() failed.\n", hr); goto CommonExit; } } // // Check the bit mask. // dwCheckUsages = *(LPDWORD) pvCallbackData; if ((dwActualUsages & dwCheckUsages) == dwCheckUsages) { bInclude = TRUE; } CommonExit: DebugTrace("Leaving FindKeyUsageCallback().\n"); return bInclude; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindByChain Synopsis : Find certificate(s) in store based on chain's criteria and filter with a callback. Parameter: HCERTSTORE hCertStore - Store to find certificate(s). DWORD dwFindType - Find type. LPCVOID pvFindPara - Content to be found. VARIANT_BOOL bFindValidOnly - True to find valid certs only. PFNCHAINFILTERPROC pfnFilterCallback - Callback filter. LPVOID pvCallbackData - Callback data. DWORD dwCurrentSafety - Current safety setting. ICertificates2 ** ppICertificates - Pointer to pointer ICertificates2 object. Remark : ------------------------------------------------------------------------------*/ static HRESULT FindByChain (HCERTSTORE hCertStore, DWORD dwFindType, LPCVOID pvFindPara, VARIANT_BOOL bFindValidOnly, PFNCHAINFILTERPROC pfnFilterCallback, LPVOID pvCallbackData, DWORD dwCurrentSafety, ICertificates2 * pICertificates) { HRESULT hr = S_OK; DWORD dwWin32Error = 0; PCCERT_CHAIN_CONTEXT pChainContext = NULL; DebugTrace("Entering FindByChain().\n"); // // Sanity check. // ATLASSERT(hCertStore); ATLASSERT(pICertificates); // // Find all chains in the store, matching the find criteria. // while (pChainContext = ::CertFindChainInStore(hCertStore, CAPICOM_ASN_ENCODING, 0, dwFindType, pvFindPara, pChainContext)) { CComPtr pICertificate = NULL; // // Apply filter if available. // if (pfnFilterCallback && !pfnFilterCallback(pChainContext, NULL, pvCallbackData)) { continue; } // // Skip it if check is required and the cert is not valid. // if (bFindValidOnly && (CERT_TRUST_NO_ERROR != pChainContext->TrustStatus.dwErrorStatus)) { continue; } // // Sanity check. // ATLASSERT(pChainContext->cChain); ATLASSERT(pChainContext->rgpChain); ATLASSERT(pChainContext->rgpChain[0]); ATLASSERT(pChainContext->rgpChain[0]->cElement); ATLASSERT(pChainContext->rgpChain[0]->rgpElement); ATLASSERT(pChainContext->rgpChain[0]->rgpElement[0]); ATLASSERT(pChainContext->rgpChain[0]->rgpElement[0]->pCertContext); // // Create a ICertificate object for the found certificate. // if (FAILED (hr = ::CreateCertificateObject( pChainContext->rgpChain[0]->rgpElement[0]->pCertContext, dwCurrentSafety, &pICertificate))) { DebugTrace("Error [%#x]: CreateCertificateObject() failed.\n", hr); goto ErrorExit; } // // Add to collection. // if (FAILED(hr = pICertificates->Add(pICertificate))) { DebugTrace("Error [%#x]: pICertificates->Add() failed.\n", hr); goto ErrorExit; } } // // Above loop can exit with normal or error condition. // if (CRYPT_E_NOT_FOUND != (dwWin32Error = ::GetLastError())) { hr = HRESULT_FROM_WIN32(dwWin32Error); DebugTrace("Error [%#x]: CertFindChainInStore() failed.\n", hr); goto ErrorExit; } CommonExit: // // Free resources. // if (pChainContext) { ::CertFreeCertificateChain(pChainContext); } DebugTrace("Leaving FindByChain().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FindByCert Synopsis : Find certificate(s) in store and filter with a callback. Parameter: HCERTSTORE hCertStore - Store to find certificate(s). DWORD dwFindType - Find type. LPCVOID pvFindPara - Content to be found. VARIANT_BOOL bFindValidOnly - True to find valid certs only. PFNCERTFILTERPROC pfnFilterCallback - Callback filter. LPVOID pvCallbackData - Callback data. DWORD dwCurrentSafety - Current safety setting. ICertificates2 ** ppICertificates - Pointer to pointer ICertificates2 object. Remark : ------------------------------------------------------------------------------*/ static HRESULT FindByCert (HCERTSTORE hCertStore, DWORD dwFindType, LPCVOID pvFindPara, VARIANT_BOOL bFindValidOnly, PFNCFILTERPROC pfnFilterCallback, LPVOID pvCallbackData, DWORD dwCurrentSafety, ICertificates2 * pICertificates) { HRESULT hr = S_OK; DWORD dwWin32Error = 0; PCCERT_CONTEXT pCertContext = NULL; DebugTrace("Entering FindByCert().\n"); // // Sanity check. // ATLASSERT(hCertStore); ATLASSERT(pICertificates); // // Find all certificates in the store, matching the find criteria. // while (pCertContext = ::CertFindCertificateInStore(hCertStore, CAPICOM_ASN_ENCODING, 0, dwFindType, pvFindPara, pCertContext)) { CComPtr pICertificate = NULL; // // Apply filter if available. // if (pfnFilterCallback && !pfnFilterCallback(pCertContext, NULL, pvCallbackData)) { continue; } // // Skip it if check is required and the cert is not valid. // if (bFindValidOnly) { if (FAILED(::VerifyCertificate(pCertContext, NULL, CERT_CHAIN_POLICY_BASE))) { continue; } } // // Create a ICertificate2 object for the found certificate. // if (FAILED (hr = ::CreateCertificateObject(pCertContext, dwCurrentSafety, &pICertificate))) { DebugTrace("Error [%#x]: CreateCertificateObject() failed.\n", hr); goto ErrorExit; } // // Add to collection. // if (FAILED(hr = pICertificates->Add(pICertificate))) { DebugTrace("Error [%#x]: pICertificates->Add() failed.\n", hr); goto ErrorExit; } } // // Above loop can exit with normal or error condition. // if (CRYPT_E_NOT_FOUND != (dwWin32Error = ::GetLastError())) { hr = HRESULT_FROM_WIN32(dwWin32Error); DebugTrace("Error [%#x]: CertFindCertificateInStore() failed.\n", hr); goto ErrorExit; } CommonExit: // // Free resources. // if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } DebugTrace("Leaving FindByCert().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); goto CommonExit; } //////////////////////////////////////////////////////////////////////////////// // // CCertificates // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::Find Synopsis : Find certificates in the collection that match the find criteria. Parameter: CAPICOM_CERTIFICATE_FIND_TYPE FindType - Find type (see CAPICOM.H for all possible values.) VARIANT varCriteria - Data type depends on FindType. VARIANT_BOOL bFindValidOnly - True to find valid certs only. ICertificates2 ** pVal - Pointer to pointer to ICertificates to receive the found certificate collection. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::Find (CAPICOM_CERTIFICATE_FIND_TYPE FindType, VARIANT varCriteria, VARIANT_BOOL bFindValidOnly, ICertificates2 ** pVal) { USES_CONVERSION; HRESULT hr = S_OK; VARIANT * pvarCriteria = NULL; BOOL bFindByChain = FALSE; HCERTSTORE hCertStore = NULL; DWORD dwFindType = CERT_FIND_ANY; LPVOID pvFindPara = NULL; LPVOID pvCallbackData = NULL; LPSTR pszOid = NULL; SYSTEMTIME st = {0}; FILETIME ftLocal = {0}; FILETIME ftUTC = {0}; CRYPT_HASH_BLOB HashBlob = {0, NULL}; PCCRYPT_OID_INFO pOidInfo = NULL; PFNCFILTERPROC pfnCertCallback = NULL; PFNCHAINFILTERPROC pfnChainCallback = NULL; CComPtr pICertificates = NULL; CAPICOM_CERTIFICATES_SOURCE ccs = {CAPICOM_CERTIFICATES_LOAD_FROM_STORE, 0}; CERT_CHAIN_FIND_BY_ISSUER_PARA ChainFindPara; DebugTrace("Entering CCertificates::Find().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Make sure parameters are valid. // if (NULL == pVal) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: pVal is NULL.\n", hr); goto ErrorExit; } // // Skip over BYREF. // for (pvarCriteria = &varCriteria; pvarCriteria && ((VT_VARIANT | VT_BYREF) == V_VT(pvarCriteria)); pvarCriteria = V_VARIANTREF(pvarCriteria)); // // Open a new memory store. // if (NULL == (hCertStore = ::CertOpenStore(CERT_STORE_PROV_MEMORY, CAPICOM_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG | CERT_STORE_ENUM_ARCHIVED_FLAG, NULL))) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertOpenStore() failed.\n", hr); goto ErrorExit; } // // Create a new collection. // ccs.hCertStore = hCertStore; if (FAILED(hr = ::CreateCertificatesObject(ccs, m_dwCurrentSafety, TRUE, &pICertificates))) { DebugTrace("Error [%#x]: CreateCertificatesObject() failed.\n", hr); goto ErrorExit; } // // Export current collection to the new memory store so that we can // use it with CAPI's find APIs. // if (FAILED(hr = _ExportToStore(hCertStore))) { DebugTrace("Error [%#x]: CCertificates::ExportToStore() failed.\n", hr); goto ErrorExit; } // // Setup find parameters. // switch (FindType) { // // Find by SHA1 hash. // case CAPICOM_CERTIFICATE_FIND_SHA1_HASH: { // // Make sure data type is OK. // if (VT_BSTR != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_BSTR))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_BSTR); goto ErrorExit; } } // // Convert hash to binary. // if (FAILED(hr = ::StringToBinary(pvarCriteria->bstrVal, ::SysStringLen(pvarCriteria->bstrVal), CRYPT_STRING_HEX, (PBYTE *) &HashBlob.pbData, &HashBlob.cbData))) { DebugTrace("Error [%#x]: StringToBinary() failed.\n", hr); goto ErrorExit; } dwFindType = CERT_FIND_HASH; pvFindPara = (LPVOID) &HashBlob; break; } // // Find by subject name substring-in-string. // case CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME: { // // Make sure data type is OK. // if (VT_BSTR != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_BSTR))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_BSTR); goto ErrorExit; } } dwFindType = CERT_FIND_SUBJECT_STR; pvFindPara = (LPVOID) pvarCriteria->bstrVal; break; } // // Find by issuer name substring-in-string. // case CAPICOM_CERTIFICATE_FIND_ISSUER_NAME: { // // Make sure data type is OK. // if (VT_BSTR != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_BSTR))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_BSTR); goto ErrorExit; } } dwFindType = CERT_FIND_ISSUER_STR; pvFindPara = (LPVOID) pvarCriteria->bstrVal; break; } // // Find by issuer name of root cert subtring-in-string. // case CAPICOM_CERTIFICATE_FIND_ROOT_NAME: { // // Make sure data type is OK. // if (VT_BSTR != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_BSTR))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_BSTR); goto ErrorExit; } } ::ZeroMemory(&ChainFindPara, sizeof(ChainFindPara)); ChainFindPara.cbSize = sizeof(ChainFindPara); dwFindType = CERT_CHAIN_FIND_BY_ISSUER; pvFindPara = (LPVOID) &ChainFindPara; pfnChainCallback = FindRootNameCallback; pvCallbackData = (LPVOID) pvarCriteria->bstrVal; bFindByChain = TRUE; break; } // // Find by template name or OID. // case CAPICOM_CERTIFICATE_FIND_TEMPLATE_NAME: { // // Make sure data type is OK. // if (VT_BSTR != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_BSTR))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_BSTR); goto ErrorExit; } } pfnCertCallback = FindTemplateCallback; pvCallbackData = (LPVOID) pvarCriteria->bstrVal; break; } // // Find by extension. // case CAPICOM_CERTIFICATE_FIND_EXTENSION: { // // Make sure data type is OK. // if (VT_BSTR != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_BSTR))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_BSTR); goto ErrorExit; } } // // Convert to OID if user passed in friendly name. // if (pOidInfo = ::CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, (LPWSTR) pvarCriteria->bstrVal, CRYPT_EXT_OR_ATTR_OID_GROUP_ID)) { pszOid = (LPSTR) pOidInfo->pszOID; } else { // // Convert to ASCII. // if (!(pszOid = W2A(pvarCriteria->bstrVal))) { hr = E_OUTOFMEMORY; DebugTrace("Error [%%#x]: pszOid = W2A(pvarCriteria->bstrVal) failed.\n", hr); goto ErrorExit; } } pfnCertCallback = FindExtensionCallback; pvCallbackData = (LPVOID) pszOid; break; } // // Find by property ID. // case CAPICOM_CERTIFICATE_FIND_EXTENDED_PROPERTY: { // // Make sure data type is OK. // if (VT_I4 != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_I4))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_I4); goto ErrorExit; } } dwFindType = CERT_FIND_PROPERTY; pvFindPara = (LPVOID) &pvarCriteria->lVal; break; } // // Find by application policy (EKU). // case CAPICOM_CERTIFICATE_FIND_APPLICATION_POLICY: { // // Make sure data type is OK. // if (VT_BSTR != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_BSTR))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_BSTR); goto ErrorExit; } } // // Try to convert to OID if user passed in friendly name. // if (pOidInfo = ::CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, (LPWSTR) pvarCriteria->bstrVal, CRYPT_ENHKEY_USAGE_OID_GROUP_ID)) { pszOid = (LPSTR) pOidInfo->pszOID; } else { // // Convert to ASCII. // if (!(pszOid = W2A(pvarCriteria->bstrVal))) { hr = E_OUTOFMEMORY; DebugTrace("Error [%#X]: pszOid = W2A(pvarCriteria->bstrVal) failed.\n", hr); goto ErrorExit; } } pfnCertCallback = FindApplicationPolicyCallback; pvCallbackData = (LPVOID) pszOid; break; } // // Find by certificate policy. // case CAPICOM_CERTIFICATE_FIND_CERTIFICATE_POLICY: { // // Make sure data type is OK. // if (VT_BSTR != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_BSTR))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_BSTR); goto ErrorExit; } } // // Convert to OID if user passed in friendly name. // if (pOidInfo = ::CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, (LPWSTR) pvarCriteria->bstrVal, CRYPT_POLICY_OID_GROUP_ID)) { pszOid = (LPSTR) pOidInfo->pszOID; } else { // // Convert to ASCII. // if (!(pszOid = W2A(pvarCriteria->bstrVal))) { hr = E_OUTOFMEMORY; DebugTrace("Error [%#x]: pszOid = W2A(pvarCriteria->bstrVal) failed.\n", hr); goto ErrorExit; } } pfnCertCallback = FindCertificatePolicyCallback; pvCallbackData = (LPVOID) pszOid; break; } // // Find by time valid. // case CAPICOM_CERTIFICATE_FIND_TIME_VALID: { // // !!! Warning, falling thru. !!! // } // // Find by notBefore time validity. // case CAPICOM_CERTIFICATE_FIND_TIME_NOT_YET_VALID: { // // !!! Warning, falling thru. !!! // } // Find by notAfter time validity. // case CAPICOM_CERTIFICATE_FIND_TIME_EXPIRED: { // // Make sure data type is OK. // if (VT_DATE != pvarCriteria->vt) { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_DATE))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_DATE); goto ErrorExit; } } // // Convert to SYSTEMTIME format. // if (0 == pvarCriteria->date) { ::GetLocalTime(&st); } else if (!::VariantTimeToSystemTime(pvarCriteria->date, &st)) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: VariantTimeToSystemTime() failed.\n", hr); goto ErrorExit; } // // Convert to FILETIME format. // if (!::SystemTimeToFileTime(&st, &ftLocal)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: SystemTimeToFileTime() failed.\n", hr); goto ErrorExit; } // // Convert to UTC FILETIME. // if (!::LocalFileTimeToFileTime(&ftLocal, &ftUTC)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: LocalFileTimeToFileTime() failed.\n", hr); goto ErrorExit; } if (CAPICOM_CERTIFICATE_FIND_TIME_VALID == FindType) { pfnCertCallback = FindTimeValidCallback; } else if (CAPICOM_CERTIFICATE_FIND_TIME_NOT_YET_VALID == FindType) { pfnCertCallback = FindNotBeforeCallback; } else { pfnCertCallback = FindNotAfterCallback; } pvCallbackData = (LPVOID) &ftUTC; break; } // // Find by key usage. // case CAPICOM_CERTIFICATE_FIND_KEY_USAGE: { // // By Key Usage bit flag? // if (VT_I4 != pvarCriteria->vt) { // // By key usage friendly's name? // if (VT_BSTR == pvarCriteria->vt) { typedef struct _KeyUsagesStruct { LPWSTR pwszKeyUsage; DWORD dwKeyUsageBit; } KEY_USAGE_STRUCT; KEY_USAGE_STRUCT KeyUsages[] = { {L"DigitalSignature", CERT_DIGITAL_SIGNATURE_KEY_USAGE}, {L"NonRepudiation", CERT_NON_REPUDIATION_KEY_USAGE}, {L"KeyEncipherment", CERT_KEY_ENCIPHERMENT_KEY_USAGE}, {L"DataEncipherment", CERT_DATA_ENCIPHERMENT_KEY_USAGE}, {L"KeyAgreement", CERT_KEY_AGREEMENT_KEY_USAGE}, {L"KeyCertSign", CERT_KEY_CERT_SIGN_KEY_USAGE}, {L"CRLSign", CERT_CRL_SIGN_KEY_USAGE}, {L"EncipherOnly", CERT_ENCIPHER_ONLY_KEY_USAGE}, {L"DecipherOnly", CERT_DECIPHER_ONLY_KEY_USAGE} }; // // Find the name. // for (DWORD i = 0; i < ARRAYSIZE(KeyUsages); i++) { if (0 == _wcsicmp(KeyUsages[i].pwszKeyUsage, (LPWSTR) pvarCriteria->bstrVal)) { break; } } if (i == ARRAYSIZE(KeyUsages)) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Unknown key usage (%ls).\n", hr, (LPWSTR) pvarCriteria->bstrVal); goto ErrorExit; } // // Convert to bit flag. // ::VariantClear(pvarCriteria); pvarCriteria->vt = VT_I4; pvarCriteria->lVal = KeyUsages[i].dwKeyUsageBit; } else { if (FAILED(hr = ::VariantChangeType(pvarCriteria, pvarCriteria, 0, VT_I4))) { DebugTrace("Error [%#x]: invalid data type %d, expect %d.\n", hr, pvarCriteria->vt, VT_I4); goto ErrorExit; } } } pfnCertCallback = FindKeyUsageCallback; pvCallbackData = (LPVOID) &pvarCriteria->lVal; break; } default: { hr = CAPICOM_E_FIND_INVALID_TYPE; DebugTrace("Error [%#x]: invalid CAPICOM_CERTIFICATE_FIND_TYPE (%d).\n", hr, FindType); goto ErrorExit; } } // // Now find the certs. // if (bFindByChain) { if (FAILED(hr = ::FindByChain(hCertStore, dwFindType, pvFindPara, bFindValidOnly, pfnChainCallback, pvCallbackData, m_dwCurrentSafety, pICertificates))) { DebugTrace("Error [%#x]: FindByChain() failed.\n", hr); goto ErrorExit; } } else { if (FAILED(hr = ::FindByCert(hCertStore, dwFindType, pvFindPara, bFindValidOnly, pfnCertCallback, pvCallbackData, m_dwCurrentSafety, pICertificates))) { DebugTrace("Error [%#x]: FindByCert() failed.\n", hr); goto ErrorExit; } } // // Return collection to caller. // if (FAILED(hr = pICertificates->QueryInterface(pVal))) { DebugTrace("Unexpected error [%#x]: pICertificates->QueryInterface() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free reources. // if (HashBlob.pbData) { ::CoTaskMemFree((LPVOID) HashBlob.pbData); } if (hCertStore) { ::CertCloseStore(hCertStore, 0); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CCertificates::Find().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::Select Synopsis : Display the certificate selection dialog box. Parameter: BSTR Title - Dialog box title. BSTR DisplayString - Display string. VARIANT_BOOL bMultiSelect - True for multi-select. ICertificates2 ** pVal - Pointer to pointer to ICertificates to receive the select certificate collection. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::Select (BSTR Title, BSTR DisplayString, VARIANT_BOOL bMultiSelect, ICertificates2 ** pVal) { HRESULT hr = S_OK; HCERTSTORE hSrcStore = NULL; HCERTSTORE hDstStore = NULL; PCCERT_CONTEXT pCertContext = NULL; CComPtr pICertificates = NULL; CAPICOM_CERTIFICATES_SOURCE ccs = {0, NULL}; DebugTrace("Entering CCertificates::Select().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Make sure parameters are valid. // if (NULL == pVal) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: pVal is NULL.\n", hr); goto ErrorExit; } // // Make sure we are allowed to pop UI. // if (!PromptForCertificateEnabled()) { hr = CAPICOM_E_UI_DISABLED; DebugTrace("Error [%#x]: UI is disabled.\n", hr); goto ErrorExit; } // // Work aroung MIDL default BSTR problem. // if (0 == ::SysStringLen(Title)) { Title = NULL; } if (0 == ::SysStringLen(DisplayString)) { DisplayString = NULL; } // // Open a new memory source store. // if (NULL == (hSrcStore = ::CertOpenStore(CERT_STORE_PROV_MEMORY, CAPICOM_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL))) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertOpenStore() failed.\n", hr); goto ErrorExit; } // // Export collections to the new memory source store. // if (FAILED(hr = _ExportToStore(hSrcStore))) { DebugTrace("Error [%#x]: CCertificates::_ExportToStore() failed.\n", hr); goto ErrorExit; } // // Open a new memory destination store for multi-select. // if (bMultiSelect) { if (!(hDstStore = ::CertOpenStore(CERT_STORE_PROV_MEMORY, CAPICOM_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL))) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertOpenStore() failed.\n", hr); goto ErrorExit; } } // // Display the cert selection dialog box. // if (FAILED(hr = ::SelectCertificateContext(hSrcStore, Title, DisplayString, (BOOL) bMultiSelect, NULL, hDstStore, &pCertContext))) { DebugTrace("Error [%#x]: SelectCertificateContext() failed.\n", hr); goto ErrorExit; } // // Create the collection object. // if (bMultiSelect) { // // Create a new collection from the destination store. // ccs.dwSource = CAPICOM_CERTIFICATES_LOAD_FROM_STORE; ccs.hCertStore = hDstStore; } else { // // Create a new collection from the cert context. // ccs.dwSource = CAPICOM_CERTIFICATES_LOAD_FROM_CERT; ccs.pCertContext = pCertContext; } if (FAILED(hr = ::CreateCertificatesObject(ccs, m_dwCurrentSafety, TRUE, &pICertificates))) { DebugTrace("Error [%#x]: CreateCertificatesObject() failed.\n", hr); goto ErrorExit; } // // Return collection to caller. // if (FAILED(hr = pICertificates->QueryInterface(pVal))) { DebugTrace("Unexpected error [%#x]: pICertificates->QueryInterface() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free reources. // if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } if (hDstStore) { ::CertCloseStore(hDstStore, 0); } if (hSrcStore) { ::CertCloseStore(hSrcStore, 0); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CCertificates::Select().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::Add Synopsis : Add a Certificate2 to the collection. Parameter: ICertificate2 * pVal - Certificate2 to be added. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::Add (ICertificate2 * pVal) { HRESULT hr = S_OK; char szIndex[33]; CComBSTR bstrIndex; DebugTrace("Entering CCertificates::Add().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Make sure parameters are valid. // if (NULL == pVal) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: pVal is NULL.\n", hr); goto ErrorExit; } #if (0) CComPtr pICertificate = NULL; // // Make sure we have a valid Certificate. // if (FAILED(hr = pVal->QueryInterface(__uuidof(ICertificate2), (void **) &pICertificate.p))) { hr = E_NOINTERFACE; DebugTrace("Error [%#x]: pVal is not an Certificate object.\n", hr); goto ErrorExit; } #endif DebugTrace("Info: m_dwNextIndex = %#x, m_coll.max_size() = %#x.\n", m_dwNextIndex, m_coll.max_size()); // // If index by number, and the index exceeds our max, then we will // force it to be indexed by thumbprint. // if ((m_bIndexedByThumbprint) || ((m_dwNextIndex + 1) > m_coll.max_size())) { if (FAILED(hr = pVal->get_Thumbprint(&bstrIndex))) { DebugTrace("Error [%#x]: pVal->get_Thumbprint() failed.\n", hr); goto ErrorExit; } m_bIndexedByThumbprint = TRUE; } else { // // BSTR index of numeric value. // wsprintfA(szIndex, "%#08x", ++m_dwNextIndex); if (!(bstrIndex = szIndex)) { hr = E_OUTOFMEMORY; DebugTrace("Error [%#x]: bstrIndex = szIndex failed.\n", hr); goto ErrorExit; } } // // Now add object to collection map. // // Note that the overloaded = operator for CComPtr will // automatically AddRef to the object. Also, when the CComPtr // is deleted (happens when the Remove or map destructor is called), // the CComPtr destructor will automatically Release the object. // m_coll[bstrIndex] = pVal; } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CCertificates::Add().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::Remove Synopsis : Remove a Certificate2 from the collection. Parameter: VARIANT Index - Can be numeric index (1-based), SHA1 string, or Certificate object. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::Remove (VARIANT Index) { HRESULT hr = S_OK; VARIANT * pvarIndex = NULL; CComBSTR bstrIndex; CertificateMap::iterator iter; CComPtr pICertificate; DebugTrace("Entering CCertificates::Remove().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Skip over BYREF. // for (pvarIndex = &Index; pvarIndex && ((VT_VARIANT | VT_BYREF) == V_VT(pvarIndex)); pvarIndex = V_VARIANTREF(pvarIndex)); // // Which form of index? // switch (pvarIndex->vt) { case VT_DISPATCH: { // // Get the thumbprint; // if (FAILED(hr = pvarIndex->pdispVal->QueryInterface(IID_ICertificate2, (void **) &pICertificate))) { DebugTrace("Error [%#x]: pvarIndex->pdispVal->QueryInterface() failed.\n", hr); goto ErrorExit; } if (FAILED(hr = pICertificate->get_Thumbprint(&bstrIndex))) { DebugTrace("Error [%#x]: pICertificate->get_Thumbprint() failed.\n", hr); goto ErrorExit; } // // !!! WARNING. Falling thru !!! // } case VT_BSTR: { // // Because we could have felt thru, so need to check again. // if (VT_BSTR == pvarIndex->vt) { bstrIndex = pvarIndex->bstrVal; } // // Find the cert matching this thumbprint. // for (iter = m_coll.begin(); iter != m_coll.end(); iter++) { CComBSTR bstrThumbprint; // // Point to the object. // pICertificate = (*iter).second; // // Get the thumbprint; // if (FAILED(hr = pICertificate->get_Thumbprint(&bstrThumbprint))) { DebugTrace("Error [%#x]: pICertificate->get_Thumbprint() failed.\n", hr); goto ErrorExit; } // // Same thumbprint? // if (0 == ::_wcsicmp(bstrThumbprint, bstrIndex)) { break; } } // // Did we find in the map? // if (iter == m_coll.end()) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Requested certificate (sha1 = %ls) is not found in the collection.\n", hr, bstrIndex); goto ErrorExit; } // // Now remove from map. // m_coll.erase(iter); break; } default: { DWORD dwIndex; // // Assume numeric index. // if (VT_I4 != pvarIndex->vt && FAILED(hr = ::VariantChangeType(pvarIndex, pvarIndex, 0, VT_I4))) { DebugTrace("Error [%#x]: VariantChangeType() failed.\n", hr); goto ErrorExit; } // // Make sure index is valid. // dwIndex = (DWORD) pvarIndex->lVal; if (dwIndex < 1 || dwIndex > m_coll.size()) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Index %d is out of range.\n", hr, dwIndex); goto ErrorExit; } // // Find object in map. // dwIndex--; iter = m_coll.begin(); while (iter != m_coll.end() && dwIndex > 0) { iter++; dwIndex--; } // // Did we find in the map? // if (iter == m_coll.end()) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Requested certificate (Index = %d) is not found in the collection.\n", hr, dwIndex); goto ErrorExit; } // // Now remove from map. // m_coll.erase(iter); break; } } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CCertificates::Remove().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::Clear Synopsis : Remove all Certificate2 from the collection. Parameter: None. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::Clear (void) { HRESULT hr = S_OK; DebugTrace("Entering CCertificates::Clear().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Clear it. // m_coll.clear(); // // Sanity check. // ATLASSERT(0 == m_coll.size()); } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CCertificates::Clear().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::Save Synopsis : Method to save certificate collection to a file. Parameter: BSTR FileName - File name. BSTR Password - Password (required for PFX file.). CAPICOM_CERTIFICATES_SAVE_AS_TYPE SaveAs - SaveAs type. CAPICOM_EXPORT_FLAG ExportFlag - Export flags. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::Save (BSTR FileName, BSTR Password, CAPICOM_CERTIFICATES_SAVE_AS_TYPE SaveAs, CAPICOM_EXPORT_FLAG ExportFlag) { HRESULT hr = S_OK; HCERTSTORE hCertStore = NULL; DebugTrace("Entering CCertificates::Save().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Not allowed if called from WEB script. // if (m_dwCurrentSafety) { hr = CAPICOM_E_NOT_ALLOWED; DebugTrace("Error [%#x]: Saving cert file from WEB script is not allowed.\n", hr); goto ErrorExit; } // // Check parameters. // if (0 == ::SysStringLen(FileName)) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Parameter FileName is NULL or empty.\n", hr); goto ErrorExit; } // // Work around MIDL problem. // if (0 == ::SysStringLen(Password)) { Password = NULL; } // // Open a new memory store. // if (NULL == (hCertStore = ::CertOpenStore(CERT_STORE_PROV_MEMORY, CAPICOM_ASN_ENCODING, NULL, CERT_STORE_CREATE_NEW_FLAG | CERT_STORE_ENUM_ARCHIVED_FLAG, NULL))) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertOpenStore() failed.\n", hr); goto ErrorExit; } // // Export current collection to the new memory store so that we can // use it with CAPI's store save APIs. // if (FAILED(hr = _ExportToStore(hCertStore))) { DebugTrace("Error [%#x]: CCertificates::ExportToStore() failed.\n", hr); goto ErrorExit; } // // Check file type. // switch (SaveAs) { case CAPICOM_CERTIFICATES_SAVE_AS_PFX: { // // Save as PFX file. // if (FAILED(hr = ::PFXSaveStore(hCertStore, FileName, Password, EXPORT_PRIVATE_KEYS | (ExportFlag & CAPICOM_EXPORT_IGNORE_PRIVATE_KEY_NOT_EXPORTABLE_ERROR ? 0 : REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)))) { DebugTrace("Error [%#x]: PFXSaveStore() failed.\n", hr); goto ErrorExit; } break; } case CAPICOM_CERTIFICATES_SAVE_AS_SERIALIZED: { // // Save as serialized store file. // if (!::CertSaveStore(hCertStore, 0, CERT_STORE_SAVE_AS_STORE, CERT_STORE_SAVE_TO_FILENAME_W, (void *) FileName, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertSaveStore() failed.\n", hr); goto ErrorExit; } break; } case CAPICOM_CERTIFICATES_SAVE_AS_PKCS7: { // // Save as PKCS 7 file. // if (!::CertSaveStore(hCertStore, CAPICOM_ASN_ENCODING, CERT_STORE_SAVE_AS_PKCS7, CERT_STORE_SAVE_TO_FILENAME_W, (void *) FileName, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertSaveStore() failed.\n", hr); goto ErrorExit; } break; } default: { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Unknown save as type (%#x).\n", hr, SaveAs); goto ErrorExit; } } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resources. // if (hCertStore) { ::CertCloseStore(hCertStore, 0); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CCertificates::Save().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } //////////////////////////////////////////////////////////////////////////////// // // Non COM functions. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::AddContext Synopsis : Add a cert to the collection. Parameter: PCCERT_CONTEXT pCertContext - Cert to be added. Remark : This method is not part of the COM interface (it is a normal C++ member function). We need it to initialize the object created internally by us. Since it is only a normal C++ member function, this function can only be called from a C++ class pointer, not an interface pointer. ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::AddContext (PCCERT_CONTEXT pCertContext) { HRESULT hr = S_OK; CComPtr pICertificate = NULL; DebugTrace("Entering CCertificates::AddContext().\n"); // // Sanity check. // ATLASSERT(pCertContext); try { // // Create the ICertificate object from CERT_CONTEXT. // if (FAILED(hr = ::CreateCertificateObject(pCertContext, m_dwCurrentSafety, &pICertificate))) { DebugTrace("Error [%#x]: CreateCertificateObject() failed.\n", hr); goto ErrorExit; } // // Add to collection. // if (FAILED(hr = Add(pICertificate))) { DebugTrace("Error [%#x]: CCertificates::Add() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } CommonExit: DebugTrace("Leaving CCertificates::AddContext().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificate::LoadFromCert Synopsis : Load from the single certificate context. Parameter: PCCERT_CONTEXT pContext - Pointer to a cert context. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::LoadFromCert (PCCERT_CONTEXT pCertContext) { HRESULT hr = S_OK; DebugTrace("Entering CCertificates::LoadFromCert().\n"); // // Sanity check. // ATLASSERT(pCertContext); // // Add the cert. // if (FAILED(hr = AddContext(pCertContext))) { DebugTrace("Error [%#x]: CCertificates::AddContext() failed.\n", hr); goto ErrorExit; } CommonExit: DebugTrace("Leaving CCertificates::LoadFromCert().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // m_coll.clear(); goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificate::LoadFromChain Synopsis : Load all certificates from a chain. Parameter: PCCERT_CHAIN_CONTEXT pChainContext - Pointer to a chain context. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::LoadFromChain (PCCERT_CHAIN_CONTEXT pChainContext) { HRESULT hr = S_OK; DebugTrace("Entering CCertificates::LoadFromChain().\n"); // // Sanity check. // ATLASSERT(pChainContext); // // Process only the simple chain. // PCERT_SIMPLE_CHAIN pSimpleChain = *pChainContext->rgpChain; // // Now loop through all certs in the chain. // for (DWORD i = 0; i < pSimpleChain->cElement; i++) { // // Add the cert. // if (FAILED(hr = AddContext(pSimpleChain->rgpElement[i]->pCertContext))) { DebugTrace("Error [%#x]: CCertificates::AddContext() failed.\n", hr); goto ErrorExit; } } CommonExit: DebugTrace("Leaving CCertificates::LoadFromChain().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // m_coll.clear(); goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::LoadFromStore Synopsis : Load all certificates from a store. Parameter: HCERTSTORE hCertStore - Store where all certificates are to be loaded from. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::LoadFromStore (HCERTSTORE hCertStore) { HRESULT hr = S_OK; PCCERT_CONTEXT pCertContext = NULL; DebugTrace("Entering CCertificates::LoadFromStore().\n"); // // Sanity check. // ATLASSERT(hCertStore); // // Now transfer all certificates from the store to the collection map. // while (pCertContext = ::CertEnumCertificatesInStore(hCertStore, pCertContext)) { // // Add the cert. // if (FAILED(hr = AddContext(pCertContext))) { DebugTrace("Error [%#x]: CCertificates::AddContext() failed.\n", hr); goto ErrorExit; } // // Don'f free cert context here, as CertEnumCertificatesInStore() // will do that automatically!!! // } // // Above loop can exit either because there is no more certificate in // the store or an error. Need to check last error to be certain. // if (CRYPT_E_NOT_FOUND != ::GetLastError()) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertEnumCertificatesInStore() failed.\n", hr); goto ErrorExit; } CommonExit: DebugTrace("Leaving CCertificates::LoadFromStore().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } if (FAILED(hr)) { m_coll.clear(); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::LoadFromMessage Synopsis : Load all certificates from a message. Parameter: HCRYPTMSG hMsg - Message handle. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::LoadFromMessage (HCRYPTMSG hMsg) { HRESULT hr = S_OK; DWORD dwCertCount = 0; DWORD cbCertCount = sizeof(dwCertCount); DebugTrace("Entering CCertificates::LoadFromMessage().\n"); // // Sanity check. // ATLASSERT(hMsg); // // Get number of certs in message. // if (!::CryptMsgGetParam(hMsg, CMSG_CERT_COUNT_PARAM, 0, (void **) &dwCertCount, &cbCertCount)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgGetParam() failed.\n", hr); goto ErrorExit; } // // Loop thru all certs in the message. // while (dwCertCount--) { PCCERT_CONTEXT pCertContext = NULL; CRYPT_DATA_BLOB EncodedCertBlob = {0, NULL}; // // Get a cert from the bag of certs. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_CERT_PARAM, dwCertCount, (void **) &EncodedCertBlob.pbData, &EncodedCertBlob.cbData))) { DebugTrace("Error [%#x]: GetMsgParam() failed.\n", hr); goto ErrorExit; } // // Create a context for the cert. // pCertContext = ::CertCreateCertificateContext(CAPICOM_ASN_ENCODING, (const PBYTE) EncodedCertBlob.pbData, EncodedCertBlob.cbData); // // Free encoded cert blob memory before checking result. // ::CoTaskMemFree((LPVOID) EncodedCertBlob.pbData); if (!pCertContext) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertCreateCertificateContext() failed.\n", hr); goto ErrorExit; } // // Add the cert. // hr = AddContext(pCertContext); // // Free cert context before checking result. // ::CertFreeCertificateContext(pCertContext); if (FAILED(hr)) { DebugTrace("Error [%#x]: CCertificates::AddContext() failed.\n", hr); goto ErrorExit; } } CommonExit: DebugTrace("Leaving CCertificates::LoadFromMessage().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // m_coll.clear(); goto CommonExit; } //////////////////////////////////////////////////////////////////////////////// // // Custom interfaces. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::_ExportToStore Synopsis : Export all certificates in the collection to a specified store. Parameter: HCERTSTORE hCertStore - HCERSTORE to copy to. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::_ExportToStore (HCERTSTORE hCertStore) { HRESULT hr = S_OK; CertificateMap::iterator iter; DebugTrace("Entering CCertificates::_ExportToStore().\n"); // // Sanity check. // ATLASSERT(hCertStore); // // Now, transfer all the certs in the collection to the store. // for (iter = m_coll.begin(); iter != m_coll.end(); iter++) { PCCERT_CONTEXT pCertContext = NULL; CComPtr pICertificate = NULL; // // Get to the stored interface pointer. // if (!(pICertificate = (*iter).second)) { hr = CAPICOM_E_INTERNAL; DebugTrace("Error [%#x]: iterator returns NULL pICertificate pointer.\n", hr); goto ErrorExit; } // // Get the CERT_CONTEXT. // if (FAILED(hr = ::GetCertContext(pICertificate, &pCertContext))) { DebugTrace("Error [%#x]: pICertificate->GetContext() failed.\n", hr); goto ErrorExit; } // // Add the link to store. // BOOL bResult = ::CertAddCertificateContextToStore(hCertStore, pCertContext, CERT_STORE_ADD_ALWAYS, NULL); // // First free cert context. // ::CertFreeCertificateContext(pCertContext); // // then check result. // if (!bResult) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertAddCertificateContextToStore() failed.\n", hr); goto ErrorExit; } } CommonExit: DebugTrace("Leaving CCertificates::_ExportToStore().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CCertificates::Init Synopsis : Initialize the object. Parameter: CAPICOM_CERTIFICATES_SOURCE ccs - Source where to get the certificates. DWORD dwCurrentSafety - Current safety setting. BOOL bIndexedByThumbprint - TRUE to index by thumbprint. Remark : This method is not part of the COM interface (it is a normal C++ member function). We need it to initialize the object created internally by us. Since it is only a normal C++ member function, this function can only be called from a C++ class pointer, not an interface pointer. ------------------------------------------------------------------------------*/ STDMETHODIMP CCertificates::Init (CAPICOM_CERTIFICATES_SOURCE ccs, DWORD dwCurrentSafety, BOOL bIndexedByThumbprint) { HRESULT hr = S_OK; DebugTrace("Entering CCertificates::Init().\n"); // // Set safety setting. // m_dwCurrentSafety = dwCurrentSafety; m_dwNextIndex = 0; #if (1) // DSIE: Can turn on index by thumbprint if desired. m_bIndexedByThumbprint = FALSE; #else m_bIndexedByThumbprint = bIndexedByThumbprint; #endif // // Initialize object. // switch (ccs.dwSource) { case CAPICOM_CERTIFICATES_LOAD_FROM_CERT: { // // Sanity check. // ATLASSERT(ccs.pCertContext); if (FAILED(hr = LoadFromCert(ccs.pCertContext))) { DebugTrace("Error [%#x]: CCertificates::LoadFromCert() failed.\n", hr); goto ErrorExit; } break; } case CAPICOM_CERTIFICATES_LOAD_FROM_CHAIN: { // // Sanity check. // ATLASSERT(ccs.pChainContext); if (FAILED(hr = LoadFromChain(ccs.pChainContext))) { DebugTrace("Error [%#x]: CCertificates::LoadFromChain() failed.\n", hr); goto ErrorExit; } break; } case CAPICOM_CERTIFICATES_LOAD_FROM_STORE: { // // Sanity check. // ATLASSERT(ccs.hCertStore); if (FAILED(hr = LoadFromStore(ccs.hCertStore))) { DebugTrace("Error [%#x]: CCertificates::LoadFromStore() failed.\n", hr); goto ErrorExit; } break; } case CAPICOM_CERTIFICATES_LOAD_FROM_MESSAGE: { // // Sanity check. // ATLASSERT(ccs.hCryptMsg); if (FAILED(hr = LoadFromMessage(ccs.hCryptMsg))) { DebugTrace("Error [%#x]: CCertificates::LoadFromMessage() failed.\n", hr); goto ErrorExit; } break; } default: { // // We have a bug. // ATLASSERT(FALSE); hr = CAPICOM_E_INTERNAL; DebugTrace("Error [%#x]: Unknown store source (ccs.dwSource = %d).\n", hr, ccs.dwSource); goto ErrorExit; } } CommonExit: DebugTrace("Leaving CCertificates::Init().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); goto CommonExit; }