/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Microsoft Windows, Copyright (C) Microsoft Corporation, 2000 File: EnvelopedData.cpp Content: Implementation of CEnvelopedData. History: 11-15-99 dsie created ------------------------------------------------------------------------------*/ #include "StdAfx.h" #include "CAPICOM.h" #include "EnvelopedData.h" #include "Common.h" #include "Convert.h" #include "CertHlpr.h" #include "MsgHlpr.h" #include "SignHlpr.h" #include "Settings.h" //////////////////////////////////////////////////////////////////////////////// // // Local functions. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : SelectRecipientCertCallback Synopsis : Callback routine for CryptUIDlgSelectCertificateW() API for recipient's cert selection. Parameter: See CryptUI.h for defination. Remark : Filter out any cert that is not time valid. ------------------------------------------------------------------------------*/ static BOOL WINAPI SelectRecipientCertCallback (PCCERT_CONTEXT pCertContext, BOOL * pfInitialSelectedCert, void * pvCallbackData) { int nValidity = 0; // // Check cert time validity. // if (0 != (nValidity = ::CertVerifyTimeValidity(NULL, pCertContext->pCertInfo))) { DebugTrace("Info: SelectRecipientCertCallback() - invalid time (%s).\n", nValidity < 0 ? "not yet valid" : "expired"); return FALSE; } return TRUE; } //////////////////////////////////////////////////////////////////////////////// // // CEnvelopedData // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : SetKeyLength Synopsis : Setup the symetric encryption key length. Parameter: HCRYPTPROV hCryptProv - CSP handle. CRYPT_ALGORITHM_IDENTIFIER EncryptAlgorithm - Encryption algorithm. CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength - Key length. void ** pAuxInfo - Receive NULL or allocated and initialized aux info structure. Remark : ------------------------------------------------------------------------------*/ static HRESULT SetKeyLength ( HCRYPTPROV hCryptProv, CRYPT_ALGORITHM_IDENTIFIER EncryptAlgorithm, CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength, void ** ppAuxInfo) { HRESULT hr = S_OK; ALG_ID AlgID = 0; PROV_ENUMALGS_EX peex; CMSG_RC2_AUX_INFO * pRC2AuxInfo = NULL; CMSG_RC4_AUX_INFO * pRC4AuxInfo = NULL; DebugTrace("Entering SetKeyLength().\n"); // // Sanity check. // ATLASSERT(hCryptProv); ATLASSERT(ppAuxInfo); // // Initialize. // *ppAuxInfo = (void *) NULL; // // Get ALG_ID. // if (FAILED(hr = ::OIDToAlgID(EncryptAlgorithm.pszObjId, &AlgID))) { DebugTrace("Error [%#x]: OIDToAlgID() failed.\n", hr); goto ErrorExit; } // // Get algorithm capability from CSP. // if (FAILED(::IsAlgSupported(hCryptProv, AlgID, &peex))) { hr = CAPICOM_E_NOT_SUPPORTED; DebugTrace("Error: requested encryption algorithm is not available.\n"); goto ErrorExit; } // // Setup AuxInfo for RC2. // if (CALG_RC2 == AlgID) { // // Allocate and intialize memory for RC2 AuxInfo structure. // if (!(pRC2AuxInfo = (CMSG_RC2_AUX_INFO *) ::CoTaskMemAlloc(sizeof(CMSG_RC2_AUX_INFO)))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } ::ZeroMemory(pRC2AuxInfo, sizeof(CMSG_RC2_AUX_INFO)); pRC2AuxInfo->cbSize = sizeof(CMSG_RC2_AUX_INFO); // // Determine key length requested. // if (CAPICOM_ENCRYPTION_KEY_LENGTH_MAXIMUM == KeyLength) { pRC2AuxInfo->dwBitLen = peex.dwMaxLen; } else if (CAPICOM_ENCRYPTION_KEY_LENGTH_40_BITS == KeyLength) { if (peex.dwMinLen <= 40 && 40 <= peex.dwMaxLen) { pRC2AuxInfo->dwBitLen = 40; } else { hr = CAPICOM_E_NOT_SUPPORTED; DebugTrace("Error [%#x]: 40-bits encryption is not available.\n", hr); goto ErrorExit; } } else if (CAPICOM_ENCRYPTION_KEY_LENGTH_56_BITS == KeyLength) { if (peex.dwMinLen <= 56 && 56 <= peex.dwMaxLen) { pRC2AuxInfo->dwBitLen = 56; } else { hr = CAPICOM_E_NOT_SUPPORTED; DebugTrace("Error [%#x]: 56-bits encryption is not available.\n", hr); goto ErrorExit; } } else if (CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS == KeyLength) { if (peex.dwMinLen <= 128 && 128 <= peex.dwMaxLen) { pRC2AuxInfo->dwBitLen = 128; } else { hr = CAPICOM_E_NOT_SUPPORTED; DebugTrace("Error [%#x]: 128-bits encryption is not available.\n", hr); goto ErrorExit; } } else { // // Should never get to here. // hr = CAPICOM_E_INTERNAL; DebugTrace("Error [%#x]: Unknown key length (%d).\n", hr, KeyLength); goto ErrorExit; } // // Return RC2 AuxInfo pointer to caller. // *ppAuxInfo = (void *) pRC2AuxInfo; } else if (CALG_RC4 == AlgID) { // // Allocate and intialize memory for RC4 AuxInfo structure. // if (!(pRC4AuxInfo = (CMSG_RC4_AUX_INFO *) ::CoTaskMemAlloc(sizeof(CMSG_RC4_AUX_INFO)))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } ::ZeroMemory(pRC4AuxInfo, sizeof(CMSG_RC4_AUX_INFO)); pRC4AuxInfo->cbSize = sizeof(CMSG_RC4_AUX_INFO); // // Determine key length requested. // if (CAPICOM_ENCRYPTION_KEY_LENGTH_MAXIMUM == KeyLength) { pRC4AuxInfo->dwBitLen = peex.dwMaxLen; } else if (CAPICOM_ENCRYPTION_KEY_LENGTH_40_BITS == KeyLength) { if (peex.dwMinLen <= 40 && 40 <= peex.dwMaxLen) { pRC4AuxInfo->dwBitLen = 40; } else { hr = CAPICOM_E_NOT_SUPPORTED; DebugTrace("Error [%#x]: 40-bits encryption is not available.\n", hr); goto ErrorExit; } } else if (CAPICOM_ENCRYPTION_KEY_LENGTH_56_BITS == KeyLength) { if (peex.dwMinLen <= 56 && 56 <= peex.dwMaxLen) { pRC4AuxInfo->dwBitLen = 56; } else { hr = CAPICOM_E_NOT_SUPPORTED; DebugTrace("Error [%#x]: 56-bits encryption is not available.\n", hr); goto ErrorExit; } } else if (CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS == KeyLength) { if (peex.dwMinLen <= 128 && 128 <= peex.dwMaxLen) { pRC4AuxInfo->dwBitLen = 128; } else { hr = CAPICOM_E_NOT_SUPPORTED; DebugTrace("Error [%#x]: 128-bits encryption is not available.\n", hr); goto ErrorExit; } } else { // // Should never get to here. // hr = CAPICOM_E_INTERNAL; DebugTrace("Error [%#x]: Unknown key length (%d).\n", hr, KeyLength); goto ErrorExit; } // // Return RC4 AuxInfo pointer to caller. // *ppAuxInfo = (void *) pRC4AuxInfo; } CommonExit: DebugTrace("Leaving SetKeyLength().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (pRC2AuxInfo) { ::CoTaskMemFree(pRC2AuxInfo); } if (pRC4AuxInfo) { ::CoTaskMemFree(pRC4AuxInfo); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : SetEncryptionAlgorithm Synopsis : Setup the encryption algorithm structure. Parameter: CAPICOM_ENCRYPTION_ALGORITHM AlgoName - Algorithm ID enum name. CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength - Key length enum name. CRYPT_ALGORITHM_IDENTIFIER * pEncryptAlgorithm - Pointer to the structure. Remark : ------------------------------------------------------------------------------*/ static HRESULT SetEncryptionAlgorithm (CAPICOM_ENCRYPTION_ALGORITHM AlgoName, CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength, CRYPT_ALGORITHM_IDENTIFIER * pEncryptAlgorithm) { HRESULT hr = S_OK; ALG_ID AlgID = 0; DebugTrace("Entering SetEncryptionAlgorithm().\n"); // // Sanity check. // ATLASSERT(pEncryptAlgorithm); // // Initialize structure. // ::ZeroMemory(pEncryptAlgorithm, sizeof(CRYPT_ALGORITHM_IDENTIFIER)); // // Convert to LPSTR. // if (FAILED(hr = ::EnumNameToAlgID(AlgoName, KeyLength, &AlgID))) { DebugTrace("Error: EnumNameToAlgID() failed.\n"); goto ErrorExit; } if (FAILED(hr = ::AlgIDToOID(AlgID, &pEncryptAlgorithm->pszObjId))) { DebugTrace("Error: AlgIDToOID() failed.\n"); goto ErrorExit; } CommonExit: DebugTrace("Leaving SetEncryptionAlgorithm().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (pEncryptAlgorithm->pszObjId) { ::CoTaskMemFree(pEncryptAlgorithm->pszObjId); } goto CommonExit; } //////////////////////////////////////////////////////////////////////////////// // // CEnvelopedData // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::get_Content Synopsis : Return the content. Parameter: BSTR * pVal - Pointer to BSTR to receive the content. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEnvelopedData::get_Content (BSTR * pVal) { HRESULT hr = S_OK; DebugTrace("Entering CEnvelopedData::get_Content().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Check parameters. // if (NULL == pVal) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Parameter pVal is NULL.\n", hr); goto ErrorExit; } // // Make sure content is already initialized. // if (0 == m_ContentBlob.cbData) { hr = CAPICOM_E_ENVELOP_NOT_INITIALIZED; DebugTrace("Error [%#x]: Enveloped object has not been initialized.\n", hr); goto ErrorExit; } // // Sanity check. // ATLASSERT(m_ContentBlob.pbData); // // Return content. // if (FAILED(hr = ::BlobToBstr(&m_ContentBlob, pVal))) { DebugTrace("Error [%#x]: BlobToBstr() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEnvelopedData::get_Content().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::put_Content Synopsis : Initialize the object with content to be enveloped. Parameter: BSTR newVal - BSTR containing the content to be enveloped. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEnvelopedData::put_Content (BSTR newVal) { HRESULT hr = S_OK; DebugTrace("Entering CEnvelopedData::put_Content().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Make sure parameters are valid. // if (0 == ::SysStringByteLen(newVal)) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Parameter newVal is NULL or empty.\n", hr); goto ErrorExit; } // // Update content. // if (FAILED(hr = ::BstrToBlob(newVal, &m_ContentBlob))) { DebugTrace("Error [%#x]: BstrToBlob() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEnvelopedData::put_Content().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::get_Algorithm Synopsis : Property to return the algorithm object. Parameter: IAlgorithm ** pVal - Pointer to pointer to IAlgorithm to receive the interfcae pointer. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEnvelopedData::get_Algorithm (IAlgorithm ** pVal) { HRESULT hr = S_OK; DebugTrace("Entering CEnvelopedData::get_Algorithm().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Check parameters. // if (NULL == pVal) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Parameter pVal is NULL.\n", hr); goto ErrorExit; } // // Sanity check. // ATLASSERT(m_pIAlgorithm); // // Return interface pointer to caller. // if (FAILED(hr = m_pIAlgorithm->QueryInterface(pVal))) { DebugTrace("Unexpected error [%#x]: m_pIAlgorithm->QueryInterface() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEnvelopedData::get_Algorithm().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::get_Recipients Synopsis : Property to return the IRecipients collection object. Parameter: IRecipients ** pVal - Pointer to pointer to IRecipietns to receive the interface pointer. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEnvelopedData::get_Recipients (IRecipients ** pVal) { HRESULT hr = S_OK; DebugTrace("Entering CEnvelopedData::get_Recipients().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Check parameters. // if (NULL == pVal) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Parameter pVal is NULL.\n", hr); goto ErrorExit; } // // Sanity check. // ATLASSERT(m_pIRecipients); // // Return interface pointer to caller. // if (FAILED(hr = m_pIRecipients->QueryInterface(pVal))) { DebugTrace("Unexpected error [%#x]: m_pIRecipients->QueryInterface() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEnvelopedData::get_Recipients().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::Encrypt Synopsis : Envelop the content. Parameter: CAPICOM_ENCODING_TYPE EncodingType - Encoding type. BSTR * pVal - Pointer to BSTR to receive the enveloped message. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEnvelopedData::Encrypt (CAPICOM_ENCODING_TYPE EncodingType, BSTR * pVal) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; HCRYPTPROV hCryptProv = NULL; CRYPT_DATA_BLOB MessageBlob = {0, NULL}; DebugTrace("Entering CEnvelopedData::Encrypt().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Check parameters. // if (NULL == pVal) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Parameter pVal is NULL.\n", hr); goto ErrorExit; } // // Make sure we do have content to envelop. // if (0 == m_ContentBlob.cbData) { hr = CAPICOM_E_ENVELOP_NOT_INITIALIZED; DebugTrace("Error [%#x]: envelop object has not been initialized.\n", hr); goto ErrorExit; } // // Open a new message to encode. // if (FAILED(hr = OpenToEncode(&hMsg, &hCryptProv))) { DebugTrace("Error [%#x]: CEnvelopedData::OpenToEncode() failed.\n", hr); goto ErrorExit; } // // Update envelop content. // if(!::CryptMsgUpdate(hMsg, m_ContentBlob.pbData, m_ContentBlob.cbData, TRUE)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgUpdate() failed.\n", hr); goto ErrorExit; } // // Retrieve enveloped message. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_CONTENT_PARAM, 0, (void **) &MessageBlob.pbData, &MessageBlob.cbData))) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: GetMsgParam() failed to get message content.\n", hr); goto ErrorExit; } // // Now export the enveloped message. // if (FAILED(hr = ::ExportData(MessageBlob, EncodingType, pVal))) { DebugTrace("Error [%#x]: ExportData() failed.\n", hr); goto ErrorExit; } // // Write encoded blob to file, so we can use offline tool such as // ASN parser to analyze message. // // The following line will resolve to void for non debug build, and // thus can be safely removed if desired. // DumpToFile("Enveloped.asn", MessageBlob.pbData, MessageBlob.cbData); } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resource. // if (MessageBlob.pbData) { ::CoTaskMemFree(MessageBlob.pbData); } if (hCryptProv) { ::ReleaseContext(hCryptProv); } if (hMsg) { ::CryptMsgClose(hMsg); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEnvelopedData::Encrypt().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::Decrypt Synopsis : Decrypt the enveloped message. Parameter: BSTR EnvelopedMessage - BSTR containing the enveloped message. Remark : If called from web environment, UI will be displayed, if has not been prevously disabled, to warn the user of accessing the private key for decrypting. ------------------------------------------------------------------------------*/ STDMETHODIMP CEnvelopedData::Decrypt (BSTR EnvelopedMessage) { HRESULT hr = S_OK; HRESULT hr2 = S_OK; HCERTSTORE hCertStores[2] = {NULL, NULL}; HCRYPTMSG hMsg = NULL; PCCERT_CONTEXT pCertContext = NULL; HCRYPTPROV hCryptProv = NULL; DWORD dwKeySpec = 0; BOOL bReleaseContext = FALSE; BOOL bUserPrompted = FALSE; DWORD dwNumRecipients = 0; DWORD cbNumRecipients = sizeof(dwNumRecipients); CRYPT_DATA_BLOB ContentBlob = {0, NULL}; DWORD dwIndex; CComBSTR bstrContent; DebugTrace("Entering CEnvelopedData::Decrypt().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Reset member variables. // if (FAILED(hr = m_pIRecipients->Clear())) { DebugTrace("Error [%#x]: m_pIRecipients->Clear() failed.\n", hr); goto ErrorExit; } // // Make sure parameters are valid. // if (0 == ::SysStringByteLen(EnvelopedMessage)) { hr = E_INVALIDARG; DebugTrace("Error [%#x]: Parameter EnvelopedMessage is NULL or empty.\n", hr); goto ErrorExit; } // // Open current user and local machine MY stores. // hCertStores[0] = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, CAPICOM_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG, L"My"); hCertStores[1] = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, CAPICOM_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG, L"My"); // // Did we manage to open any of the MY store? // if (NULL == hCertStores[0] && NULL == hCertStores[1]) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertOpenStore() failed.\n", hr); goto ErrorExit; } // // Open the message for decode. // if (FAILED(hr = OpenToDecode(NULL, EnvelopedMessage, &hMsg))) { DebugTrace("Error [%#x]: CEnvelopedData::OpenToDecode() failed.\n", hr); goto ErrorExit; } // // Determine number of recipients. // if (!::CryptMsgGetParam(hMsg, CMSG_RECIPIENT_COUNT_PARAM, 0, (void *) &dwNumRecipients, &cbNumRecipients)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgGetParam() failed for CMSG_RECIPIENT_COUNT_PARAM.\n", hr); goto ErrorExit; } // // Find recipient. // for (dwIndex = 0; dwIndex < dwNumRecipients; dwIndex++) { BOOL bFound = FALSE; DATA_BLOB CertInfoBlob = {0, NULL}; // // Get RecipientInfo. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_RECIPIENT_INFO_PARAM, dwIndex, (void **) &CertInfoBlob.pbData, &CertInfoBlob.cbData))) { DebugTrace("Error [%#x]: GetMsgParam() failed for CMSG_RECIPIENT_INFO_PARAM.\n", hr); goto ErrorExit; } // // Find recipient's cert in store. // if ((hCertStores[0] && (pCertContext = ::CertGetSubjectCertificateFromStore(hCertStores[0], CAPICOM_ASN_ENCODING, (CERT_INFO *) CertInfoBlob.pbData))) || (hCertStores[1] && (pCertContext = ::CertGetSubjectCertificateFromStore(hCertStores[1], CAPICOM_ASN_ENCODING, (CERT_INFO *) CertInfoBlob.pbData)))) { bFound = TRUE; } // // Free memory. // ::CoTaskMemFree(CertInfoBlob.pbData); // // Did we find the recipient? // if (bFound) { CMSG_CTRL_DECRYPT_PARA DecryptPara; // // If we are called from a web page, we need to pop up UI // to get user permission to perform decrypt operation. // if (!bUserPrompted) { if (m_dwCurrentSafety && FAILED(hr = OperationApproved(IDD_DECRYPT_SECURITY_ALERT_DLG))) { DebugTrace("Error [%#x]: OperationApproved() failed.\n", hr); goto ErrorExit; } bUserPrompted = TRUE; } // // Acquire CSP context. // if (S_OK == ::AcquireContext(pCertContext, &hCryptProv, &dwKeySpec, &bReleaseContext)) { // // Decypt the message. // ::ZeroMemory(&DecryptPara, sizeof(DecryptPara)); DecryptPara.cbSize = sizeof(DecryptPara); DecryptPara.hCryptProv = hCryptProv; DecryptPara.dwKeySpec = dwKeySpec; DecryptPara.dwRecipientIndex = dwIndex; if(::CryptMsgControl(hMsg, 0, CMSG_CTRL_DECRYPT, &DecryptPara)) { // // Get decrypted content. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_CONTENT_PARAM, 0, (void **) &ContentBlob.pbData, &ContentBlob.cbData))) { DebugTrace("Error [%#x]: GetMsgParam() failed to get CMSG_CONTENT_PARAM.\n", hr); goto ErrorExit; } // // Update member variables. // m_ContentBlob = ContentBlob; // // We are all done, so break out of loop. // break; } else { // // Keep copy of error code. // hr2 = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Info [%#x]: CryptMsgControl() failed to decrypt.\n", hr2); } if (bReleaseContext) { ::ReleaseContext(hCryptProv), hCryptProv = NULL; } } ::CertFreeCertificateContext(pCertContext), pCertContext = NULL; } } // // Did we find the recipient? // if (dwIndex == dwNumRecipients) { // // Retrieve previous error if any. // if (FAILED(hr2)) { hr = hr2; DebugTrace("Error [%#x]: CryptMsgControl() failed to decrypt.\n", hr); } else { hr = CAPICOM_E_ENVELOP_RECIPIENT_NOT_FOUND; DebugTrace("Error [%#x]: recipient not found.\n", hr); } goto ErrorExit; } } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resource. // if (hCryptProv && bReleaseContext) { ::ReleaseContext(hCryptProv); } if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } if(hMsg) { ::CryptMsgClose(hMsg); } if (hCertStores[0]) { ::CertCloseStore(hCertStores[0], 0); } if (hCertStores[1]) { ::CertCloseStore(hCertStores[1], 0); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEnvelopedData::Decrypt().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resources. // if (ContentBlob.pbData) { ::CoTaskMemFree(ContentBlob.pbData); } ReportError(hr); goto UnlockExit; } //////////////////////////////////////////////////////////////////////////////// // // Private member functions. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::Init Synopsis : Initialize the object. Parameter: None. 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 CEnvelopedData::Init () { HRESULT hr = S_OK; CComPtr pIAlgorithm = NULL; CComPtr pIRecipients = NULL; DebugTrace("Entering CEnvelopedData::Init().\n"); // // Create embeded IAlgorithm. // if (FAILED(hr = ::CreateAlgorithmObject(FALSE, FALSE, &pIAlgorithm))) { DebugTrace("Error [%#x]: CreateAlgorithmObject() failed.\n", hr); goto CommonExit; } // // Create embeded IRecipients. // if (FAILED(hr = ::CreateRecipientsObject(&pIRecipients))) { DebugTrace("Error [%#x]: CreateRecipientsObject() failed.\n", hr); goto CommonExit; } // // Update member variables. // m_bEnveloped = FALSE; m_ContentBlob.cbData = 0; m_ContentBlob.pbData = NULL; m_pIAlgorithm = pIAlgorithm; m_pIRecipients = pIRecipients; CommonExit: DebugTrace("Leaving CEnvelopedData::Init().\n"); return hr; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::OpenToEncode Synopsis : Create and initialize an envelop message for encoding. Parameter: HCRYPTMSG * phMsg - Pointer to HCRYPTMSG to receive message handler. HCRYPTPROV * phCryptProv - Pointer to HCRYPTPROV to receive CSP handler. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEnvelopedData::OpenToEncode (HCRYPTMSG * phMsg, HCRYPTPROV * phCryptProv) { HRESULT hr = S_OK; DWORD dwNumRecipients = 0; PCCERT_CONTEXT * pCertContexts = NULL; PCERT_INFO * pCertInfos = NULL; HCRYPTPROV hCryptProv = NULL; void * pEncryptionAuxInfo = NULL; CAPICOM_STORE_INFO StoreInfo = {0, L"AddressBook"}; CComPtr pIRecipient = NULL; CComPtr pICertificate2 = NULL; DWORD dwIndex; CAPICOM_ENCRYPTION_ALGORITHM AlgoName; CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength; CMSG_ENVELOPED_ENCODE_INFO EnvelopInfo; CRYPT_ALGORITHM_IDENTIFIER EncryptionAlgorithm; DebugTrace("Entering CEnvelopedData::OpenToEncode().\n"); // // Sanity check. // ATLASSERT(phMsg); ATLASSERT(phCryptProv); ATLASSERT(m_ContentBlob.cbData && m_ContentBlob.pbData); ATLASSERT(m_pIRecipients); // // Initialize. // ::ZeroMemory(&EnvelopInfo, sizeof(EnvelopInfo)); ::ZeroMemory(&EncryptionAlgorithm, sizeof(EncryptionAlgorithm)); // // Make sure we do have at least 1 recipient. // if (FAILED(hr = m_pIRecipients->get_Count((long *) &dwNumRecipients))) { DebugTrace("Error [%#x]: m_pIRecipients->get_Count() failed.\n", hr); goto ErrorExit; } if (0 == dwNumRecipients) { // // Prompt user to add a recipient. // if (FAILED(hr = ::SelectCertificate(StoreInfo, SelectRecipientCertCallback, &pICertificate2))) { if (hr == CAPICOM_E_STORE_EMPTY) { hr = CAPICOM_E_ENVELOP_NO_RECIPIENT; } DebugTrace("Error [%#x]: SelectCertificate() failed.\n", hr); goto ErrorExit; } if (FAILED(hr = pICertificate2->QueryInterface(__uuidof(ICertificate), (void **) &pIRecipient))) { DebugTrace("Unexpected error [%#x]: pICertificate2->QueryInterface() failed.\n", hr); goto ErrorExit; } // // Add to collection. // if (FAILED (hr = m_pIRecipients->Add(pIRecipient))) { DebugTrace("Error [%#x]: m_pIRecipients->Add() failed.\n", hr); goto ErrorExit; } // // Make sure count is 1. // if (FAILED(hr = m_pIRecipients->get_Count((long *) &dwNumRecipients))) { DebugTrace("Error [%#x]: m_pIRecipients->get_Count() failed.\n", hr); goto ErrorExit; } // // Sanity check. // ATLASSERT(1 == dwNumRecipients); } // // Allocate memory for CERT_CONTEXT array. // if (!(pCertContexts = (PCCERT_CONTEXT *) ::CoTaskMemAlloc(sizeof(PCCERT_CONTEXT) * dwNumRecipients))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } ::ZeroMemory(pCertContexts, sizeof(PCCERT_CONTEXT) * dwNumRecipients); // // Allocate memory for CERT_INFO array. // if (!(pCertInfos = (PCERT_INFO *) ::CoTaskMemAlloc(sizeof(PCERT_INFO) * dwNumRecipients))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } ::ZeroMemory(pCertInfos, sizeof(PCERT_INFO) * dwNumRecipients); // // Set CERT_INFO array. // for (dwIndex = 0; dwIndex < dwNumRecipients; dwIndex++) { CComVariant varRecipient; CComPtr pIRecipient2 = NULL; // // Get next recipient. // if (FAILED(hr = m_pIRecipients->get_Item((long) (dwIndex + 1), &varRecipient))) { DebugTrace("Error [%#x]: m_pIRecipients->get_Item() failed.\n", hr); goto ErrorExit; } // // Get custom interface. // if (FAILED(hr = varRecipient.pdispVal->QueryInterface(IID_ICertificate, (void **) &pIRecipient2))) { DebugTrace("Error [%#x]: varRecipient.pdispVal->QueryInterface() failed.\n", hr); goto ErrorExit; } // // Get CERT_CONTEXT. // if (FAILED(hr = ::GetCertContext(pIRecipient2, &pCertContexts[dwIndex]))) { DebugTrace("Error [%#x]: GetCertContext() failed.\n", hr); goto ErrorExit; } // // Set CERT_INFO. // pCertInfos[dwIndex] = pCertContexts[dwIndex]->pCertInfo; } // // Get algorithm ID enum name. // if (FAILED(hr = m_pIAlgorithm->get_Name(&AlgoName))) { DebugTrace("Error [%#x]: m_pIAlgorithm->get_Name() failed.\n", hr); goto ErrorExit; } // // Get key length enum name. // if (FAILED(hr = m_pIAlgorithm->get_KeyLength(&KeyLength))) { DebugTrace("Error [%#x]: m_pIAlgorithm->get_KeyLength() failed.\n", hr); goto ErrorExit; } // // Get CSP context. // if (FAILED(hr = ::AcquireContext(AlgoName, KeyLength, &hCryptProv))) { DebugTrace("Error [%#x]: AcquireContext() failed.\n", hr); goto ErrorExit; } // // Set algorithm. // if (FAILED(hr = ::SetEncryptionAlgorithm(AlgoName, KeyLength, &EncryptionAlgorithm))) { DebugTrace("Error [%#x]: SetEncryptionAlgorithm() failed.\n", hr); goto ErrorExit; } // // Set key length. // if (FAILED(hr = ::SetKeyLength(hCryptProv, EncryptionAlgorithm, KeyLength, &pEncryptionAuxInfo))) { DebugTrace("Error [%#x]: SetKeyLength() failed.\n", hr); goto ErrorExit; } // // Set CMSG_ENVELOPED_ENCODE_INFO. // EnvelopInfo.cbSize = sizeof(EnvelopInfo); EnvelopInfo.ContentEncryptionAlgorithm = EncryptionAlgorithm; EnvelopInfo.hCryptProv = hCryptProv; EnvelopInfo.cRecipients = dwNumRecipients; EnvelopInfo.rgpRecipients = pCertInfos; EnvelopInfo.pvEncryptionAuxInfo = pEncryptionAuxInfo; // // Open the message for encoding. // if(!(*phMsg = ::CryptMsgOpenToEncode(CAPICOM_ASN_ENCODING, // ASN encoding type 0, // Flags CMSG_ENVELOPED, // Message type &EnvelopInfo, // Pointer to structure NULL, // Inner content OID NULL))) // Stream information (not used) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgOpenToEncode() failed.\n", hr); goto ErrorExit; } // // Return HCRYPTPROV to caller. // *phCryptProv = hCryptProv; CommonExit: // // Free resource. // if (pEncryptionAuxInfo) { ::CoTaskMemFree(pEncryptionAuxInfo); } if (EncryptionAlgorithm.pszObjId) { ::CoTaskMemFree(EncryptionAlgorithm.pszObjId); } if (EncryptionAlgorithm.Parameters.pbData) { ::CoTaskMemFree(EncryptionAlgorithm.Parameters.pbData); } if (pCertInfos) { ::CoTaskMemFree(pCertInfos); } if (pCertContexts) { for (dwIndex = 0; dwIndex < dwNumRecipients; dwIndex++) { if (pCertContexts[dwIndex]) { ::CertFreeCertificateContext(pCertContexts[dwIndex]); } } ::CoTaskMemFree(pCertContexts); } DebugTrace("Leaving CEnvelopedData::OpenToEncode().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (hCryptProv) { ::ReleaseContext(hCryptProv); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEnvelopedData::OpenToDeccode Synopsis : Open an enveloped message for decoding. Parameter: HCRYPTPROV hCryptProv - CSP handle. BSTR EnvelopMessage - Enveloped message. HCRYPTMSG * phMsg - Pointer to HCRYPTMSG. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEnvelopedData::OpenToDecode (HCRYPTPROV hCryptProv, BSTR EnvelopedMessage, HCRYPTMSG * phMsg) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; DWORD dwMsgType = 0; DWORD cbMsgType = sizeof(dwMsgType); DATA_BLOB MessageBlob = {0, NULL}; DATA_BLOB AlgorithmBlob = {0, NULL}; DATA_BLOB ParametersBlob = {0, NULL}; DebugTrace("Leaving CEnvelopedData::OpenToDecode().\n"); // Sanity check. // ATLASSERT(phMsg); ATLASSERT(EnvelopedMessage); try { // // Open the message for decoding. // if (!(hMsg = ::CryptMsgOpenToDecode(CAPICOM_ASN_ENCODING, 0, 0, hCryptProv, NULL, NULL))) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgOpenToDecode() failed.\n", hr); goto CommonExit; } // // Import the message. // if (FAILED(hr = ::ImportData(EnvelopedMessage, CAPICOM_ENCODE_ANY, &MessageBlob))) { DebugTrace("Error [%#x]: ImportData() failed.\n", hr); goto ErrorExit; } // // Update message with enveloped content. // if (!::CryptMsgUpdate(hMsg, MessageBlob.pbData, MessageBlob.cbData, TRUE)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgUpdate() failed.\n", hr); goto ErrorExit; } // // Check message type. // if (!::CryptMsgGetParam(hMsg, CMSG_TYPE_PARAM, 0, (void *) &dwMsgType, &cbMsgType)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgGetParam() failed for CMSG_TYPE_PARAM.\n", hr); goto ErrorExit; } if (CMSG_ENVELOPED != dwMsgType) { hr = CAPICOM_E_ENVELOP_INVALID_TYPE; DebugTrace("Error [%#x]: Enveloped message's dwMsgType (%#x) is not CMSG_ENVELOPED.\n", hr, dwMsgType); goto ErrorExit; } // // Get algorithm ID. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_ENVELOPE_ALGORITHM_PARAM, 0, (void **) &AlgorithmBlob.pbData, &AlgorithmBlob.cbData))) { DebugTrace("Error [%#x]: GetMsgParam() failed for CMSG_ENVELOPE_ALGORITHM_PARAM.\n", hr); goto ErrorExit; } // // Restore encryption algorithm state, as much as we can. // if (0 == lstrcmpA(szOID_RSA_RC2CBC, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->pszObjId)) { CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength; DebugTrace("INFO: Envelop encryption algorithm was RC2.\n"); // // Set encryption algorithm name. // if (FAILED(hr = m_pIAlgorithm->put_Name(CAPICOM_ENCRYPTION_ALGORITHM_RC2))) { DebugTrace("Error [%#x]: m_pIAlgorithm->put_Name() failed.\n", hr); goto ErrorExit; } // // Determine encryption key length. // if (((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->Parameters.cbData) { if (FAILED(hr = ::DecodeObject(PKCS_RC2_CBC_PARAMETERS, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->Parameters.pbData, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->Parameters.cbData, &ParametersBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed for PKCS_RC2_CBC_PARAMETERS.\n", hr); goto ErrorExit; } } // // Set encryption key length. // switch (((CRYPT_RC2_CBC_PARAMETERS *) ParametersBlob.pbData)->dwVersion) { case CRYPT_RC2_40BIT_VERSION: { KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_40_BITS; DebugTrace("INFO: Envelop encryption key length was 40-bits.\n"); break; } case CRYPT_RC2_56BIT_VERSION: { KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_56_BITS; DebugTrace("INFO: Envelop encryption key length was 56-bits.\n"); break; } case CRYPT_RC2_128BIT_VERSION: { KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS; DebugTrace("INFO: Envelop encryption key length was 128-bits.\n"); break; } default: { // // Unknown key length, so arbitrary choose one. // KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_MAXIMUM; DebugTrace("INFO: Unknown envelop encryption key length.\n"); break; } } // // Set key length. // if (FAILED(hr = m_pIAlgorithm->put_KeyLength(KeyLength))) { DebugTrace("Error [%#x]: m_pIAlgorithm->put_KeyLength() failed.\n", hr); goto ErrorExit; } } else if (0 == lstrcmpA(szOID_RSA_RC4, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->pszObjId)) { DebugTrace("INFO: Envelop encryption algorithm was RC4.\n"); // // Set encryption algorithm name. // if (FAILED(hr = m_pIAlgorithm->put_Name(CAPICOM_ENCRYPTION_ALGORITHM_RC4))) { DebugTrace("Error [%#x]: m_pIAlgorithm->put_Name() failed.\n", hr); goto ErrorExit; } // // For RC4, CAPI simply does not provide a way to retrieve the // encryption key length, so we will have to leave it alone!!! // } else if (0 == lstrcmpA(szOID_OIWSEC_desCBC, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->pszObjId)) { DebugTrace("INFO: Envelop encryption algorithm was DES.\n"); // // Set encryption algorithm name. // if (FAILED(hr = m_pIAlgorithm->put_Name(CAPICOM_ENCRYPTION_ALGORITHM_DES))) { DebugTrace("Error [%#x]: m_pIAlgorithm->put_Name() failed.\n", hr); goto ErrorExit; } // // For DES, key length is fixed at 56, and should be ignored. // } else if (0 == lstrcmpA(szOID_RSA_DES_EDE3_CBC, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->pszObjId)) { DebugTrace("INFO: Envelop encryption algorithm was 3DES.\n"); // // Set encryption algorithm name. // if (FAILED(hr = m_pIAlgorithm->put_Name(CAPICOM_ENCRYPTION_ALGORITHM_3DES))) { DebugTrace("Error [%#x]: m_pIAlgorithm->put_Name() failed.\n", hr); goto ErrorExit; } // // For 3DES, key length is fixed at 168, and should be ignored. // } else { DebugTrace("INFO: Unknown envelop encryption algorithm.\n"); } // // Return msg handler to caller. // *phMsg = hMsg; } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } CommonExit: // // Free resource. // if (ParametersBlob.pbData) { ::CoTaskMemFree(ParametersBlob.pbData); } if (AlgorithmBlob.pbData) { ::CoTaskMemFree(AlgorithmBlob.pbData); } if (MessageBlob.pbData) { ::CoTaskMemFree(MessageBlob.pbData); } DebugTrace("Leaving CEnvelopedData::OpenToDecode().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); if (hMsg) { ::CryptMsgClose(hMsg); } goto CommonExit; }