// // Certificat.cpp // #include "StdAfx.h" #include "CertWiz.h" #include "Certificat.h" #include "certutil.h" #include #include "base64.h" #include "resource.h" #include "certupgr.h" #include #include "mru.h" #include "Shlwapi.h" #include const CLSID CLSID_CEnroll = {0x43F8F289, 0x7A20, 0x11D0, {0x8F, 0x06, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xE1}}; const IID IID_IEnroll = {0xacaa7838, 0x4585, 0x11d1, {0xab, 0x57, 0x00, 0xc0, 0x4f, 0xc2, 0x95, 0xe1}}; const IID IID_ICEnroll2 = {0x704ca730, 0xc90b, 0x11d1, {0x9b, 0xec, 0x00, 0xc0, 0x4f, 0xc2, 0x95, 0xe1}}; const CLSID CLSID_CCertRequest = {0x98aff3f0, 0x5524, 0x11d0, {0x88, 0x12, 0x00, 0xa0, 0xc9, 0x03, 0xb8, 0x3c}}; const IID IID_ICertRequest = {0x014e4840, 0x5523, 0x11d0, {0x88, 0x12, 0x00, 0xa0, 0xc9, 0x03, 0xb8, 0x3c}}; WCHAR * bstrEmpty = L""; extern CCertWizApp theApp; BOOL CCryptBlob::Resize(DWORD cb) { if (cb > GetSize()) { if (NULL != (m_blob.pbData = Realloc(m_blob.pbData, cb))) { m_blob.cbData = cb; return TRUE; } return FALSE; } return TRUE; } IMPLEMENT_DYNAMIC(CCertificate, CObject) CCertificate::CCertificate() : m_CAType(CA_OFFLINE), m_KeyLength(512), m_pPendingRequest(NULL), m_RespCertContext(NULL), m_pInstalledCert(NULL), m_pKeyRingCert(NULL), m_pEnroll(NULL), m_status_code(-1), m_CreateDirectory(FALSE), m_SGCcertificat(FALSE), m_DefaultCSP(TRUE), m_DefaultProviderType(PROV_RSA_SCHANNEL) { } CCertificate::~CCertificate() { if (m_pPendingRequest != NULL) CertFreeCertificateContext(m_pPendingRequest); if (m_RespCertContext != NULL) CertFreeCertificateContext(m_RespCertContext); if (m_pInstalledCert != NULL) CertFreeCertificateContext(m_pInstalledCert); if (m_pKeyRingCert != NULL) CertFreeCertificateContext(m_pKeyRingCert); if (m_pEnroll != NULL) m_pEnroll->Release(); } const TCHAR szResponseFileName[] = _T("ResponseFileName"); const TCHAR szKeyRingFileName[] = _T("KeyRingFileName"); const TCHAR szRequestFileName[] = _T("RequestFileName"); const TCHAR szCertificateTemplate[] = _T("CertificateTemplate"); const TCHAR szState[] = _T("State"); const TCHAR szStateMRU[] = _T("StateMRU"); const TCHAR szLocality[] = _T("Locality"); const TCHAR szLocalityMRU[] = _T("LocalityMRU"); const TCHAR szOrganization[] = _T("Organization"); const TCHAR szOrganizationMRU[] = _T("OrganizationMRU"); const TCHAR szOrganizationUnit[] = _T("OrganizationUnit"); const TCHAR szOrganizationUnitMRU[] = _T("OrganizationUnitMRU"); #define QUERY_NAME(x,y)\ do {\ if (ERROR_SUCCESS == RegQueryValueEx(hKey, (x), NULL, &dwType, NULL, &cbData))\ {\ ASSERT(dwType == REG_SZ);\ pName = (BYTE *)(y).GetBuffer(cbData);\ RegQueryValueEx(hKey, (x), NULL, &dwType, pName, &cbData);\ if (pName != NULL)\ {\ (y).ReleaseBuffer();\ pName = NULL;\ }\ }\ } while (0) BOOL CCertificate::Init() { ASSERT(!m_MachineName.IsEmpty()); ASSERT(!m_WebSiteInstanceName.IsEmpty()); // get web site description from metabase, it could be empty // do not panic in case of error if (!GetServerComment(m_MachineName, m_WebSiteInstanceName, m_FriendlyName, &m_hResult)) m_hResult = S_OK; m_CommonName = m_MachineName; m_CommonName.MakeLower(); HKEY hKey = theApp.RegOpenKeyWizard(); DWORD dwType; DWORD cbData; if (hKey != NULL) { BYTE * pName = NULL; QUERY_NAME(szRequestFileName, m_ReqFileName); QUERY_NAME(szResponseFileName, m_RespFileName); QUERY_NAME(szKeyRingFileName, m_KeyFileName); QUERY_NAME(szCertificateTemplate, m_CertificateTemplate); QUERY_NAME(szState, m_State); QUERY_NAME(szLocality, m_Locality); QUERY_NAME(szOrganization, m_Organization); QUERY_NAME(szOrganizationUnit, m_OrganizationUnit); RegCloseKey(hKey); } #ifdef _DEBUG else { TRACE(_T("Failed to open Registry key for Wizard parameters\n")); } #endif if (m_CertificateTemplate.IsEmpty()) { // User didn't defined anything -- use standard name m_CertificateTemplate = wszCERTTYPE_WEBSERVER; } return TRUE; } #define SAVE_NAME(x,y)\ do {\ if (!(y).IsEmpty())\ {\ VERIFY(ERROR_SUCCESS == RegSetValueEx(hKey, (x), 0, REG_SZ, \ (const BYTE *)(LPCTSTR)(y), \ sizeof(TCHAR) * ((y).GetLength() + 1)));\ }\ } while (0) BOOL CCertificate::SaveSettings() { HKEY hKey = theApp.RegOpenKeyWizard(); if (hKey != NULL) { switch (GetStatusCode()) { case REQUEST_NEW_CERT: case REQUEST_RENEW_CERT: SAVE_NAME(szState, m_State); AddToMRU(szStateMRU, m_State); SAVE_NAME(szLocality, m_Locality); AddToMRU(szLocalityMRU, m_Locality); SAVE_NAME(szOrganization, m_Organization); AddToMRU(szOrganizationMRU, m_Organization); SAVE_NAME(szOrganizationUnit, m_OrganizationUnit); AddToMRU(szOrganizationUnitMRU, m_OrganizationUnit); SAVE_NAME(szRequestFileName, m_ReqFileName); break; case REQUEST_PROCESS_PENDING: SAVE_NAME(szResponseFileName, m_RespFileName); break; case REQUEST_IMPORT_KEYRING: SAVE_NAME(szKeyRingFileName, m_KeyFileName); break; default: break; } RegCloseKey(hKey); return TRUE; } #ifdef _DEBUG else { TRACE(_T("Failed to open Registry key for Wizard parameters\n")); } #endif return FALSE; } BOOL CCertificate::SetSecuritySettings() { long dwGenKeyFlags; if (SUCCEEDED(GetEnrollObject()->get_GenKeyFlags(&dwGenKeyFlags))) { dwGenKeyFlags &= 0x0000FFFF; dwGenKeyFlags |= (m_KeyLength << 16); if (m_SGCcertificat) dwGenKeyFlags |= CRYPT_SGCKEY; return (SUCCEEDED(GetEnrollObject()->put_GenKeyFlags(dwGenKeyFlags))); } return FALSE; } // defines taken from the old KeyGen utility #define MESSAGE_HEADER "-----BEGIN NEW CERTIFICATE REQUEST-----\r\n" #define MESSAGE_TRAILER "-----END NEW CERTIFICATE REQUEST-----\r\n" BOOL CCertificate::WriteRequestString(CString& request) { ASSERT(!PathIsRelative(m_ReqFileName)); BOOL bRes = FALSE; try { CString strPath; strPath = m_ReqFileName; LPTSTR pPath = strPath.GetBuffer(strPath.GetLength()); PathRemoveFileSpec(pPath); if (!PathIsDirectory(pPath)) { if (!CreateDirectoryFromPath(strPath, NULL)) { m_hResult = HRESULT_FROM_WIN32(GetLastError()); SetBodyTextID(USE_DEFAULT_CAPTION); return FALSE; } } strPath.ReleaseBuffer(); HANDLE hFile = ::CreateFile(m_ReqFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD cb = request.GetLength(); char * ascii_buf = (char *)_alloca(cb); wcstombs(ascii_buf, request, cb); bRes = ::WriteFile(hFile, ascii_buf, cb, &cb, NULL); ::CloseHandle(hFile); } else { m_hResult = HRESULT_FROM_WIN32(GetLastError()); SetBodyTextID(USE_DEFAULT_CAPTION); } } catch (CFileException * e) { TCHAR szCause[255]; e->GetErrorMessage(szCause, 255); TRACE(_T("Got CFileException with error: %s\n"), szCause); m_hResult = HRESULT_FROM_WIN32(e->m_lOsError); } catch (CException * e) { TCHAR szCause[255]; e->GetErrorMessage(szCause, 255); TRACE(_T("Got CException with error: %s\n"), szCause); m_hResult = HRESULT_FROM_WIN32(GetLastError()); } return bRes; } #define HEADER_SERVER_ _T("Server:\t%s\r\n\r\n") #define HEADER_COMMON_NAME_ _T("Common-name:\t%s\r\n") #define HEADER_FRIENDLY_NAME_ _T("Friendly name:\t%s\r\n") #define HEADER_ORG_UNIT_ _T("Organization Unit:\t%s\r\n") #define HEADER_ORGANIZATION_ _T("Organization:\t%s\r\n") #define HEADER_LOCALITY_ _T("Locality:\t%s\r\n") #define HEADER_STATE_ _T("State:\t%s\r\n") #define HEADER_COUNTRY_ _T("Country:\t%s\r\n") static void WRITE_LINE(CString& str, TCHAR * format, CString& data) { CString buf; buf.Format(format, data); str += buf; } void CCertificate::DumpHeader(CString& str) { DumpOnlineHeader(str); } void CCertificate::DumpOnlineHeader(CString& str) { WRITE_LINE(str, HEADER_SERVER_, m_CommonName); WRITE_LINE(str, HEADER_FRIENDLY_NAME_, m_FriendlyName); WRITE_LINE(str, HEADER_ORG_UNIT_, m_OrganizationUnit); WRITE_LINE(str, HEADER_ORGANIZATION_, m_Organization); WRITE_LINE(str, HEADER_LOCALITY_, m_Locality);; WRITE_LINE(str, HEADER_STATE_, m_State); WRITE_LINE(str, HEADER_COUNTRY_, m_Country); } BOOL CCertificate::GetSelectedCertDescription(CERT_DESCRIPTION& cd) { BOOL bRes = FALSE; ASSERT(m_pSelectedCertHash != NULL); HCERTSTORE hStore = OpenMyStore(GetEnrollObject(), &m_hResult); if (hStore != NULL) { PCCERT_CONTEXT pCert = CertFindCertificateInStore(hStore, CRYPT_ASN_ENCODING, 0, CERT_FIND_HASH, m_pSelectedCertHash, NULL); if (pCert != NULL) { bRes = GetCertDescription(pCert, cd); CertFreeCertificateContext(pCert); } CertCloseStore(hStore, 0); } return bRes; } void CCertificate::CreateDN(CString& str) { str.Empty(); str += _T("CN=") + m_CommonName; str += _T("\n,OU=") + m_OrganizationUnit; str += _T("\n,O=") + m_Organization; str += _T("\n,L=") + m_Locality; str += _T("\n,S=") + m_State; str += _T("\n,C=") + m_Country; } PCCERT_CONTEXT CCertificate::GetPendingRequest() { if (m_pPendingRequest == NULL) { ASSERT(!m_WebSiteInstanceName.IsEmpty()); m_pPendingRequest = GetPendingDummyCert(m_WebSiteInstanceName, GetEnrollObject(), &m_hResult); } return m_pPendingRequest; } PCCERT_CONTEXT CCertificate::GetInstalledCert() { if (m_pInstalledCert == NULL) { m_pInstalledCert = ::GetInstalledCert(m_MachineName, m_WebSiteInstanceName, GetEnrollObject(), &m_hResult); } return m_pInstalledCert; } PCCERT_CONTEXT CCertificate::GetKeyRingCert() { ASSERT(!m_KeyFileName.IsEmpty()); ASSERT(!m_KeyPassword.IsEmpty()); if (m_pKeyRingCert == NULL) { int len = m_KeyPassword.GetLength() * 2; char * ascii_password = (char *)_alloca(len + 1); ASSERT(ascii_password != NULL); size_t n; VERIFY(-1 != (n = wcstombs(ascii_password, m_KeyPassword, len))); ascii_password[n] = '\0'; m_pKeyRingCert = ::ImportKRBackupToCAPIStore( (LPTSTR)(LPCTSTR)m_KeyFileName, ascii_password, _T("MY")); if (m_pKeyRingCert == NULL) { m_hResult = HRESULT_FROM_WIN32(GetLastError()); } } return m_pKeyRingCert; } /* INTRINSA suppress=null_pointers, uninitialized */ PCCERT_CONTEXT CCertificate::GetResponseCert() { if (m_RespCertContext == NULL) { ASSERT(!m_RespFileName.IsEmpty()); m_RespCertContext = GetCertContextFromPKCS7File( m_RespFileName, &GetPendingRequest()->pCertInfo->SubjectPublicKeyInfo, &m_hResult); ASSERT(SUCCEEDED(m_hResult)); } return m_RespCertContext; } BOOL CCertificate::GetResponseCertDescription(CERT_DESCRIPTION& cd) { CERT_DESCRIPTION cdReq; if (GetCertDescription(GetResponseCert(), cd)) { if (GetCertDescription(GetPendingRequest(), cdReq)) { cd.m_FriendlyName = cdReq.m_FriendlyName; } return TRUE; } return FALSE; } /*------------------------------------------------------------------------------ IsResponseInstalled Function checks if certificate from the response file m_RespFileName was istalled to some server. If possible, it returns name of this server in str. Returns FALSE if certificate is not found in MY store or if this store cannot be opened */ BOOL CCertificate::IsResponseInstalled( CString& str // return server instance name (not yet implemented) ) { BOOL bRes = FALSE; // get cert context from response file PCCERT_CONTEXT pContext = GetCertContextFromPKCS7File( m_RespFileName, NULL, &m_hResult); if (pContext != NULL) { HCERTSTORE hStore = OpenMyStore(GetEnrollObject(), &m_hResult); if (hStore != NULL) { PCCERT_CONTEXT pCert = NULL; while (NULL != (pCert = CertEnumCertificatesInStore(hStore, pCert))) { // do not include installed cert to the list if (CertCompareCertificate(X509_ASN_ENCODING, pContext->pCertInfo, pCert->pCertInfo)) { bRes = TRUE; // Try to find, where is was installed break; } } if (pCert != NULL) CertFreeCertificateContext(pCert); } } return bRes; } BOOL CCertificate::FindInstanceNameForResponse(CString& str) { BOOL bRes = FALSE; // get cert context from response file PCCERT_CONTEXT pContext = GetCertContextFromPKCS7File( m_RespFileName, NULL, &m_hResult); if (pContext != NULL) { // find dummy cert in REQUEST store that has public key // the same as in this context PCCERT_CONTEXT pReq = GetReqCertByKey(GetEnrollObject(), &pContext->pCertInfo->SubjectPublicKeyInfo, &m_hResult); if (pReq != NULL) { // get friendly name prop from this dummy cert if (!GetFriendlyName(pReq, str, &m_hResult)) { // get instance name prop from this dummy cert DWORD cb; BYTE * prop; if ( CertGetCertificateContextProperty(pReq, CERTWIZ_INSTANCE_NAME_PROP_ID, NULL, &cb) && (NULL != (prop = (BYTE *)_alloca(cb))) && CertGetCertificateContextProperty(pReq, CERTWIZ_INSTANCE_NAME_PROP_ID, prop, &cb) ) { // decode this instance name property DWORD cbData = 0; BYTE * data = NULL; if ( CryptDecodeObject(CRYPT_ASN_ENCODING, X509_UNICODE_ANY_STRING, prop, cb, 0, NULL, &cbData) && (NULL != (data = (BYTE *)_alloca(cbData))) && CryptDecodeObject(CRYPT_ASN_ENCODING, X509_UNICODE_ANY_STRING, prop, cb, 0, data, &cbData) ) { CERT_NAME_VALUE * p = (CERT_NAME_VALUE *)data; CString strInstanceName = (LPCTSTR)p->Value.pbData; // now try to get comment from this server if (GetServerComment(m_MachineName, strInstanceName, str, &m_hResult)) { if (str.IsEmpty()) { // generate something like [Web Site #n] str.LoadString(IDS_WEB_SITE_N); int len = strInstanceName.GetLength(); for (int i = len - 1, count = 0; i >= 0; i--, count++) { if (!_istdigit(strInstanceName.GetAt(i))) break; } ASSERT(count < len); AfxFormatString1(str, IDS_WEB_SITE_N, strInstanceName.Right(count)); } } m_hResult = S_OK; bRes = TRUE; } } } CertFreeCertificateContext(pReq); } else { // probably this request was deleted from the request store } CertFreeCertificateContext(pContext); } return bRes; } IEnroll * CCertificate::GetEnrollObject() { if (m_pEnroll == NULL) { m_hResult = CoCreateInstance(CLSID_CEnroll, NULL, CLSCTX_INPROC_SERVER, IID_IEnroll, (void **)&m_pEnroll); // now we need to change defaults for this // object to LOCAL_MACHINE if (m_pEnroll != NULL) { long dwFlags; VERIFY(SUCCEEDED(m_pEnroll->get_MyStoreFlags(&dwFlags))); dwFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; dwFlags |= CERT_SYSTEM_STORE_LOCAL_MACHINE; // following call will change Request store flags also VERIFY(SUCCEEDED(m_pEnroll->put_MyStoreFlags(dwFlags))); VERIFY(SUCCEEDED(m_pEnroll->get_GenKeyFlags(&dwFlags))); dwFlags |= CRYPT_EXPORTABLE; VERIFY(SUCCEEDED(m_pEnroll->put_GenKeyFlags(dwFlags))); VERIFY(SUCCEEDED(m_pEnroll->put_KeySpec(AT_KEYEXCHANGE))); VERIFY(SUCCEEDED(m_pEnroll->put_ProviderType(m_DefaultProviderType))); VERIFY(SUCCEEDED(m_pEnroll->put_DeleteRequestCert(TRUE))); } } ASSERT(m_pEnroll != NULL); return m_pEnroll; } BOOL CCertificate::HasInstalledCert() { BOOL bResult = FALSE; CMetaKey key(m_MachineName, METADATA_PERMISSION_READ, METADATA_MASTER_ROOT_HANDLE, m_WebSiteInstanceName); if (key.Succeeded()) { CString store_name; CBlob blob; if ( S_OK == key.QueryValue(MD_SSL_CERT_HASH, blob) && S_OK == key.QueryValue(MD_SSL_CERT_STORE_NAME, store_name) ) { bResult = TRUE; } } return bResult; } HRESULT CCertificate::UninstallCert() { CMetaKey key(m_MachineName, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE, METADATA_MASTER_ROOT_HANDLE, m_WebSiteInstanceName); if (key.Succeeded()) { CString store_name; key.QueryValue(MD_SSL_CERT_STORE_NAME, store_name); if (SUCCEEDED(key.DeleteValue(MD_SSL_CERT_HASH))) key.DeleteValue(MD_SSL_CERT_STORE_NAME); } return m_hResult = key.QueryResult(); } BOOL CCertificate::WriteRequestBody() { ASSERT(!m_ReqFileName.IsEmpty()); HRESULT hr; BOOL bRes = FALSE; CString strDN; CreateDN(strDN); ASSERT(!strDN.IsEmpty()); CString strUsage(szOID_PKIX_KP_SERVER_AUTH); CCryptBlobIMalloc request; GetEnrollObject()->put_ProviderType(m_DefaultCSP ? m_DefaultProviderType : m_CustomProviderType); if (!m_DefaultCSP) { GetEnrollObject()->put_ProviderNameWStr((LPTSTR)(LPCTSTR)m_CspName); GetEnrollObject()->put_KeySpec(AT_SIGNATURE); } if (SUCCEEDED(hr = GetEnrollObject()->createPKCS10WStr((LPTSTR)(LPCTSTR)strDN, (LPTSTR)(LPCTSTR)strUsage, request))) { // BASE64 encode pkcs 10 DWORD err, cch; char * psz; if ( (err = Base64EncodeA(request.GetData(), request.GetSize(), NULL, &cch)) == ERROR_SUCCESS && (psz = (char *)_alloca(cch)) != NULL && (err = Base64EncodeA(request.GetData(), request.GetSize(), psz, &cch)) == ERROR_SUCCESS ) { HANDLE hFile = ::CreateFile(m_ReqFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == NULL) return FALSE; DWORD written; ::SetFilePointer(hFile, 0, NULL, FILE_END); ::WriteFile(hFile, MESSAGE_HEADER, sizeof(MESSAGE_HEADER) - 1, &written, NULL); ::WriteFile(hFile, psz, cch, &written, NULL); ::WriteFile(hFile, MESSAGE_TRAILER, sizeof(MESSAGE_TRAILER) - 1, &written, NULL); ::CloseHandle(hFile); // get back request from encoded data PCERT_REQUEST_INFO req_info; VERIFY(GetRequestInfoFromPKCS10(request, &req_info, &m_hResult)); // find dummy cert put to request store by createPKCS10 call HCERTSTORE hStore = OpenRequestStore(GetEnrollObject(), &m_hResult); if (hStore != NULL) { PCCERT_CONTEXT pDummyCert = CertFindCertificateInStore(hStore, CRYPT_ASN_ENCODING, 0, CERT_FIND_PUBLIC_KEY, (void *)&req_info->SubjectPublicKeyInfo, NULL); if (pDummyCert != NULL) { // now we need to attach web server instance name to this cert // encode string into data blob CRYPT_DATA_BLOB name; CERT_NAME_VALUE name_value; name_value.dwValueType = CERT_RDN_BMP_STRING; name_value.Value.cbData = 0; name_value.Value.pbData = (LPBYTE)(LPCTSTR)m_WebSiteInstanceName; { if ( !CryptEncodeObject(CRYPT_ASN_ENCODING, X509_UNICODE_ANY_STRING, &name_value, NULL, &name.cbData) || (name.pbData = (BYTE *)_alloca(name.cbData)) == NULL || !CryptEncodeObject(CRYPT_ASN_ENCODING, X509_UNICODE_ANY_STRING, &name_value, name.pbData, &name.cbData) ) { ASSERT(FALSE); } VERIFY(bRes = CertSetCertificateContextProperty(pDummyCert, CERTWIZ_INSTANCE_NAME_PROP_ID, 0, &name)); } // put friendly name to dummy cert -- we will reuse it later m_FriendlyName.ReleaseBuffer(); AttachFriendlyName(pDummyCert, m_FriendlyName, &m_hResult); // we also need to put some flag to show what we are waiting for: // new sertificate or renewing certificate CRYPT_DATA_BLOB flag; if ( !CryptEncodeObject(CRYPT_ASN_ENCODING, X509_INTEGER, &m_status_code, NULL, &flag.cbData) || (flag.pbData = (BYTE *)_alloca(flag.cbData)) == NULL || !CryptEncodeObject(CRYPT_ASN_ENCODING, X509_INTEGER, &m_status_code, flag.pbData, &flag.cbData) ) { ASSERT(FALSE); } VERIFY(bRes = CertSetCertificateContextProperty(pDummyCert, CERTWIZ_REQUEST_FLAG_PROP_ID, 0, &flag)); CertFreeCertificateContext(pDummyCert); } CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } LocalFree(req_info); } bRes = TRUE; } return bRes; } BOOL CCertificate::InstallResponseCert() { BOOL bRes = FALSE; CCryptBlobLocal blobRequestText; // Get all our data attached to dummy cert GetFriendlyName(GetPendingRequest(), m_FriendlyName, &m_hResult); ASSERT(!m_FriendlyName.IsEmpty()); GetBlobProperty(GetPendingRequest(), CERTWIZ_REQUEST_TEXT_PROP_ID, blobRequestText, &m_hResult); ASSERT(blobRequestText.GetSize() != 0); CCryptBlobLocal hash_blob; if (::GetHashProperty(GetResponseCert(), hash_blob, &m_hResult)) { if (SUCCEEDED(m_hResult = GetEnrollObject()->acceptFilePKCS7WStr( (LPTSTR)(LPCTSTR)m_RespFileName)) && InstallCertByHash(hash_blob, m_MachineName, m_WebSiteInstanceName, GetEnrollObject(), &m_hResult) ) { // reattach friendly name and request text to installed cert m_FriendlyName.ReleaseBuffer(); AttachFriendlyName(GetInstalledCert(), m_FriendlyName, &m_hResult); bRes = CertSetCertificateContextProperty(GetInstalledCert(), CERTWIZ_REQUEST_TEXT_PROP_ID, 0, blobRequestText); } } if (!bRes) { SetBodyTextID(USE_DEFAULT_CAPTION); } return bRes; } // We don't have initial request for KeyRing certificate, therefore we will // not be able to renew this certificate // BOOL CCertificate::InstallKeyRingCert() { BOOL bRes = FALSE; CCryptBlobLocal hash_blob; if (::GetHashProperty(GetKeyRingCert(), hash_blob, &m_hResult)) { HRESULT hr; CString name; ::GetFriendlyName(GetKeyRingCert(), name, &hr); if (CRYPT_E_NOT_FOUND == hr || name.IsEmpty()) { CERT_DESCRIPTION desc; if (GetCertDescription(GetKeyRingCert(), desc)) bRes = AttachFriendlyName(GetKeyRingCert(), desc.m_CommonName, &hr); } ASSERT(bRes); bRes = InstallCertByHash(hash_blob, m_MachineName, m_WebSiteInstanceName, GetEnrollObject(), &m_hResult); } if (!bRes) { SetBodyTextID(USE_DEFAULT_CAPTION); } return bRes; } // Instead of renewal we create new certificate based on parameters // from the current one. After creation we install this certificate in place // of current one and deleting the old one from store. Even if IIS has an // opened SSL connection it should get a notification and update the certificate // data. // BOOL CCertificate::SubmitRenewalRequest() { BOOL bRes = LoadRenewalData(); if (bRes) { PCCERT_CONTEXT pCurrent = GetInstalledCert(); m_pInstalledCert = NULL; if (bRes = SubmitRequest()) { CertDeleteCertificateFromStore(pCurrent); } } return bRes; } BOOL CCertificate::SubmitRequest() { ASSERT(!m_ConfigCA.IsEmpty()); BOOL bRes = FALSE; ICertRequest * pRequest = NULL; if (SUCCEEDED(m_hResult = CoCreateInstance(CLSID_CCertRequest, NULL, CLSCTX_INPROC_SERVER, IID_ICertRequest, (void **)&pRequest))) { CString strDN; CreateDN(strDN); BSTR request; if (SUCCEEDED(m_hResult = CreateRequest_Base64( (BSTR)(LPCTSTR)strDN, GetEnrollObject(), m_DefaultCSP ? NULL : (LPTSTR)(LPCTSTR)m_CspName, m_DefaultCSP ? m_DefaultProviderType : m_CustomProviderType, &request))) { ASSERT(pRequest != NULL); CString attrib; GetCertificateTemplate(attrib); LONG disp; m_hResult = pRequest->Submit(CR_IN_BASE64 | CR_IN_PKCS10, request, (BSTR)(LPCTSTR)attrib, (LPTSTR)(LPCTSTR)m_ConfigCA, &disp); #ifdef _DEBUG if (FAILED(m_hResult)) TRACE(_T("Submit request returned HRESULT %x; Disposition %x\n"), m_hResult, disp); #endif if (SUCCEEDED(m_hResult)) { if (disp == CR_DISP_ISSUED) { BSTR bstrOutCert = NULL; if (SUCCEEDED(m_hResult = pRequest->GetCertificate(CR_OUT_BASE64 /*| CR_OUT_CHAIN */, &bstrOutCert))) { CRYPT_DATA_BLOB blob; blob.cbData = SysStringByteLen(bstrOutCert); blob.pbData = (BYTE *)bstrOutCert; m_hResult = GetEnrollObject()->acceptPKCS7Blob(&blob); if (SUCCEEDED(m_hResult)) { PCCERT_CONTEXT pContext = GetCertContextFromPKCS7(blob.pbData, blob.cbData, NULL, &m_hResult); ASSERT(pContext != NULL); if (pContext != NULL) { BYTE HashBuffer[40]; // give it some extra size DWORD dwHashSize = sizeof(HashBuffer); if (CertGetCertificateContextProperty(pContext, CERT_SHA1_HASH_PROP_ID, (VOID *) HashBuffer, &dwHashSize)) { CRYPT_HASH_BLOB hash_blob = {dwHashSize, HashBuffer}; if (!(bRes = InstallHashToMetabase(&hash_blob, m_MachineName, m_WebSiteInstanceName, &m_hResult))) { SetBodyTextID(IDS_CERT_INSTALLATION_FAILURE); } } CertFreeCertificateContext(pContext); } // now put extra properties to the installed cert if (NULL != (pContext = GetInstalledCert())) { if (!(bRes = AttachFriendlyName(pContext, m_FriendlyName, &m_hResult))) { SetBodyTextID(IDS_CERT_INSTALLATION_FAILURE); } } } SysFreeString(bstrOutCert); } } else { switch (disp) { case CR_DISP_INCOMPLETE: case CR_DISP_ERROR: case CR_DISP_DENIED: case CR_DISP_ISSUED_OUT_OF_BAND: case CR_DISP_UNDER_SUBMISSION: { BSTR bstr; if (SUCCEEDED(pRequest->GetDispositionMessage(&bstr))) { SetBodyTextString(CString(bstr)); SysFreeString(bstr); } m_hResult = !S_OK; } break; default: SetBodyTextID(IDS_INTERNAL_ERROR); break; } } } else // !SUCCEEDED { // clear out any error IDs and strings // we will use default processing of m_hResult SetBodyTextID(USE_DEFAULT_CAPTION); } SysFreeString(request); } pRequest->Release(); } return bRes; } BOOL CCertificate::PrepareRequestString(CString& request_text, CCryptBlob& request_blob) { CString strDN; TCHAR szUsage[] = _T(szOID_PKIX_KP_SERVER_AUTH); if (m_status_code == REQUEST_RENEW_CERT) { if ( FALSE == LoadRenewalData() || FALSE == SetSecuritySettings() ) return FALSE; } CreateDN(strDN); ASSERT(!strDN.IsEmpty()); GetEnrollObject()->put_ProviderType(m_DefaultCSP ? m_DefaultProviderType : m_CustomProviderType); if (!m_DefaultCSP) { GetEnrollObject()->put_ProviderNameWStr((LPTSTR)(LPCTSTR)m_CspName); // We are supporting only these two types of CSP, it is pretty safe to // have just two options, because we are using the same two types when // we are populating CSP selection list. if (m_CustomProviderType == PROV_DH_SCHANNEL) GetEnrollObject()->put_KeySpec(AT_SIGNATURE); else if (m_CustomProviderType == PROV_RSA_SCHANNEL) GetEnrollObject()->put_KeySpec(AT_KEYEXCHANGE); } if (FAILED(m_hResult = GetEnrollObject()->createPKCS10WStr((LPTSTR)(LPCTSTR)strDN, szUsage, request_blob)) ) { SetBodyTextID(USE_DEFAULT_CAPTION); return FALSE; } // BASE64 encode pkcs 10 DWORD err, cch; char * psz; if ( ERROR_SUCCESS != (err = Base64EncodeA(request_blob.GetData(), request_blob.GetSize(), NULL, &cch)) || NULL == (psz = (char *)_alloca(cch+1)) || ERROR_SUCCESS != (err = Base64EncodeA(request_blob.GetData(), request_blob.GetSize(), psz, &cch)) ) { return FALSE; } psz[cch] = '\0'; request_text = MESSAGE_HEADER; request_text += psz; request_text += MESSAGE_TRAILER; return TRUE; } BOOL CCertificate::PrepareRequest() { BOOL bRes = FALSE; CString request_text; CCryptBlobIMalloc request_blob; if (PrepareRequestString(request_text, request_blob)) { if (WriteRequestString(request_text)) { CCryptBlobLocal name_blob, request_store_blob, status_blob; // prepare data we want to attach to dummy request if ( EncodeString(m_WebSiteInstanceName, name_blob, &m_hResult) && EncodeInteger(m_status_code, status_blob, &m_hResult) ) { // get back request from encoded data PCERT_REQUEST_INFO pReqInfo; bRes = GetRequestInfoFromPKCS10(request_blob, &pReqInfo, &m_hResult); if (bRes) { // find dummy cert put to request store by createPKCS10 call HCERTSTORE hStore = OpenRequestStore(GetEnrollObject(), &m_hResult); if (hStore != NULL) { PCCERT_CONTEXT pDummyCert = CertFindCertificateInStore(hStore, CRYPT_ASN_ENCODING, 0, CERT_FIND_PUBLIC_KEY, (void *)&pReqInfo->SubjectPublicKeyInfo, NULL); if (pDummyCert != NULL) { if ( CertSetCertificateContextProperty(pDummyCert, CERTWIZ_INSTANCE_NAME_PROP_ID, 0, name_blob) && CertSetCertificateContextProperty(pDummyCert, CERTWIZ_REQUEST_FLAG_PROP_ID, 0, status_blob) // put friendly name to dummy cert -- we will reuse it later && AttachFriendlyName(pDummyCert, m_FriendlyName, &m_hResult) ) { bRes = TRUE; // put certificate text to the clipboard if (OpenClipboard(GetFocus())) { size_t len = request_text.GetLength() + 1; HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len); LPSTR pMem = (LPSTR)GlobalLock(hMem); if (pMem != NULL) { wcstombs(pMem, request_text, len); GlobalUnlock(hMem); SetClipboardData(CF_TEXT, hMem); } CloseClipboard(); } } else { m_hResult = HRESULT_FROM_WIN32(GetLastError()); } CertFreeCertificateContext(pDummyCert); } CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } LocalFree(pReqInfo); } } } } if (!bRes) SetBodyTextID(USE_DEFAULT_CAPTION); return bRes; } BOOL CCertificate::LoadRenewalData() { // we need to obtain data from the installed cert CERT_DESCRIPTION desc; ASSERT(GetInstalledCert() != NULL); BOOL res = FALSE; if (GetCertDescription(GetInstalledCert(), desc)) { m_CommonName = desc.m_CommonName; m_FriendlyName = desc.m_FriendlyName; m_Country = desc.m_Country; m_State = desc.m_State; m_Locality = desc.m_Locality; m_Organization = desc.m_Organization; m_OrganizationUnit = desc.m_OrganizationUnit; DWORD len = CertGetPublicKeyLength(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &GetInstalledCert()->pCertInfo->SubjectPublicKeyInfo); if (len == 0) { m_hResult = HRESULT_FROM_WIN32(GetLastError()); goto ErrorExit; } m_KeyLength = len; BYTE pbData[1024]; CRYPT_KEY_PROV_INFO * pProvInfo = (CRYPT_KEY_PROV_INFO *)pbData; DWORD dwData = 1000; if (!CertGetCertificateContextProperty(GetInstalledCert(), CERT_KEY_PROV_INFO_PROP_ID, pProvInfo, &dwData)) { m_hResult = HRESULT_FROM_WIN32(GetLastError()); goto ErrorExit; } if (pProvInfo->dwProvType != m_DefaultProviderType) { m_DefaultCSP = FALSE; m_CustomProviderType = pProvInfo->dwProvType; m_CspName = pProvInfo->pwszProvName; } CArray uses; uses.Add(szOID_SERVER_GATED_CRYPTO); uses.Add(szOID_SGC_NETSCAPE); m_SGCcertificat = ContainsKeyUsageProperty(GetInstalledCert(), uses, &m_hResult); res = TRUE; } ErrorExit: return res; } #if 0 BOOL CCertificate::WriteRenewalRequest() { BOOL bRes = FALSE; if (GetInstalledCert() != NULL) { BSTR bstrRequest; if ( SUCCEEDED(m_hResult = GetEnrollObject()->put_RenewalCertificate(GetInstalledCert())) && SUCCEEDED(m_hResult = CreateRequest_Base64(bstrEmpty, GetEnrollObject(), m_DefaultCSP ? NULL : (LPTSTR)(LPCTSTR)m_CspName, m_DefaultCSP ? m_DefaultProviderType : m_CustomProviderType, &bstrRequest)) ) { CString str = MESSAGE_HEADER; str += bstrRequest; str += MESSAGE_TRAILER; if (WriteRequestString(str)) { CCryptBlobLocal name_blob, status_blob; CCryptBlobIMalloc request_blob; request_blob.Set(SysStringLen(bstrRequest), (BYTE *)bstrRequest); // prepare data we want to attach to dummy request if ( EncodeString(m_WebSiteInstanceName, name_blob, &m_hResult) && EncodeInteger(m_status_code, status_blob, &m_hResult) ) { // get back request from encoded data PCERT_REQUEST_INFO req_info; if (GetRequestInfoFromPKCS10(request_blob, &req_info, &m_hResult)) { // find dummy cert put to request store by createPKCS10 call HCERTSTORE hStore = OpenRequestStore(GetEnrollObject(), &m_hResult); if (hStore != NULL) { PCCERT_CONTEXT pDummyCert = CertFindCertificateInStore(hStore, CRYPT_ASN_ENCODING, 0, CERT_FIND_PUBLIC_KEY, (void *)&req_info->SubjectPublicKeyInfo, NULL); if (pDummyCert != NULL) { if ( CertSetCertificateContextProperty(pDummyCert, CERTWIZ_INSTANCE_NAME_PROP_ID, 0, name_blob) && CertSetCertificateContextProperty(pDummyCert, CERTWIZ_REQUEST_FLAG_PROP_ID, 0, status_blob) // put friendly name to dummy cert -- we will reuse it later && AttachFriendlyName(pDummyCert, m_FriendlyName, &m_hResult) ) { bRes = TRUE; } else { m_hResult = HRESULT_FROM_WIN32(GetLastError()); } CertFreeCertificateContext(pDummyCert); } CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } LocalFree(req_info); } } } } } return bRes; } #endif CCertDescList::~CCertDescList() { POSITION pos = GetHeadPosition(); while (pos != NULL) { CERT_DESCRIPTION * pDesc = GetNext(pos); delete pDesc; } } BOOL CCertificate::GetCertDescription(PCCERT_CONTEXT pCert, CERT_DESCRIPTION& desc) { BOOL bRes = FALSE; DWORD cb; UINT i, j; CERT_NAME_INFO * pNameInfo; if (pCert == NULL) goto ErrExit; if ( !CryptDecodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME, pCert->pCertInfo->Subject.pbData, pCert->pCertInfo->Subject.cbData, 0, NULL, &cb) || NULL == (pNameInfo = (CERT_NAME_INFO *)_alloca(cb)) || !CryptDecodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME, pCert->pCertInfo->Subject.pbData, pCert->pCertInfo->Subject.cbData, 0, pNameInfo, &cb) ) { goto ErrExit; } for (i = 0; i < pNameInfo->cRDN; i++) { CERT_RDN rdn = pNameInfo->rgRDN[i]; for (j = 0; j < rdn.cRDNAttr; j++) { CERT_RDN_ATTR attr = rdn.rgRDNAttr[j]; if (strcmp(attr.pszObjId, szOID_COMMON_NAME) == 0) { FormatRdnAttr(desc.m_CommonName, attr.dwValueType, attr.Value); } else if (strcmp(attr.pszObjId, szOID_COUNTRY_NAME) == 0) { FormatRdnAttr(desc.m_Country, attr.dwValueType, attr.Value); } else if (strcmp(attr.pszObjId, szOID_LOCALITY_NAME) == 0) { FormatRdnAttr(desc.m_Locality, attr.dwValueType, attr.Value); } else if (strcmp(attr.pszObjId, szOID_STATE_OR_PROVINCE_NAME) == 0) { FormatRdnAttr(desc.m_State, attr.dwValueType, attr.Value); } else if (strcmp(attr.pszObjId, szOID_ORGANIZATION_NAME) == 0) { FormatRdnAttr(desc.m_Organization, attr.dwValueType, attr.Value); } else if (strcmp(attr.pszObjId, szOID_ORGANIZATIONAL_UNIT_NAME) == 0) { FormatRdnAttr(desc.m_OrganizationUnit, attr.dwValueType, attr.Value); } } } // issued to if (!GetNameString(pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, desc.m_CAName, &m_hResult)) goto ErrExit; // expiration date if (!FormatDateString(desc.m_ExpirationDate, pCert->pCertInfo->NotAfter, FALSE, FALSE)) { goto ErrExit; } // purpose if (!FormatEnhancedKeyUsageString(desc.m_Usage, pCert, FALSE, FALSE, &m_hResult)) { // According to local experts, we should also use certs without this property set ASSERT(FALSE); //goto ErrExit; } // friendly name if (!GetFriendlyName(pCert, desc.m_FriendlyName, &m_hResult)) { desc.m_FriendlyName.LoadString(IDS_FRIENDLYNAME_NONE); } bRes = TRUE; ErrExit: return bRes; } int CCertificate::MyStoreCertCount() { int count = 0; HCERTSTORE hStore = OpenMyStore(GetEnrollObject(), &m_hResult); if (hStore != NULL) { PCCERT_CONTEXT pCert = NULL; CArray uses; uses.Add(szOID_PKIX_KP_SERVER_AUTH); uses.Add(szOID_SERVER_GATED_CRYPTO); uses.Add(szOID_SGC_NETSCAPE); while (NULL != (pCert = CertEnumCertificatesInStore(hStore, pCert))) { // do not include installed cert to the list if ( GetInstalledCert() != NULL && CertCompareCertificate(X509_ASN_ENCODING, GetInstalledCert()->pCertInfo, pCert->pCertInfo) ) continue; if (!ContainsKeyUsageProperty(pCert, uses, &m_hResult)) { continue; } count++; } if (pCert != NULL) CertFreeCertificateContext(pCert); VERIFY(CertCloseStore(hStore, 0)); } return count; } BOOL CCertificate::GetCertDescList(CCertDescList& list) { ASSERT(list.GetCount() == 0); BOOL bRes = FALSE; // we are looking to MY store only HCERTSTORE hStore = OpenMyStore(GetEnrollObject(), &m_hResult); if (hStore != NULL) { PCCERT_CONTEXT pCert = NULL; // do not include certs with improper usage CArray uses; uses.Add(szOID_PKIX_KP_SERVER_AUTH); uses.Add(szOID_SERVER_GATED_CRYPTO); uses.Add(szOID_SGC_NETSCAPE); while (NULL != (pCert = CertEnumCertificatesInStore(hStore, pCert))) { // do not include installed cert to the list if ( GetInstalledCert() != NULL && CertCompareCertificate(X509_ASN_ENCODING, GetInstalledCert()->pCertInfo, pCert->pCertInfo) ) continue; if (!ContainsKeyUsageProperty(pCert, uses, &m_hResult)) { if (SUCCEEDED(m_hResult) || m_hResult == CRYPT_E_NOT_FOUND) continue; else goto ErrExit; } CERT_DESCRIPTION * pDesc = new CERT_DESCRIPTION; pDesc->m_hash_length = CERT_HASH_LENGTH; if (!GetCertDescription(pCert, *pDesc)) { delete pDesc; if (m_hResult == CRYPT_E_NOT_FOUND) continue; goto ErrExit; } if (!CertGetCertificateContextProperty(pCert, CERT_SHA1_HASH_PROP_ID, (VOID *)pDesc->m_hash, &pDesc->m_hash_length)) { delete pDesc; m_hResult = HRESULT_FROM_WIN32(GetLastError()); goto ErrExit; } list.AddTail(pDesc); } bRes = TRUE; ErrExit: if (pCert != NULL) CertFreeCertificateContext(pCert); VERIFY(CertCloseStore(hStore, 0)); } return bRes; } BOOL CCertificate::ReplaceInstalled() { // Current cert will be left in the store for next use // Selected cert will be installed instead return InstallSelectedCert(); } BOOL CCertificate::CancelRequest() { // we are just removing dummy cert from the REQUEST store if (NULL != GetPendingRequest()) { BOOL bRes = CertDeleteCertificateFromStore(GetPendingRequest()); if (!bRes) { m_hResult = HRESULT_FROM_WIN32(GetLastError()); SetBodyTextID(USE_DEFAULT_CAPTION); } else m_pPendingRequest = NULL; return bRes; } return FALSE; } BOOL CCertificate::InstallSelectedCert() { BOOL bRes = FALSE; HRESULT hr; // local authorities required that cert should have some // friendly name. We will put common name when friendly name is not available HCERTSTORE hStore = OpenMyStore(GetEnrollObject(), &hr); if (hStore != NULL) { PCCERT_CONTEXT pCert = CertFindCertificateInStore(hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_HASH, (LPVOID)m_pSelectedCertHash, NULL); if (pCert != NULL) { CString name; ::GetFriendlyName(pCert, name, &hr); if (CRYPT_E_NOT_FOUND == hr || name.IsEmpty()) { CERT_DESCRIPTION desc; if (GetCertDescription(pCert, desc)) bRes = AttachFriendlyName(pCert, desc.m_CommonName, &hr); } } VERIFY(CertCloseStore(hStore, 0)); } ASSERT(bRes); // we are just rewriting current settings // current cert will be left in MY store bRes = ::InstallCertByHash(m_pSelectedCertHash, m_MachineName, m_WebSiteInstanceName, GetEnrollObject(), &m_hResult); if (!bRes) { SetBodyTextID(USE_DEFAULT_CAPTION); } return bRes; }