/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Microsoft Windows, Copyright (C) Microsoft Corporation, 2000 File: EncryptedData.cpp Content: Implementation of CEncryptedData. History: 11-15-99 dsie created ------------------------------------------------------------------------------*/ // // Turn off: // // - Unreferenced formal parameter warning. // - Assignment within conditional expression warning. // #pragma warning (disable: 4100) #pragma warning (disable: 4706) #include "stdafx.h" #include "CAPICOM.h" #include "EncryptedData.h" #include "Convert.h" #include "Common.h" /////////////////////////////////////////////////////////////////////////////// // // Local functions. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : DeriveKey Synopsis : Derive a session key. Parameter: HCRYPTPROV hCryptProv - CSP handler. ALG_ID AlgID - Encryption algorithm ID. DWORD dwKeyLength - Key length. DATA_BLOB SecretBlob - Secret blob. DATA_BLOB SaltBlob - Salt blob. HCRYPTKEY * phKey - Pointer to HCRYPTKEY to receive session key. Remark : ------------------------------------------------------------------------------*/ static HRESULT DeriveKey (HCRYPTPROV hCryptProv, ALG_ID AlgID, DWORD dwKeyLength, DATA_BLOB SecretBlob, DATA_BLOB SaltBlob, HCRYPTKEY * phKey) { HRESULT hr = S_OK; HCRYPTHASH hHash = NULL; HCRYPTKEY hKey = NULL; DWORD dwFlags = CRYPT_EXPORTABLE | CRYPT_NO_SALT; DebugTrace("Entering DeriveKey().\n"); // // Sanity check. // ATLASSERT(hCryptProv); ATLASSERT(AlgID); ATLASSERT(SecretBlob.cbData); ATLASSERT(SecretBlob.pbData); ATLASSERT(SaltBlob.cbData); ATLASSERT(SaltBlob.pbData); ATLASSERT(phKey); // // Create a hash object. // if (!::CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: ::CryptCreateHash() failed.\n", hr); goto ErrorExit; } // // Hash in the password data. // if(!::CryptHashData(hHash, SecretBlob.pbData, SecretBlob.cbData, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: ::CryptHashData() failed.\n", hr); goto ErrorExit; } // // Hash in the salt. // if(!::CryptHashData(hHash, SaltBlob.pbData, SaltBlob.cbData, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: ::CryptHashData() failed.\n", hr); goto ErrorExit; } // // Set key length. // if (CALG_RC2 == AlgID || CALG_RC4 == AlgID) { dwFlags |= dwKeyLength << 16; } // // Derive a session key from the hash object. // if (!::CryptDeriveKey(hCryptProv, AlgID, hHash, dwFlags, &hKey)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: ::CryptDeriveKey() failed.\n", hr); goto ErrorExit; } // // Return session key to caller. // *phKey = hKey; CommonExit: // // Free resource. // if (hHash) { ::CryptDestroyHash(hHash); } DebugTrace("Leaving DeriveKey().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (hKey) { ::CryptDestroyKey(hKey); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : EncodeEncryptedData Synopsis : ASN.1 encode the cipher blob. Parameter: HCRYPTKEY hKey - Session key used to encrypt the data. DATA_BLOB SaltBlob - Salt blob. DATA_BLOB CipherBlob - Cipher blob. DATA_BLOB * pEncodedBlob - Pointer to DATA_BLOB to receive the ASN.1 encoded blob. Remark : The format is proprietory, and should not be documented. It is right now encoded as a PKCS_CONTENT_INFO_SEQUENCE_OF_ANY with proprietory OID. ------------------------------------------------------------------------------*/ static HRESULT EncodeEncryptedData (HCRYPTKEY hKey, DATA_BLOB SaltBlob, DATA_BLOB CipherBlob, DATA_BLOB * pEncodedBlob) { HRESULT hr = S_OK; DWORD dwCAPICOMVersion = CAPICOM_VERSION; DATA_BLOB KeyParamBlob[3] = {{0, NULL}, {0, NULL}, {0, NULL}}; DWORD i; CAPICOM_ENCTYPTED_DATA_INFO EncryptedDataInfo; CRYPT_CONTENT_INFO_SEQUENCE_OF_ANY EncryptedDataFormat; CRYPT_CONTENT_INFO ContentInfo; CRYPT_DER_BLOB ContentBlob = {0, NULL}; DebugTrace("Entering EncodeEncryptedData().\n"); // // Sanity check. // ATLASSERT(hKey); ATLASSERT(pEncodedBlob); // // Initialize. // ::ZeroMemory(pEncodedBlob, sizeof(DATA_BLOB)); ::ZeroMemory(&ContentInfo, sizeof(ContentInfo)); ::ZeroMemory(&EncryptedDataInfo, sizeof(EncryptedDataInfo)); ::ZeroMemory(&EncryptedDataFormat, sizeof(EncryptedDataFormat)); // // Encode the version number. // if (FAILED(hr = ::EncodeObject(X509_INTEGER, &dwCAPICOMVersion, &EncryptedDataInfo.VersionBlob))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } // // Encode ALG_ID. // if (FAILED(hr = ::GetKeyParam(hKey, KP_ALGID, &KeyParamBlob[0].pbData, &KeyParamBlob[0].cbData))) { DebugTrace("Error [%#x]: GetKeyParam() failed for KP_ALGID.\n", hr); goto ErrorExit; } if (FAILED(hr = ::EncodeObject(X509_INTEGER, KeyParamBlob[0].pbData, &EncryptedDataInfo.AlgIDBlob))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } // // Encode key length. // if (FAILED(hr = ::GetKeyParam(hKey, KP_KEYLEN, &KeyParamBlob[1].pbData, &KeyParamBlob[1].cbData))) { DebugTrace("Error [%#x]: GetKeyParam() failed for KP_KEYLEN.\n", hr); goto ErrorExit; } if (FAILED(hr = ::EncodeObject(X509_INTEGER, KeyParamBlob[1].pbData, &EncryptedDataInfo.KeyLengthBlob))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } // // Encode IV value. // if (FAILED(hr = ::GetKeyParam(hKey, KP_IV, &KeyParamBlob[2].pbData, &KeyParamBlob[2].cbData))) { DebugTrace("Error [%#x]: GetKeyParam() failed for KP_IV.\n", hr); goto ErrorExit; } if (FAILED(hr = ::EncodeObject(X509_OCTET_STRING, &KeyParamBlob[2], &EncryptedDataInfo.IVBlob))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } // // Encode salt value. // if (FAILED(hr = ::EncodeObject(X509_OCTET_STRING, &SaltBlob, &EncryptedDataInfo.SaltBlob))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } // // Encode the cipher text. // if (FAILED(hr = ::EncodeObject(X509_OCTET_STRING, &CipherBlob, &EncryptedDataInfo.CipherBlob))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } // // Encode the entire content as PKCS_CONTENT_INFO_SEQUENCE_OF_ANY. // EncryptedDataFormat.pszObjId = szOID_CAPICOM_ENCRYPTED_CONTENT; EncryptedDataFormat.cValue = 6; EncryptedDataFormat.rgValue = (DATA_BLOB *) &EncryptedDataInfo; if (FAILED(hr = ::EncodeObject(PKCS_CONTENT_INFO_SEQUENCE_OF_ANY, &EncryptedDataFormat, &ContentBlob))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } // // Finally, wrap the entire encrypted content in CONTENT_INFO. // ContentInfo.pszObjId = szOID_CAPICOM_ENCRYPTED_DATA; ContentInfo.Content = ContentBlob; if (FAILED(hr = ::EncodeObject(PKCS_CONTENT_INFO, &ContentInfo, pEncodedBlob))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } CommonExit: // // Free resource. // for (i = 0; i < 3; i++) { if (KeyParamBlob[i].pbData) { ::CoTaskMemFree(KeyParamBlob[i].pbData); } } if (EncryptedDataInfo.VersionBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.VersionBlob.pbData); } if (EncryptedDataInfo.AlgIDBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.AlgIDBlob.pbData); } if (EncryptedDataInfo.KeyLengthBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.KeyLengthBlob.pbData); } if (EncryptedDataInfo.IVBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.IVBlob.pbData); } if (EncryptedDataInfo.SaltBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.SaltBlob.pbData); } if (EncryptedDataInfo.CipherBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.CipherBlob.pbData); } if (ContentBlob.pbData) { ::CoTaskMemFree(ContentBlob.pbData); } DebugTrace("Leaving EncodeEncryptedData().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : DecodeEncryptedData Synopsis : ASN.1 decode the cipher text blob. Parameter: DATA_BLOB EncodedBlob - Encoded cipher blob. CAPICOM_ENCTYPTED_DATA_INFO * pEncryptedDataInfo - Pointer to structure to receive decoded structure. Remark : ------------------------------------------------------------------------------*/ static HRESULT DecodeEncryptedData (DATA_BLOB EncodedBlob, CAPICOM_ENCTYPTED_DATA_INFO * pEncryptedDataInfo) { HRESULT hr = S_OK; DATA_BLOB ContentInfoBlob = {0, NULL}; DATA_BLOB EncryptedBlob = {0, NULL}; CRYPT_CONTENT_INFO ContentInfo; CAPICOM_ENCTYPTED_DATA_INFO EncryptedDataInfo; CRYPT_CONTENT_INFO_SEQUENCE_OF_ANY * pEncryptedDataFormat = NULL; DebugTrace("Entering DecodeEncryptedData().\n"); // // Sanity check. // ATLASSERT(pEncryptedDataInfo); // // Initialize. // ::ZeroMemory(&ContentInfo, sizeof(ContentInfo)); ::ZeroMemory(&EncryptedDataInfo, sizeof(EncryptedDataInfo)); // // Decode the CONTENT_INFO. // if (FAILED(hr = ::DecodeObject(PKCS_CONTENT_INFO, EncodedBlob.pbData, EncodedBlob.cbData, &ContentInfoBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed.\n", hr); goto ErrorExit; } ContentInfo = * ((CRYPT_CONTENT_INFO *) ContentInfoBlob.pbData); // // Make sure this is our CONTENT_INFO. // if (0 != ::lstrcmpA(szOID_CAPICOM_ENCRYPTED_DATA, ContentInfo.pszObjId)) { DebugTrace("Error [%#x]: not a CAPICOM encrypted data.\n", hr); goto ErrorExit; } // // Decode the content blob. // if (FAILED(hr = ::DecodeObject(PKCS_CONTENT_INFO_SEQUENCE_OF_ANY, ContentInfo.Content.pbData, ContentInfo.Content.cbData, &EncryptedBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed.\n", hr); goto ErrorExit; } pEncryptedDataFormat = (CRYPT_CONTENT_INFO_SEQUENCE_OF_ANY *) EncryptedBlob.pbData; // // Make sure it is the right format. // if (0 != ::lstrcmpA(szOID_CAPICOM_ENCRYPTED_CONTENT, pEncryptedDataFormat->pszObjId)) { hr = CAPICOM_E_ENCRYPT_INVALID_TYPE; DebugTrace("Error: not a CAPICOM encrypted content.\n"); goto ErrorExit; } // // Sanity check. // ATLASSERT(6 == pEncryptedDataFormat->cValue); // // Decode version. // if (FAILED(hr = ::DecodeObject(X509_INTEGER, pEncryptedDataFormat->rgValue[0].pbData, pEncryptedDataFormat->rgValue[0].cbData, &EncryptedDataInfo.VersionBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed.\n", hr); goto ErrorExit; } // // Decode ALG_ID. // if (FAILED(hr = ::DecodeObject(X509_INTEGER, pEncryptedDataFormat->rgValue[1].pbData, pEncryptedDataFormat->rgValue[1].cbData, &EncryptedDataInfo.AlgIDBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed.\n", hr); goto ErrorExit; } // // Decode key length. // if (FAILED(hr = ::DecodeObject(X509_INTEGER, pEncryptedDataFormat->rgValue[2].pbData, pEncryptedDataFormat->rgValue[2].cbData, &EncryptedDataInfo.KeyLengthBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed.\n", hr); goto ErrorExit; } // // Decode IV value. // if (FAILED(hr = ::DecodeObject(X509_OCTET_STRING, pEncryptedDataFormat->rgValue[3].pbData, pEncryptedDataFormat->rgValue[3].cbData, &EncryptedDataInfo.IVBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed.\n", hr); goto ErrorExit; } // // Decode salt value. // if (FAILED(hr = ::DecodeObject(X509_OCTET_STRING, pEncryptedDataFormat->rgValue[4].pbData, pEncryptedDataFormat->rgValue[4].cbData, &EncryptedDataInfo.SaltBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed.\n", hr); goto ErrorExit; } // // Decode cipher text. // if (FAILED(hr = ::DecodeObject(X509_OCTET_STRING, pEncryptedDataFormat->rgValue[5].pbData, pEncryptedDataFormat->rgValue[5].cbData, &EncryptedDataInfo.CipherBlob))) { DebugTrace("Error [%#x]: DecodeObject() failed.\n", hr); goto ErrorExit; } // // Return decoded encrypted data to caller. // *pEncryptedDataInfo = EncryptedDataInfo; CommonExit: // // Free resource. // if (EncryptedBlob.pbData) { ::CoTaskMemFree(EncryptedBlob.pbData); } if (ContentInfoBlob.pbData) { ::CoTaskMemFree(ContentInfoBlob.pbData); } DebugTrace("Leaving DecodeEncryptedData().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (EncryptedDataInfo.VersionBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.VersionBlob.pbData); } if (EncryptedDataInfo.AlgIDBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.AlgIDBlob.pbData); } if (EncryptedDataInfo.KeyLengthBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.KeyLengthBlob.pbData); } if (EncryptedDataInfo.IVBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.IVBlob.pbData); } if (EncryptedDataInfo.SaltBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.SaltBlob.pbData); } if (EncryptedDataInfo.CipherBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.CipherBlob.pbData); } goto CommonExit; } /////////////////////////////////////////////////////////////////////////////// // // CEncryptedData // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::get_Content Synopsis : Return the content. Parameter: BSTR * pVal - Pointer to BSTR to receive the content. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::get_Content (BSTR * pVal) { HRESULT hr = S_OK; DebugTrace("Entering CEncryptedData::get_Content().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Make sure parameter is valid. // if (NULL == pVal) { hr = E_POINTER; DebugTrace("Error: invalid parameter, pVal is NULL.\n"); goto ErrorExit; } // // Make sure content is already initialized. // if (0 == m_ContentBlob.cbData) { hr = CAPICOM_E_ENCRYPT_NOT_INITIALIZED; DebugTrace("Error: encrypt object has not been initialized.\n"); 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 CEncryptedData::get_Content().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::put_Content Synopsis : Initialize the object with content to be encrypted. Parameter: BSTR newVal - BSTR containing the content to be encrypted. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::put_Content (BSTR newVal) { HRESULT hr = S_OK; DebugTrace("Entering CEncryptedData::put_Content().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Reset member variables. // if (m_ContentBlob.pbData) { ::CoTaskMemFree(m_ContentBlob.pbData); } m_ContentBlob.cbData = 0; m_ContentBlob.pbData = NULL; // // Make sure parameters are valid. // if (NULL == newVal) { hr = E_POINTER; DebugTrace("Error: invalid parameter, newVal is NULL.\n"); goto ErrorExit; } if (0 == ::SysStringByteLen(newVal)) { hr = E_INVALIDARG; DebugTrace("Error: invalid parameter, newVal is empty.\n"); 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 CEncryptedData::put_Content().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::get_Algorithm Synopsis : Property to return the algorithm object. Parameter: IAlgorithm ** pVal - Pointer to pointer to IAlgorithm to receive the interfcae pointer. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::get_Algorithm (IAlgorithm ** pVal) { HRESULT hr = S_OK; DebugTrace("Entering CEncryptedData::get_Algorithm().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // 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 CEncryptedData::get_Algorithm().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::SetSecret Synopsis : Set the encryption secret used to generated the session key. Parameter: BSTR newVal - The secret. CAPICOM_SECRET_TYPE SecretType - Secret type, which can be: SECRET_PASSWORD = 0 Remark : For v1.0, we only support password secret. But, we really need to consider plain text session key (See Q228786), as this is one of the frequently asked question on the public list server. ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::SetSecret (BSTR newVal, CAPICOM_SECRET_TYPE SecretType) { HRESULT hr = S_OK; DebugTrace("Entering CEncryptedData::SetSecret().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Make sure parameters are valid. // DWORD dwCharLen = ::SysStringLen(newVal); if (0 == dwCharLen || 256 < dwCharLen) { hr = E_INVALIDARG; DebugTrace("Error: invalid secret length.\n"); goto ErrorExit; } // // Determine secret type. // switch (SecretType) { case CAPICOM_SECRET_PASSWORD: { m_SecretType = SecretType; break; } default: { hr = E_INVALIDARG; DebugTrace("Error: invalid parameter, unknown secret type.\n"); goto ErrorExit; } } // // Initialize secret. // m_bstrSecret = newVal; } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEncryptedData::SetSecret().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::Encrypt Synopsis : Encrypt the content. Parameter: CAPICOM_ENCODING_TYPE EncodingType - Encoding type. BSTR * pVal - Pointer to BSTR to receive the encrypted message. Remark : Note that since CAPI still does not support PKCS 7 EncryptedData type, therefore, the format of the encrypted data used here is propriety, and should not be documented. ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::Encrypt (CAPICOM_ENCODING_TYPE EncodingType, BSTR * pVal) { HRESULT hr = S_OK; HCRYPTPROV hCryptProv = NULL; HCRYPTKEY hSessionKey = NULL; DWORD dwBufLength = 0; DATA_BLOB SaltBlob = {0, NULL}; DATA_BLOB CipherBlob = {0, NULL}; DATA_BLOB MessageBlob = {0, NULL}; DebugTrace("Entering CEncryptedData::Encrypt().\n"); try { // // Lock access to this object. // m_Lock.Lock(); // // Make sure parameters are valid. // if (NULL == pVal) { hr = E_POINTER; DebugTrace("Error: invalid parameter, pVal is NULL.\n"); goto ErrorExit; } // // Make sure we do have content to encrypt. // if (!m_ContentBlob.cbData) { hr = CAPICOM_E_ENCRYPT_NOT_INITIALIZED; DebugTrace("Error: encrypt object has not been initialized.\n"); goto ErrorExit; } if (!m_bstrSecret) { hr = CAPICOM_E_ENCRYPT_NO_SECRET; DebugTrace("Error: secret has not been set.\n"); goto ErrorExit; } // // Open a new message to encode. // if (FAILED(hr = OpenToEncode(&SaltBlob, &hCryptProv, &hSessionKey))) { DebugTrace("Error [%#x]: CEncryptedData::OpenToEncode() failed.\n", hr); goto ErrorExit; } // // Determine buffer length. // dwBufLength = m_ContentBlob.cbData; if (!::CryptEncrypt(hSessionKey, NULL, TRUE, 0, NULL, &dwBufLength, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptEncrypt() failed.\n", hr); goto ErrorExit; } // // Sanity check. // ATLASSERT(m_ContentBlob.cbData <= dwBufLength); // // Copy clear text to another buffer. // if (!(CipherBlob.pbData = (PBYTE) ::CoTaskMemAlloc(dwBufLength))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } CipherBlob.cbData = dwBufLength; ::CopyMemory(CipherBlob.pbData, m_ContentBlob.pbData, m_ContentBlob.cbData); // // Encrypt. // dwBufLength = m_ContentBlob.cbData; if (!::CryptEncrypt(hSessionKey, NULL, TRUE, 0, CipherBlob.pbData, &dwBufLength, CipherBlob.cbData)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptEncrypt() failed.\n", hr); goto ErrorExit; } // // Encode the cipher text. // if (FAILED(hr = ::EncodeEncryptedData(hSessionKey, SaltBlob, CipherBlob, &MessageBlob))) { DebugTrace("Error [%#x]: Encode() failed.\n", hr); goto ErrorExit; } // // Now export the encoded 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("Encrypted.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 (CipherBlob.pbData) { ::CoTaskMemFree(CipherBlob.pbData); } if (hSessionKey) { ::CryptDestroyKey(hSessionKey); } if (hCryptProv) { ::ReleaseContext(hCryptProv); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEncryptedData::Encrypt().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::Decrypt Synopsis : Decrypt the encrypted content. Parameter: BSTR EncryptedMessage - BSTR containing the encrypted message. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::Decrypt (BSTR EncryptedMessage) { HRESULT hr = S_OK; HCRYPTPROV hCryptProv = NULL; HCRYPTKEY hSessionKey = NULL; DATA_BLOB ContentBlob = {0, NULL}; CAPICOM_ENCTYPTED_DATA_INFO EncryptedDataInfo; CAPICOM_ENCRYPTION_ALGORITHM AlgoName; CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength; DebugTrace("Entering CEncryptedData::Decrypt().\n"); // // Initialize. // ::ZeroMemory(&EncryptedDataInfo, sizeof(CAPICOM_ENCTYPTED_DATA_INFO)); try { // // Lock access to this object. // m_Lock.Lock(); // // Reset member variables. // if (m_ContentBlob.pbData) { ::CoTaskMemFree(m_ContentBlob.pbData); } m_ContentBlob.cbData = 0; m_ContentBlob.pbData = NULL; // // Make sure parameters are valid. // if (NULL == EncryptedMessage ) { hr = E_POINTER; DebugTrace("Error: invalid parameter, EncryptedMessage is NULL.\n"); goto ErrorExit; } if (0 == ::SysStringByteLen(EncryptedMessage)) { hr = E_INVALIDARG; DebugTrace("Error: invalid parameter, EncryptedMessage is empty\n"); goto ErrorExit; } if (!m_bstrSecret) { hr = CAPICOM_E_ENCRYPT_NO_SECRET; DebugTrace("Error: secret has not been set.\n"); goto ErrorExit; } // // Open a new message to decode. // if (FAILED(hr = OpenToDecode(EncryptedMessage, &hCryptProv, &hSessionKey, &EncryptedDataInfo))) { DebugTrace("Error [%#x]: CEncryptedData::OpenToDecode() failed.\n", hr); goto ErrorExit; } // // Point to content blob. // ContentBlob = *((DATA_BLOB *) EncryptedDataInfo.CipherBlob.pbData); // // Decrypt. // if (!::CryptDecrypt(hSessionKey, NULL, TRUE, 0, ContentBlob.pbData, &ContentBlob.cbData)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptDecrypt() failed.\n", hr); goto ErrorExit; } // // Convert ALG_ID to CAPICOM_ENCRYPTION_ALGORITHM. // if (FAILED(::AlgIDToEnumName(*((ALG_ID *) EncryptedDataInfo.AlgIDBlob.pbData), &AlgoName))) { // // Default to RC2. // AlgoName = CAPICOM_ENCRYPTION_ALGORITHM_RC2; } // // Convert key length value to CAPICOM_ENCRYPTION_KEY_LENGTH. // if (FAILED(::KeyLengthToEnumName(*((DWORD *) EncryptedDataInfo.KeyLengthBlob.pbData), &KeyLength))) { // // Default to maximum. // KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_MAXIMUM; } // // Update member variables. // if (!(m_ContentBlob.pbData = (PBYTE) ::CoTaskMemAlloc(ContentBlob.cbData))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } m_ContentBlob.cbData = ContentBlob.cbData; ::CopyMemory(m_ContentBlob.pbData, ContentBlob.pbData, ContentBlob.cbData); m_pIAlgorithm->put_Name(AlgoName); m_pIAlgorithm->put_KeyLength(KeyLength); } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resource. // if (hSessionKey) { ::CryptDestroyKey(hSessionKey); } if (hCryptProv) { ::ReleaseContext(hCryptProv); } if (EncryptedDataInfo.VersionBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.VersionBlob.pbData); } if (EncryptedDataInfo.AlgIDBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.AlgIDBlob.pbData); } if (EncryptedDataInfo.KeyLengthBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.KeyLengthBlob.pbData); } if (EncryptedDataInfo.SaltBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.SaltBlob.pbData); } if (EncryptedDataInfo.IVBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.IVBlob.pbData); } if (EncryptedDataInfo.CipherBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.CipherBlob.pbData); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CEncryptedData::Decrypt().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } //////////////////////////////////////////////////////////////////////////////// // // Private member functions. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::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 CEncryptedData::Init() { HRESULT hr = S_OK; CComPtr pIAlgorithm = NULL; DebugTrace("Entering CEncryptedData::Init().\n"); // // Create embeded IAlgorithm. // if (FAILED(hr = ::CreateAlgorithmObject(&pIAlgorithm))) { DebugTrace("Error [%#x]: CreateAlgorithmObject() failed.\n", hr); goto CommonExit; } // // Update member variables. // m_ContentBlob.cbData = 0; m_ContentBlob.pbData = NULL; m_bstrSecret.Empty(); m_pIAlgorithm = pIAlgorithm; CommonExit: DebugTrace("Leaving CEncryptedData::Init().\n"); return hr; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::OpenToEncode Synopsis : Create and initialize an encrypt message for encoding. Parameter: DATA_BLOB * pSaltBlob - Pointer to DATA_BLOB to receive the salt value blob. HCRYPTPROV * phCryptProv - Pointer to HCRYPTPROV to receive CSP handler. HCRYPTKEY * phKey - Pointer to HCRYPTKEY to receive session key. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::OpenToEncode(DATA_BLOB * pSaltBlob, HCRYPTPROV * phCryptProv, HCRYPTKEY * phKey) { HRESULT hr = S_OK; HCRYPTPROV hCryptProv = NULL; HCRYPTKEY hKey = NULL; DATA_BLOB SaltBlob = {16, NULL}; CAPICOM_ENCRYPTION_ALGORITHM AlgoName; CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength; DebugTrace("Entering CEncryptedData::OpenToEncode().\n"); // // Sanity check. // ATLASSERT(pSaltBlob); ATLASSERT(phCryptProv); ATLASSERT(phKey); ATLASSERT(m_ContentBlob.cbData && m_ContentBlob.pbData); ATLASSERT(m_bstrSecret); // // Get algorithm 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; } // // Generate random salt. // if (!(SaltBlob.pbData = (BYTE *) ::CoTaskMemAlloc(SaltBlob.cbData))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } if (!::CryptGenRandom(hCryptProv, SaltBlob.cbData, SaltBlob.pbData)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptGenRandom() failed.\n", hr); goto ErrorExit; } // // Generate the session key. // if (FAILED(hr = GenerateKey(hCryptProv, AlgoName, KeyLength, SaltBlob, &hKey))) { DebugTrace("Error [%#x]: GenerateKey() failed.\n", hr); goto ErrorExit; } // // Set CMSG_ENCRYPTED_ENCODE_INFO. // *pSaltBlob = SaltBlob; *phCryptProv = hCryptProv; *phKey = hKey; CommonExit: DebugTrace("Leaving CEncryptedData::OpenToEncode().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (hKey) { ::CryptDestroyKey(hKey); } if (hCryptProv) { ::ReleaseContext(hCryptProv); } if (SaltBlob.pbData) { ::CoTaskMemFree(SaltBlob.pbData); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::OpenToDecode Synopsis : Open an encrypt message for decoding. Parameter: BSTR EncryptedMessage - BSTR containing the encrypted message. HCRYPTPROV * phCryptProv - Pointer to HCRYPTPROV to receive CSP handler. HCRYPTKEY * phKey - Pointer to HCRYPTKEY to receive session key. CAPICOM_ENCTYPTED_DATA_INFO * pEncryptedDataInfo; Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::OpenToDecode ( BSTR EncryptedMessage, HCRYPTPROV * phCryptProv, HCRYPTKEY * phKey, CAPICOM_ENCTYPTED_DATA_INFO * pEncryptedDataInfo) { HRESULT hr = S_OK; HCRYPTPROV hCryptProv = NULL; HCRYPTKEY hKey = NULL; ALG_ID AlgID = 0; DWORD dwKeyLength = 0; DATA_BLOB MessageBlob = {0, NULL}; DATA_BLOB SecretBlob = {m_bstrSecret.Length() * sizeof(WCHAR), (BYTE *) m_bstrSecret.m_str}; DATA_BLOB SaltBlob; DATA_BLOB IVBlob; CAPICOM_ENCTYPTED_DATA_INFO EncryptedDataInfo; DebugTrace("Entering CEncryptedData::OpenToDecode().\n"); // // Sanity check. // ATLASSERT(EncryptedMessage); ATLASSERT(phCryptProv); ATLASSERT(phKey); ATLASSERT(pEncryptedDataInfo); try { // // Initialize. // ::ZeroMemory(&EncryptedDataInfo, sizeof(EncryptedDataInfo)); // // Import the message. // if (FAILED(hr = ::ImportData(EncryptedMessage, &MessageBlob))) { DebugTrace("Error [%#x]: ImportData() failed.\n", hr); goto ErrorExit; } // // Decode the blob. // if (FAILED(hr = ::DecodeEncryptedData(MessageBlob, &EncryptedDataInfo))) { DebugTrace("Error [%#x]: DecodeEncryptedData() failed.\n", hr); goto ErrorExit; } // // Retrieve values. // AlgID = *((ALG_ID *) EncryptedDataInfo.AlgIDBlob.pbData); dwKeyLength = *((DWORD *) EncryptedDataInfo.KeyLengthBlob.pbData); SaltBlob = *((DATA_BLOB *) EncryptedDataInfo.SaltBlob.pbData); IVBlob = *((DATA_BLOB *) EncryptedDataInfo.IVBlob.pbData); // // Get CSP context. // if (FAILED(hr = ::AcquireContext(AlgID, dwKeyLength, &hCryptProv))) { DebugTrace("Error [%#x]: AcquireContext() failed.\n", hr); goto ErrorExit; } // // Derive the key. // if (FAILED(hr = ::DeriveKey(hCryptProv, AlgID, dwKeyLength, SecretBlob, SaltBlob, &hKey))) { DebugTrace("Error [%#x]: DeriveKey() failed.\n", hr); goto ErrorExit; } // // Set IV, if required. // if ((CALG_RC2 == AlgID) || (CALG_DES == AlgID) || (CALG_3DES == AlgID)) { // // Set IV. // if(IVBlob.cbData && !::CryptSetKeyParam(hKey, KP_IV, IVBlob.pbData, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptSetKeyParam() failed for KP_IV.\n", hr); goto ErrorExit; } } // // Return results to caller. // *phCryptProv = hCryptProv; *phKey = hKey; *pEncryptedDataInfo = EncryptedDataInfo; } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } CommonExit: // // Free resource. // if (MessageBlob.pbData) { ::CoTaskMemFree(MessageBlob.pbData); } DebugTrace("Leaving CEncryptedData::OpenToDecode().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (hKey) { ::CryptDestroyKey(hKey); } if (hCryptProv) { ::ReleaseContext(hCryptProv); } if (EncryptedDataInfo.VersionBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.VersionBlob.pbData); } if (EncryptedDataInfo.AlgIDBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.AlgIDBlob.pbData); } if (EncryptedDataInfo.KeyLengthBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.KeyLengthBlob.pbData); } if (EncryptedDataInfo.SaltBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.SaltBlob.pbData); } if (EncryptedDataInfo.IVBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.IVBlob.pbData); } if (EncryptedDataInfo.CipherBlob.pbData) { ::CoTaskMemFree(EncryptedDataInfo.CipherBlob.pbData); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CEncryptedData::GenerateKey Synopsis : Generate the session key. Parameter: HCRYPTPROV hCryptProv - CSP handler. CAPICOM_ENCRYPTION_ALGORITHM AlogName - Algo enum name. CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength - Key length enum name. DATA_BLOB SaltBlob - Salt blob. HCRYPTKEY * phKey - Pointer to HCRYPTKEY to receive session key. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CEncryptedData::GenerateKey ( HCRYPTPROV hCryptProv, CAPICOM_ENCRYPTION_ALGORITHM AlgoName, CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength, DATA_BLOB SaltBlob, HCRYPTKEY * phKey) { HRESULT hr = S_OK; HCRYPTKEY hKey = NULL; ALG_ID AlgID = 0; DWORD dwKeyLength = 0; DATA_BLOB SecretBlob = {m_bstrSecret.Length() * sizeof(WCHAR), (BYTE *) m_bstrSecret.m_str}; DWORD dwBlockLen = 0; DWORD cbBlockLen = sizeof(dwBlockLen); DATA_BLOB IVBlob = {0, NULL}; DebugTrace("Entering CEncryptedData::GenerateKey().\n"); // // Sanity check. // ATLASSERT(hCryptProv); ATLASSERT(phKey); ATLASSERT(SaltBlob.cbData); ATLASSERT(SaltBlob.pbData); // // Conver to ALG_ID. // if (FAILED(hr = ::EnumNameToAlgID(AlgoName, &AlgID))) { DebugTrace("Error [%#x]: EnumNameToAlgID() failed.\n", hr); goto ErrorExit; } // // Set key length for RC2 and RC4. // if ((CALG_RC2 == AlgID || CALG_RC4 == AlgID) && FAILED(hr = ::EnumNameToKeyLength(KeyLength, &dwKeyLength))) { DebugTrace("Error [%#x]: EnumNameToKeyLength() failed.\n", hr); goto ErrorExit; } // // Derive a session key from the secret. // if (FAILED(hr = DeriveKey(hCryptProv, AlgID, dwKeyLength, SecretBlob, SaltBlob, &hKey)) ) { DebugTrace("Error [%#x]: DeriveKey() failed.\n", hr); goto ErrorExit; } // // Generate random IV, if required. // if ((CALG_RC2 == AlgID) || (CALG_DES == AlgID) || (CALG_3DES == AlgID)) { // // Get block size. // if (!::CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE *) &dwBlockLen, &cbBlockLen, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptGetKeyParam() failed for KP_BLOCKLEN.\n", hr); goto ErrorExit; } // // Make sure block length is valid. // if (IVBlob.cbData = dwBlockLen / 8) { // // Allocate memory. // if (!(IVBlob.pbData = (BYTE *) ::CoTaskMemAlloc(IVBlob.cbData))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } // // Generate random IV. // if(!::CryptGenRandom(hCryptProv, IVBlob.cbData, IVBlob.pbData)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptGenRandom() failed.\n", hr); goto ErrorExit; } // // Set IV. // if(IVBlob.cbData && !::CryptSetKeyParam(hKey, KP_IV, IVBlob.pbData, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptSetKeyParam() failed for KP_IV.\n", hr); goto ErrorExit; } } } // // Return session key to caller. // *phKey = hKey; CommonExit: // // Free resource. // if (IVBlob.pbData) { ::CoTaskMemFree(IVBlob.pbData); } DebugTrace("Leaving EncryptedData::GenerateKey().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (hKey) { ::CryptDestroyKey(hKey); } goto CommonExit; }