You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1875 lines
52 KiB
1875 lines
52 KiB
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
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<IAlgorithm> pIAlgorithm = NULL;
|
|
CComPtr<IRecipients> 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<ICertificate> pIRecipient = NULL;
|
|
CComPtr<ICertificate2> 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<ICertificate> 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;
|
|
}
|