/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Microsoft Windows, Copyright (C) Microsoft Corporation, 2000 File: SignedData.cpp Content: Implementation of CSignedData. 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 "Certificate.h" #include "Signer.h" #include "Chain.h" #include "Convert.h" #include "Common.h" #include "Settings.h" #include "DialogUI.h" #include "SignedData.h" //////////////////////////////////////////////////////////////////////////////// // // Local functions. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : VerifyCertificate Synopsis : Verify if the certificate is valid. Parameter: HCERTSTORE hPKCS7Store - HCERTSTORE of message or NULL. PCCERT_CONTEXT - CERT_CONTEXT of cert to verify. DWORD * pdwCertError - Pointer to DWORD to receive the cert validity error code. Remark : ------------------------------------------------------------------------------*/ HRESULT VerifyCertificate (HCERTSTORE hPKCS7Store, PCCERT_CONTEXT pCertContext, DWORD * pdwCertError) { HRESULT hr = S_OK; PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_CHAIN_PARA ChainPara = {sizeof(CERT_CHAIN_PARA), {USAGE_MATCH_TYPE_AND, {0, NULL}}}; CERT_CHAIN_POLICY_PARA PolicyPara = {sizeof(CERT_CHAIN_POLICY_PARA), 0, NULL}; CERT_CHAIN_POLICY_STATUS PolicyStatus = {sizeof(CERT_CHAIN_POLICY_STATUS), 0, 0, 0, NULL}; DebugTrace("Entering VerifyCertificate().\n"); // // Sanity check. // ATLASSERT(pCertContext); ATLASSERT(pdwCertError); // // Build the chain. // if (!::CertGetCertificateChain(NULL, // in optional pCertContext, // in NULL, // in optional hPKCS7Store, // in optional &ChainPara, // in 0, // in NULL, // in &pChainContext)) // out { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CertGetCertificateChain() failed.\n", hr); goto ErrorExit; } // // Verify the chain. // if (::CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE, pChainContext, &PolicyPara, &PolicyStatus)) { *pdwCertError = 0; } else { *pdwCertError = PolicyStatus.dwError; DebugTrace("Error: CertVerifyCertificateChainPolicy() failed.\n"); } CommonExit: // // Free resource. // if (pChainContext) { ::CertFreeCertificateChain(pChainContext); } DebugTrace("Leaving VerifyCertificate().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : GetSignerCert Synopsis : Retrieve signer's cert from ISigner object. If signer's cert is not available in the ISigner object, pop UI to prompt user to select a signing cert. Parameter: ISigner * pISigner - Pointer to ISigner or NULL. ISigner ** ppISigner - Pointer to pointer to ISigner to receive interface pointer. ICertificate ** ppICertificate - Pointer to pointer to ICertificate to receive interface pointer. PCCERT_CONTEXT * ppCertContext - Pointer to pointer to CERT_CONTEXT to receive cert context. Remark : ------------------------------------------------------------------------------*/ static HRESULT GetSignerCert (ISigner * pISigner, ISigner ** ppISigner, ICertificate ** ppICertificate, PCCERT_CONTEXT * ppCertContext) { HRESULT hr = S_OK; CComPtr pISigner2 = NULL; CComPtr pICertificate = NULL; PCCERT_CONTEXT pCertContext = NULL; DWORD dwCertError = 0; VARIANT_BOOL bHasPrivateKey = VARIANT_TRUE; DebugTrace("Entering GetSignerCert().\n"); // // Sanity check. // ATLASSERT(ppISigner); ATLASSERT(ppICertificate); ATLASSERT(ppCertContext); // // Initialize. // *ppISigner = NULL; *ppICertificate = NULL; *ppCertContext = NULL; try { // // Did user pass us an ISigner? // if (pISigner) { // // Retrieve the signer's cert. // hr = pISigner->get_Certificate(&pICertificate); // // If signer's cert is not present, pop UI. // if (CAPICOM_E_SIGNER_NOT_INITIALIZED == hr) { // // Prompt user to select a certificate. // if (FAILED(hr = ::SelectSignerCert(&pICertificate))) { DebugTrace("Error [%#x]: SelectSignerCert() failed.\n", hr); goto ErrorExit; } } // // Get cert context. // if (FAILED(hr = ::GetCertContext(pICertificate, &pCertContext))) { DebugTrace("Error [%#x]: GetCertContext() failed.\n", hr); goto ErrorExit; } pISigner2 = pISigner; } else { CRYPT_ATTRIBUTES attributes = {0, NULL}; // // No signer specified, so prompt user to select a certificate. // if (FAILED(hr = ::SelectSignerCert(&pICertificate))) { DebugTrace("Error [%#x]: SelectSignerCert() failed.\n", hr); goto ErrorExit; } // // Get cert context. // if (FAILED(hr = ::GetCertContext(pICertificate, &pCertContext))) { DebugTrace("Error [%#x]: GetCertContext() failed.\n", hr); goto ErrorExit; } // // Create the ISigner object. // if (FAILED(hr = ::CreateSignerObject(pCertContext, &attributes, &pISigner2))) { DebugTrace("Error [%#x]: CreateSignerObject() failed.\n", hr); goto ErrorExit; } } // // Make sure the cert is valid. // if (FAILED(hr = ::VerifyCertificate(NULL, pCertContext, &dwCertError))) { DebugTrace("Error: VerifyCertificate() failed.\n"); goto ErrorExit; } if (dwCertError) { hr = HRESULT_FROM_WIN32(dwCertError); DebugTrace("Error [%#x]: invalid cert.\n", hr); goto ErrorExit; } // // Make sure we have a private key. // if (FAILED(hr = pICertificate->HasPrivateKey(&bHasPrivateKey))) { DebugTrace("Error [%#x]: pICertifiacte->HasPrivateKey() failed.\n", hr); goto ErrorExit; } if (VARIANT_TRUE != bHasPrivateKey) { hr = CAPICOM_E_CERTIFICATE_NO_PRIVATE_KEY; DebugTrace("Error: signer's private key is not available.\n"); goto ErrorExit; } // // Return values to caller. // if (FAILED(hr = pISigner2->QueryInterface(ppISigner))) { DebugTrace("Unexpected error [%#x]: pISigner2->QueryInterface() failed.\n", hr); goto ErrorExit; } if (FAILED(hr = pICertificate->QueryInterface(ppICertificate))) { DebugTrace("Unexpected error [%#x]: pICertificate->QueryInterface() failed.\n", hr); goto ErrorExit; } *ppCertContext = pCertContext; } catch(...) { hr = E_POINTER; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } CommonExit: DebugTrace("Leaving GetSignerCert().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } if (*ppICertificate) { (*ppICertificate)->Release(); *ppICertificate = NULL; } if (*ppISigner) { (*ppISigner)->Release(); *ppISigner = NULL; } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FreeCertificateChain Synopsis : Free resources allocated for the chain built with InitCertificateChain. Parameter: CRYPT_DATA_BLOB * pChainBlob - Pointer to chain blob. Remark : ------------------------------------------------------------------------------*/ static void FreeCertificateChain (CRYPT_DATA_BLOB * pChainBlob) { PCCERT_CONTEXT * rgCertContext; DebugTrace("Entering FreeCertificateChain().\n"); // // Sanity check. // ATLASSERT(pChainBlob); // // Return if nothing to free. // if (0 == pChainBlob->cbData) { return; } // // Sanity check. // ATLASSERT(pChainBlob->pbData); // // Free all allocated memory for the chain. // rgCertContext = (PCCERT_CONTEXT *) pChainBlob->pbData; for (DWORD i = 0; i < pChainBlob->cbData; i++, rgCertContext++) { ATLASSERT(*rgCertContext); ::CertFreeCertificateContext(*rgCertContext); } ::CoTaskMemFree((LPVOID) pChainBlob->pbData); return; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : AddCertificateChain Synopsis : Add the chain of certificates to the bag of certs. Parameter: HCRYPTMSG hMsg - Message handle. DATA_BLOB * pChainBlob - Pointer chain blob of PCCERT_CONTEXT. Remark : ------------------------------------------------------------------------------*/ static HRESULT AddCertificateChain (HCRYPTMSG hMsg, DATA_BLOB * pChainBlob) { HRESULT hr = S_OK; DebugTrace("Entering AddCertificateChain().\n"); // // Sanity check. // ATLASSERT(hMsg); ATLASSERT(pChainBlob); ATLASSERT(pChainBlob->cbData); ATLASSERT(pChainBlob->pbData); DWORD cCertContext = pChainBlob->cbData; PCERT_CONTEXT * rgCertContext = (PCERT_CONTEXT *) pChainBlob->pbData; if (1 < cCertContext) { cCertContext--; } // // Add all certs from the chain to message. // for (DWORD i = 0; i < cCertContext; i++) { // // Is this cert already in the bag of certs? // if (FAILED(hr =::FindSignerCertInMessage(hMsg, &rgCertContext[i]->pCertInfo->Issuer, &rgCertContext[i]->pCertInfo->SerialNumber, NULL))) { // // No, so add to bag of certs. // DATA_BLOB CertBlob = {rgCertContext[i]->cbCertEncoded, rgCertContext[i]->pbCertEncoded}; if (!::CryptMsgControl(hMsg, 0, CMSG_CTRL_ADD_CERT, &CertBlob)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgControl() failed for CMSG_CTRL_ADD_CERT.\n", hr); break; } hr = S_OK; } } DebugTrace("Leaving AddCertificateChain().\n"); return hr; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : InitCertificateChain Synopsis : Allocate and initialize a certificate chain for the specified certificate, and return the chain in an array of PCERT_CONTEXT. Parameter: ICertificate * pICertificate - Pointer to ICertificate for which the chain will be built. CRYPT_DATA_BLOB * pChainBlob - Pointer to blob to recevie the size and array of PCERT_CONTEXT for the chain. Remark : ------------------------------------------------------------------------------*/ static HRESULT InitCertificateChain (ICertificate * pICertificate, CRYPT_DATA_BLOB * pChainBlob) { HRESULT hr = S_OK; CComObject * pCChain = NULL; CComPtr pIChain = NULL; VARIANT_BOOL bResult = VARIANT_FALSE; DebugTrace("Entering InitCertificateChain().\n"); // // Sanity checks. // ATLASSERT(pICertificate); ATLASSERT(pChainBlob); // // Create a chain object. // if (FAILED(hr = CComObject::CreateInstance(&pCChain))) { DebugTrace("Error [%#x]: CComObject::CreateInstance() failed.\n", hr); goto CommonExit; } // // Get IChain pointer. // if (FAILED(hr = pCChain->QueryInterface(&pIChain))) { delete pCChain; DebugTrace("Unexpected error [%#x]: pCChain->QueryInterface() failed.\n", hr); goto CommonExit; } // // Build the chain. // if (FAILED(hr = pIChain->Build(pICertificate, &bResult))) { DebugTrace("Error [%#x]: pIChain->Build() failed.\n", hr); goto CommonExit; } // // Get the chain of certs. // if (FAILED(hr = ::GetChainContext(pIChain, pChainBlob))) { DebugTrace("Error [%#x]: GetChainContext() failed.\n", hr); } CommonExit: DebugTrace("Leaving InitCertificateChain().\n"); return hr; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FreeAuthenticatedAttributes Synopsis : Free memory allocated for all authenticated attributes. Parameter: DWORD cAuthAttr - Number of attributes. PCRYPT_ATTRIBUTE rgAuthAttr - Pointer to CRYPT_ATTRIBUTE array. Remark : ------------------------------------------------------------------------------*/ static void FreeAuthenticatedAttributes (DWORD cAuthAttr, PCRYPT_ATTRIBUTE rgAuthAttr) { DebugTrace("Entering FreeAuthenticatedAttributes().\n"); // // Make sure we have something to free. // if (rgAuthAttr) { // // First free each element of the array. // for (DWORD i = 0; i < cAuthAttr; i++) { if (NULL != rgAuthAttr[i].rgValue) { for (DWORD j = 0; j < rgAuthAttr[i].cValue; j++) { if (NULL != rgAuthAttr[i].rgValue[j].pbData) { ::CoTaskMemFree((LPVOID) rgAuthAttr[i].rgValue[j].pbData); } } ::CoTaskMemFree(rgAuthAttr[i].rgValue); } } // // Then free the array itself. // ::CoTaskMemFree((LPVOID) rgAuthAttr); } DebugTrace("Leaving FreeAuthenticatedAttributes().\n"); return; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : AddAuthenticatedAttributes Synopsis : Allocate memory and add authenticated attribute of the specified signer to the CMSG_SIGNER_ENCODE_INFO structure. Parameter: ISigner * pISigner - Pointer to ISigner. CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo - Pointer to CMSG_SIGNER_ENCODE_INFO structure. Remark : ------------------------------------------------------------------------------*/ static HRESULT AddAuthenticatedAttributes (ISigner * pISigner, CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo) { HRESULT hr = S_OK; long cAttributes = 0; CComPtr pIAttributes = NULL; DebugTrace("Entering AddAuthenticatedAttributes().\n"); // // Sanity check. // ATLASSERT(pSignerEncodeInfo); // // Get authenticated attributes. // if (FAILED(hr = pISigner->get_AuthenticatedAttributes(&pIAttributes))) { DebugTrace("Error [%#x]: pISigner->get_AuthenticatedAttributes() failed.\n", hr); goto ErrorExit; } // // Get count of attributes. // if (FAILED(hr = pIAttributes->get_Count(&cAttributes))) { DebugTrace("Error [%#x]: pIAttributes->get_Count() failed.\n", hr); goto ErrorExit; } if (0 < cAttributes) { // // Allocate memory for attribute array. // pSignerEncodeInfo->cAuthAttr = (DWORD) cAttributes; pSignerEncodeInfo->rgAuthAttr = (PCRYPT_ATTRIBUTE) ::CoTaskMemAlloc(sizeof(CRYPT_ATTRIBUTE) * cAttributes); if (!pSignerEncodeInfo->rgAuthAttr) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } ::ZeroMemory(pSignerEncodeInfo->rgAuthAttr, sizeof(CRYPT_ATTRIBUTE) * cAttributes); // // Loop thru each attribute and add to the array. // for (long cAttr = 0; cAttr < cAttributes; cAttr++) { CAPICOM_ATTRIBUTE AttrName; CComVariant varValue; CComVariant varIAttribute; CComPtr pIAttribute = NULL; // // Get next attribute. // if (FAILED(hr = pIAttributes->get_Item(cAttr + 1, &varIAttribute))) { DebugTrace("Error [%#x]: pIAttributes->get_Item() failed.\n", hr); goto ErrorExit; } // // Get custom interface. // if (FAILED(hr = varIAttribute.pdispVal->QueryInterface(IID_IAttribute, (void **) &pIAttribute))) { DebugTrace("Error [%#x]: varIAttribute.pdispVal->QueryInterface() failed.\n", hr); goto ErrorExit; } // // Get attribute name. // if (FAILED(hr = pIAttribute->get_Name(&AttrName))) { DebugTrace("Error [%#x]: pIAttribute->get_Name() failed.\n", hr); goto ErrorExit; } // // Get attribute value. // if (FAILED(hr = pIAttribute->get_Value(&varValue))) { DebugTrace("Error [%#x]: pIAttribute->get_Value() failed.\n", hr); goto ErrorExit; } switch (AttrName) { case CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME: { FILETIME ft; SYSTEMTIME st; // // Conver to FILETIME. // if (!::VariantTimeToSystemTime(varValue.date, &st)) { hr = CAPICOM_E_ATTRIBUTE_INVALID_VALUE; DebugTrace("Error [%#x]: VariantTimeToSystemTime() failed.\n"); goto ErrorExit; } if (!::SystemTimeToFileTime(&st, &ft)) { hr = CAPICOM_E_ATTRIBUTE_INVALID_VALUE; DebugTrace("Error [%#x]: VariantTimeToSystemTime() failed.\n"); goto ErrorExit; } // // Now encode it. // pSignerEncodeInfo->rgAuthAttr[cAttr].cValue = 1; pSignerEncodeInfo->rgAuthAttr[cAttr].pszObjId = szOID_RSA_signingTime; pSignerEncodeInfo->rgAuthAttr[cAttr].rgValue = (CRYPT_ATTR_BLOB *) ::CoTaskMemAlloc(sizeof(CRYPT_ATTR_BLOB)); if (FAILED(hr = ::EncodeObject ((LPSTR) szOID_RSA_signingTime, (LPVOID) &ft, pSignerEncodeInfo->rgAuthAttr[cAttr].rgValue))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } break; } case CAPICOM_AUTHENTICATED_ATTRIBUTE_DOCUMENT_NAME: { CRYPT_DATA_BLOB NameBlob = {0, NULL}; NameBlob.cbData = ::SysStringByteLen(varValue.bstrVal); NameBlob.pbData = (PBYTE) varValue.bstrVal; pSignerEncodeInfo->rgAuthAttr[cAttr].cValue = 1; pSignerEncodeInfo->rgAuthAttr[cAttr].pszObjId = szOID_CAPICOM_DOCUMENT_NAME; pSignerEncodeInfo->rgAuthAttr[cAttr].rgValue = (CRYPT_ATTR_BLOB *) ::CoTaskMemAlloc(sizeof(CRYPT_ATTR_BLOB)); if (FAILED(hr = ::EncodeObject ((LPSTR) X509_OCTET_STRING, (LPVOID) &NameBlob, pSignerEncodeInfo->rgAuthAttr[cAttr].rgValue))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } break; } case CAPICOM_AUTHENTICATED_ATTRIBUTE_DOCUMENT_DESCRIPTION: { CRYPT_DATA_BLOB DescBlob = {0, NULL}; DescBlob.cbData = ::SysStringByteLen(varValue.bstrVal); DescBlob.pbData = (PBYTE) varValue.bstrVal; pSignerEncodeInfo->rgAuthAttr[cAttr].cValue = 1; pSignerEncodeInfo->rgAuthAttr[cAttr].pszObjId = szOID_CAPICOM_DOCUMENT_DESCRIPTION; pSignerEncodeInfo->rgAuthAttr[cAttr].rgValue = (CRYPT_ATTR_BLOB *) ::CoTaskMemAlloc(sizeof(CRYPT_ATTR_BLOB)); if (FAILED(hr = ::EncodeObject ((LPSTR) X509_OCTET_STRING, (LPVOID) &DescBlob, pSignerEncodeInfo->rgAuthAttr[cAttr].rgValue))) { DebugTrace("Error [%#x]: EncodeObject() failed.\n", hr); goto ErrorExit; } break; } default: { hr = CAPICOM_E_ATTRIBUTE_INVALID_NAME; DebugTrace("Error [%#x]: unknown attribute name.\n", hr); goto ErrorExit; } } } } CommonExit: DebugTrace("Leaving AddAuthenticatedAttributes().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resources. // if (pSignerEncodeInfo->rgAuthAttr) { ::FreeAuthenticatedAttributes(pSignerEncodeInfo->cAuthAttr, pSignerEncodeInfo->rgAuthAttr); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : FreeSignerEncodeInfo Synopsis : Free all memory allocated for the CMSG_SIGNER_ENCODE_INFO structure, including any memory allocated for members of the structure. Parameter: CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo - Pointer to the structure. Remark : ------------------------------------------------------------------------------*/ static void FreeSignerEncodeInfo (CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo) { DebugTrace("Entering FreeSignerEncodeInfo().\n"); // // Sanity check. // ATLASSERT(pSignerEncodeInfo); // // First free the authenticated attributes array, if present. // if (pSignerEncodeInfo->rgAuthAttr) { ::FreeAuthenticatedAttributes(pSignerEncodeInfo->cAuthAttr, pSignerEncodeInfo->rgAuthAttr); } // // Then free the structure inself. // ::CoTaskMemFree((LPVOID) pSignerEncodeInfo); DebugTrace("Leaving FreeSignerEncodeInfo().\n"); return; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : InitSignerEncodeInfo Synopsis : Allocate a CMSG_SIGNER_ENCODE_INFO structure, and initialize it with values passed in through parameters. Parameter: ISigner * pISigner - Pointer to ISigner. PCCERT_CONTEXT pCertContext - Pointer to CERT_CONTEXT of cert. HCRYPTPROV phCryptProv - CSP handle. DWORD dwKeySpec - Key spec, AT_KEYEXCHANGE or AT_SIGNATURE. CMSG_SIGNER_ENCODE_INFO ** ppSignerEncodeInfo - Receive the structure. Return : Pointer to an initialized CMSG_SIGNER_ENCODE_INFO structure if success, otherwise NULL (out of memory). Remark : Must call FreeSignerEncodeInfo to free the structure. ------------------------------------------------------------------------------*/ static HRESULT InitSignerEncodeInfo (ISigner * pISigner, PCCERT_CONTEXT pCertContext, HCRYPTPROV hCryptProv, DWORD dwKeySpec, CMSG_SIGNER_ENCODE_INFO ** ppSignerEncodeInfo) { HRESULT hr = S_OK; CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo = NULL; DebugTrace("Entering InitSignerEncodeInfo().\n"); // // Sanity check. // ATLASSERT(pISigner); ATLASSERT(pCertContext); ATLASSERT(hCryptProv); // // Allocate memory for structure. // if (!(pSignerEncodeInfo = (CMSG_SIGNER_ENCODE_INFO *) ::CoTaskMemAlloc(sizeof(CMSG_SIGNER_ENCODE_INFO)))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } // // Setup CMSG_SIGNER_ENCODE_INFO structure. // ::ZeroMemory(pSignerEncodeInfo, sizeof(CMSG_SIGNER_ENCODE_INFO)); pSignerEncodeInfo->cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO); pSignerEncodeInfo->pCertInfo = pCertContext->pCertInfo; pSignerEncodeInfo->hCryptProv = hCryptProv; pSignerEncodeInfo->dwKeySpec = dwKeySpec; pSignerEncodeInfo->HashAlgorithm.pszObjId = szOID_OIWSEC_sha1; // // Add authenticated attributes to SignerInfo. // if (FAILED(hr = ::AddAuthenticatedAttributes(pISigner, pSignerEncodeInfo))) { DebugTrace("Error [%#x]: AddAuthenticatedAttributes() failed.\n", hr); goto ErrorExit; } // // Return structure to caller. // *ppSignerEncodeInfo = pSignerEncodeInfo; CommonExit: DebugTrace("Leaving InitSignerEncodeInfo().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resources. // if (pSignerEncodeInfo) { ::CoTaskMemFree((LPVOID) pSignerEncodeInfo); } goto CommonExit; } //////////////////////////////////////////////////////////////////////////////// // // CSignedData // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::get_Content Synopsis : Return the content. Parameter: BSTR * pVal - Pointer to BSTR to receive the content. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::get_Content (BSTR * pVal) { HRESULT hr = S_OK; DebugTrace("Entering CSignedData::get_Content().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // 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_SIGN_NOT_INITIALIZED; DebugTrace("Error: sign 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_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CSignedData::get_Content().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::put_Content Synopsis : Initialize the object with content to be signed. Parameter: BSTR newVal - BSTR containing the content to be signed. Remark : Note that this property should not be changed once a signature is created, as it will re-initialize the object even in error condition, unless that's your intention. ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::put_Content (BSTR newVal) { HRESULT hr = S_OK; DebugTrace("Entering CSignedData::put_Content().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Reset member variables. // if (m_ContentBlob.pbData) { ::CoTaskMemFree(m_ContentBlob.pbData); } if (m_MessageBlob.pbData) { ::CoTaskMemFree(m_MessageBlob.pbData); } m_bSigned = FALSE; m_bDetached = VARIANT_FALSE; m_ContentBlob.cbData = 0; m_ContentBlob.pbData = NULL; m_MessageBlob.cbData = 0; m_MessageBlob.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_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CSignedData::put_Content().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::get_Signers Synopsis : Return all the content signers as an ISigners collection object. Parameter: ISigner * pVal - Pointer to pointer to ISigners to receive interface pointer. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::get_Signers (ISigners ** pVal) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; DebugTrace("Entering CSignedData::get_Signers().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Make sure the messages is already signed. // if (!m_bSigned) { hr = CAPICOM_E_SIGN_NOT_SIGNED; DebugTrace("Error: content was not signed.\n"); goto ErrorExit; } // // Open the encoded message for decode. // if (FAILED(hr = OpenToDecode(NULL, &hMsg))) { DebugTrace("Error [%#x]: OpenToDecode() failed.\n", hr); goto ErrorExit; } // // Create the ISigners collection object. // if (FAILED(hr = ::CreateSignersObject(hMsg, 1, pVal))) { DebugTrace("Error [%#x]: CreateSignersObject() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resource. // if (hMsg) { ::CryptMsgClose(hMsg); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CSignedData::get_Signers().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::get_Certificates Synopsis : Return all certificates found in the message as an non-ordered ICertificates collection object. Parameter: ICertificates ** pVal - Pointer to pointer to ICertificates to receive the interface pointer. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::get_Certificates (ICertificates ** pVal) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; DebugTrace("Entering CSignedData::get_Certificates().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Make sure the messages is already signed. // if (!m_bSigned) { hr = CAPICOM_E_SIGN_NOT_SIGNED; DebugTrace("Error: content was not signed.\n"); goto ErrorExit; } // // Open the encoded message for decode. // if (FAILED(hr = OpenToDecode(NULL, &hMsg))) { DebugTrace("Error [%#x]: OpenToDecode() failed.\n", hr); goto ErrorExit; } // // Create the ICertificates collection object. // if (FAILED(hr = ::CreateCertificatesObject(CAPICOM_CERTIFICATES_LOAD_FROM_MESSAGE, (LPARAM) hMsg, pVal))) { DebugTrace("Error [%#x]: CreateCertificatesObject() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resource. // if (hMsg) { ::CryptMsgClose(hMsg); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CSignedData::get_Certificates().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::Sign Synopsis : Sign the content and produce a signed message. Parameter: ISigner * pSigner - Pointer to ISigner. VARIANT_BOOL bDetached - TRUE if content is to be detached, else FALSE. CAPICOM_ENCODING_TYPE EncodingType - Encoding type. BSTR * pVal - Pointer to BSTR to receive the signed message. Remark : The certificate selection dialog will be launched (CryptUIDlgSelectCertificate API) to display a list of certificates from the Current User\My store for selecting a signer's certificate, for the following conditions: 1) A signer is not specified (pVal is NULL) or the ICertificate property of the ISigner is not set 2) There is more than 1 cert in the store, and 3) The Settings::EnablePromptForIdentityUI property is not disabled. Also 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 signing. ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::Sign (ISigner * pISigner, VARIANT_BOOL bDetached, CAPICOM_ENCODING_TYPE EncodingType, BSTR * pVal) { HRESULT hr = S_OK; CComPtr pISigner2 = NULL; CComPtr pICertificate = NULL; HCRYPTPROV hCryptProv = NULL; PCCERT_CONTEXT pCertContext = NULL; DWORD dwKeySpec = 0; BOOL bReleaseContext = FALSE; DATA_BLOB ChainBlob = {0, NULL}; CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo = NULL; DebugTrace("Entering CSignedData::Sign().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // 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_SIGN_NOT_INITIALIZED; DebugTrace("Error: sign object has not been initialized.\n"); goto ErrorExit; } // // Sanity check. // ATLASSERT(m_ContentBlob.pbData); // // Get the signer's cert (may prompt user to select signer's cert). // if (FAILED(hr = ::GetSignerCert(pISigner, &pISigner2, &pICertificate, &pCertContext))) { DebugTrace("Error [%#x]: GetSignerCert() failed.\n", hr); goto ErrorExit; } // // If we are called from a web page, we need to pop up UI // to get user permission to perform signing operation. // if (m_dwCurrentSafety && PromptForSigningOperationEnabled() && FAILED(hr = ::UserApprovedOperation(IDD_SIGN_SECURITY_ALERT_DLG))) { DebugTrace("Error [%#x]: UserApprovedOperation() failed.\n", hr); goto ErrorExit; } // // Acquire CSP context and access to private key. // if (FAILED(hr = ::AcquireContext(pCertContext, &hCryptProv, &dwKeySpec, &bReleaseContext))) { DebugTrace("Error [%#x]: AcquireContext() failed.\n", hr); goto ErrorExit; } // // Build the bag of certs (cert chain minus root) to be included // into the message. // if (FAILED(hr = InitCertificateChain(pICertificate, &ChainBlob))) { DebugTrace("Error [%#x]: InitCertificateChain() failed.\n", hr); goto ErrorExit; } // // Allocate and initialize a CMSG_SIGNER_ENCODE_INFO structure. // if (FAILED(hr = ::InitSignerEncodeInfo(pISigner2, pCertContext, hCryptProv, dwKeySpec, &pSignerEncodeInfo))) { DebugTrace("Error [%#x]: InitSignerEncodeInfo() failed.\n", hr); goto ErrorExit; } // // Now sign the content. // if (FAILED(hr = SignContent(pSignerEncodeInfo, &ChainBlob, bDetached, EncodingType, pVal))) { DebugTrace("Error [%#x]: CSignedData::SignContent() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resources. // if (pSignerEncodeInfo) { ::FreeSignerEncodeInfo(pSignerEncodeInfo); } if (ChainBlob.pbData) { ::FreeCertificateChain(&ChainBlob); } if (hCryptProv && bReleaseContext) { ::CryptReleaseContext(hCryptProv, 0); } if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CSignedData::Sign().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::CoSign Synopsis : CoSign the content and produce a signed message. This method will behaves the same as the Sign method as a non-detached message if the messge currently does not already have a signature. Parameter: ISigner * pSigner - Pointer to ISigner. CAPICOM_ENCODING_TYPE EncodingType - Encoding type. BSTR * pVal - Pointer to BSTR to receive the signed message. Remark : The certificate selection dialog will be launched (CryptUIDlgSelectCertificate API) to display a list of certificates from the Current User\My store for selecting a signer's certificate, for the following conditions: 1) A signer is not specified (pVal is NULL) or the ICertificate property of the ISigner is not set 2) There is more than 1 cert in the store, and 3) The Settings::EnablePromptForIdentityUI property is not disabled. Also 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 signing. ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::CoSign (ISigner * pISigner, CAPICOM_ENCODING_TYPE EncodingType, BSTR * pVal) { HRESULT hr = S_OK; CComPtr pISigner2 = NULL; CComPtr pICertificate = NULL; HCRYPTPROV hCryptProv = NULL; PCCERT_CONTEXT pCertContext = NULL; DWORD dwKeySpec = 0; BOOL bReleaseContext = FALSE; DATA_BLOB ChainBlob = {0, NULL}; CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo = NULL; DebugTrace("Entering CSignedData::CoSign().\n"); // // Lock access to this object. // m_Lock.Lock(); try { if (NULL == pVal) { hr = E_POINTER; DebugTrace("Error: invalid parameter, pVal is NULL.\n"); goto ErrorExit; } // // Make sure message has been signed? // if (!m_bSigned) { hr = CAPICOM_E_SIGN_NOT_SIGNED; DebugTrace("Error: cannot cosign an unsigned message.\n"); goto ErrorExit; } // // Make sure content is already initialized. // if (0 == m_ContentBlob.cbData) { hr = CAPICOM_E_SIGN_NOT_INITIALIZED; DebugTrace("Error: sign object has not been initialized.\n"); goto ErrorExit; } // // Sanity check. // ATLASSERT(m_ContentBlob.pbData); ATLASSERT(m_MessageBlob.cbData); ATLASSERT(m_MessageBlob.pbData); // // Get the signer's cert (may prompt user to select signer's cert). // if (FAILED(hr = ::GetSignerCert(pISigner, &pISigner2, &pICertificate, &pCertContext))) { DebugTrace("Error [%#x]: GetSignerCert() failed.\n", hr); goto ErrorExit; } // // If we are called from a web page, we need to pop up UI // to get user permission to perform signing operation. // if (m_dwCurrentSafety && PromptForSigningOperationEnabled() && FAILED(hr = ::UserApprovedOperation(IDD_SIGN_SECURITY_ALERT_DLG))) { DebugTrace("Error [%#x]: UserApprovedOperation() failed.\n", hr); goto ErrorExit; } // // Acquire CSP context and access to private key. // if (FAILED(hr = ::AcquireContext(pCertContext, &hCryptProv, &dwKeySpec, &bReleaseContext))) { DebugTrace("Error [%#x]: AcquireContext() failed.\n", hr); goto ErrorExit; } // // Build the bag of certs (cert chain minus root) to be included // into the message. // if (FAILED(hr = InitCertificateChain(pICertificate, &ChainBlob))) { DebugTrace("Error [%#x]: InitCertificateChain() failed.\n", hr); goto ErrorExit; } // // Allocate and initialize a CMSG_SIGNER_ENCODE_INFO structure. // if (FAILED(hr = ::InitSignerEncodeInfo(pISigner2, pCertContext, hCryptProv, dwKeySpec, &pSignerEncodeInfo))) { DebugTrace("Error [%#x]: InitSignerEncodeInfo() failed.\n", hr); goto ErrorExit; } // // CoSign the content. // if (FAILED(hr = CoSignContent(pSignerEncodeInfo, &ChainBlob, EncodingType, pVal))) { DebugTrace("Error [%#x]: CSignedData::CoSignContent() failed.\n", hr); goto ErrorExit; } } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resources. // if (pSignerEncodeInfo) { ::FreeSignerEncodeInfo(pSignerEncodeInfo); } if (ChainBlob.pbData) { ::FreeCertificateChain(&ChainBlob); } if (hCryptProv && bReleaseContext) { ::CryptReleaseContext(hCryptProv, 0); } if (pCertContext) { ::CertFreeCertificateContext(pCertContext); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CSignedData::CoSign().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); ReportError(hr); goto UnlockExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::Verify Synopsis : Verify a signed message. Parameter: BSTR SignedMessage - BSTR containing the signed message to be verified. VARIANT_BOOL bDetached - TRUE if content was detached, else FALSE. CAPICOM_SIGNED_DATA_VERIFY_FLAG VerifyFlag - Verify flag. Remark : Note that for non-detached message, this method will always try to set the Content property using the signed message, even if the signed message does not verify. ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::Verify (BSTR SignedMessage, VARIANT_BOOL bDetached, CAPICOM_SIGNED_DATA_VERIFY_FLAG VerifyFlag) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; HCERTSTORE hPKCS7Store = NULL; DWORD dwCertError = 0; DWORD dwNumSigners = 0; DWORD cbSigners = sizeof(dwNumSigners); DWORD dwSigner = 0; DebugTrace("Entering CSignedData::Verify().\n"); // // Lock access to this object. // m_Lock.Lock(); try { // // Initialize member variables. // if (!bDetached) { if (m_ContentBlob.pbData) { ::CoTaskMemFree(m_ContentBlob.pbData); } m_ContentBlob.cbData = 0; m_ContentBlob.pbData = NULL; } if (m_MessageBlob.pbData) { ::CoTaskMemFree(m_MessageBlob.pbData); } m_bSigned = FALSE; m_bDetached = bDetached; m_MessageBlob.cbData = 0; m_MessageBlob.pbData = NULL; // // Make sure parameters are valid. // if (NULL == SignedMessage) { hr = E_POINTER; DebugTrace("Error: invalid parameter, SignedMessage is NULL.\n"); goto ErrorExit; } if (0 == ::SysStringByteLen(SignedMessage)) { hr = E_INVALIDARG; DebugTrace("Error: invalid parameter, SignedMessage is empty.\n", hr); goto ErrorExit; } // // If detached, make sure content is already initialized. // if (m_bDetached) { if (0 == m_ContentBlob.cbData) { hr = CAPICOM_E_SIGN_NOT_INITIALIZED; DebugTrace("Error: content was not initialized for detached decoding.\n"); goto ErrorExit; } // // Sanity check. // ATLASSERT(m_ContentBlob.pbData); } // // Import the message. // if (FAILED(hr = ::ImportData(SignedMessage, &m_MessageBlob))) { DebugTrace("Error [%#x]: ImportData() 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("ImportedSigned.asn", m_MessageBlob.pbData, m_MessageBlob.cbData); // // Open the message to decode. // if (FAILED(hr = OpenToDecode(NULL, &hMsg))) { DebugTrace("Error [%#x]: OpenToDecode() failed.\n", hr); goto ErrorExit; } // // Verify cert as well? // if (CAPICOM_VERIFY_SIGNATURE_AND_CERTIFICATE == VerifyFlag) { // // Yes, so open the PKCS7 store. // if (!(hPKCS7Store = ::CertOpenStore(CERT_STORE_PROV_PKCS7, CAPICOM_ASN_ENCODING, NULL, CERT_STORE_OPEN_EXISTING_FLAG, &m_MessageBlob))) { DebugTrace("Error [%#x]: CertOpenStore() failed.\n", hr); goto ErrorExit; } } // // Get number of content signers (first level signers). // if (!::CryptMsgGetParam(hMsg, CMSG_SIGNER_COUNT_PARAM, 0, (void **) &dwNumSigners, &cbSigners)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgGetParam() failed to get CMSG_SIGNER_COUNT_PARAM.\n", hr); goto ErrorExit; } // // Verify all signatures. // for (dwSigner = 0; dwSigner < dwNumSigners; dwSigner++) { PCERT_CONTEXT pCertContext = NULL; CMSG_SIGNER_INFO * pSignerInfo = NULL; CRYPT_DATA_BLOB SignerInfoBlob = {0, NULL}; // // Get signer info. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_SIGNER_INFO_PARAM, dwSigner, (void**) &SignerInfoBlob.pbData, &SignerInfoBlob.cbData))) { DebugTrace("Error [%#x]: GetMsgParam() failed to get CMSG_SIGNER_INFO_PARAM for signer #%d.\n", hr, dwSigner); goto ErrorExit; } pSignerInfo = (CMSG_SIGNER_INFO *) SignerInfoBlob.pbData; // // Find the cert in the message. // hr = ::FindSignerCertInMessage(hMsg, &pSignerInfo->Issuer, &pSignerInfo->SerialNumber, &pCertContext); // // First free memory. // ::CoTaskMemFree(SignerInfoBlob.pbData); // // Check result. // if (FAILED(hr)) { DebugTrace("Error [%#x]: FindSignerCertInMessage() failed.\n", hr); goto ErrorExit; } // // Verify the cert regardless if the user had requested. This // is done so that the chain will always be built first before // we later verify the signature, which is required by DSS. // if (FAILED(hr = ::VerifyCertificate(hPKCS7Store, pCertContext, &dwCertError))) { // // Free CERT_CONTEXT. // ::CertFreeCertificateContext(pCertContext); DebugTrace("Error [%#x]: VerifyCertificate() failed.\n", hr); goto ErrorExit; } // // Verify cert as well? // if ((CAPICOM_VERIFY_SIGNATURE_AND_CERTIFICATE == VerifyFlag) && (dwCertError)) { // // Invalid certificate. // hr = HRESULT_FROM_WIN32(dwCertError); // // Free CERT_CONTEXT. // ::CertFreeCertificateContext(pCertContext); DebugTrace("Error [%#x]: invalid cert.\n", hr); goto ErrorExit; } // // Verify signature. // if (!::CryptMsgControl(hMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE, pCertContext->pCertInfo)) { // // Invalid signature. // hr = HRESULT_FROM_WIN32(::GetLastError()); // // Free CERT_CONTEXT. // ::CertFreeCertificateContext(pCertContext); DebugTrace("Error [%#x]: CryptMsgControl(CMSG_CTRL_VERIFY_SIGNATURE) failed.\n", hr); goto ErrorExit; } // // Free CERT_CONTEXT. // ::CertFreeCertificateContext(pCertContext); } // // Update member variables. // m_bSigned = TRUE; } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } UnlockExit: // // Free resouce. // if(hMsg) { ::CryptMsgClose(hMsg); } if (hPKCS7Store) { ::CertCloseStore(hPKCS7Store, 0); } // // Unlock access to this object. // m_Lock.Unlock(); DebugTrace("Leaving CSignedData::Verify().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Reset member variables. // m_bSigned = FALSE; m_bDetached = VARIANT_FALSE; #if (0) if (m_ContentBlob.pbData) { ::CoTaskMemFree(m_ContentBlob.pbData); } m_ContentBlob.cbData = 0; m_ContentBlob.pbData = NULL; #endif if (m_MessageBlob.pbData) { ::CoTaskMemFree(m_MessageBlob.pbData); } m_MessageBlob.cbData = 0; m_MessageBlob.pbData = NULL; ReportError(hr); goto UnlockExit; } //////////////////////////////////////////////////////////////////////////////// // // Private member functions. // /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSign::OpenToEncode Synopsis : Open a message for encoding. Parameter: CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo - Pointer to signer's CMSG_SIGNER_ENCODE_INFO structure. DATA_BLOB * pChainBlob - Pointer chain blob of PCCERT_CONTEXT. HCRYPTMSG * phMsg - Pointer to HCRYPTMSG to receive handle. CMSG_SIGNED_ENCODE_INFO * pSignedEncodeInfo - Pointer to CMSG_SIGNED_ENCODE_INFO to receive initialized structure. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::OpenToEncode(CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo, DATA_BLOB * pChainBlob, HCRYPTMSG * phMsg, CMSG_SIGNED_ENCODE_INFO * pSignedEncodeInfo) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; DWORD dwFlags = 0; CERT_BLOB * rgEncodedCertBlob = NULL; DWORD i; DebugTrace("Entering CSignedData::OpenToEncode().\n"); // // Sanity check. // ATLASSERT(pSignerEncodeInfo); ATLASSERT(pChainBlob); ATLASSERT(pChainBlob->cbData); ATLASSERT(pChainBlob->pbData); ATLASSERT(phMsg); ATLASSERT(pSignedEncodeInfo); // // If this is not a root cert, don't include its root, // DWORD cCertContext = pChainBlob->cbData; PCERT_CONTEXT * rgCertContext = (PCERT_CONTEXT *) pChainBlob->pbData; if (1 < cCertContext) { cCertContext--; } // // Allocate memory for the array. // if (!(rgEncodedCertBlob = (CERT_BLOB *) ::CoTaskMemAlloc(cCertContext * sizeof(CERT_BLOB)))) { hr = E_OUTOFMEMORY; DebugTrace("Error: out of memory.\n"); goto ErrorExit; } ::ZeroMemory(rgEncodedCertBlob, cCertContext * sizeof(CERT_BLOB)); // // Build encoded certs array. // for (i = 0; i < cCertContext; i++) { rgEncodedCertBlob[i].cbData = rgCertContext[i]->cbCertEncoded; rgEncodedCertBlob[i].pbData = rgCertContext[i]->pbCertEncoded; } // // Setup up CMSG_SIGNED_ENCODE_INFO structure. // ::ZeroMemory((void *) pSignedEncodeInfo, sizeof(CMSG_SIGNED_ENCODE_INFO)); pSignedEncodeInfo->cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO); pSignedEncodeInfo->cSigners = 1; pSignedEncodeInfo->rgSigners = pSignerEncodeInfo; pSignedEncodeInfo->cCertEncoded = cCertContext; pSignedEncodeInfo->rgCertEncoded = rgEncodedCertBlob; // // Detached flag. // if (m_bDetached) { dwFlags = CMSG_DETACHED_FLAG; } // // Open a message to encode. // if (!(hMsg = ::CryptMsgOpenToEncode(CAPICOM_ASN_ENCODING, dwFlags, CMSG_SIGNED, pSignedEncodeInfo, NULL, NULL))) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgOpenToEncode() failed.\n", hr); goto ErrorExit; } // // Returned message handle to caller. // *phMsg = hMsg; CommonExit: // // Free resources. // if (rgEncodedCertBlob) { ::CoTaskMemFree(rgEncodedCertBlob); } DebugTrace("Leaving CSignedData::OpenToEncode().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (hMsg) { ::CryptMsgClose(hMsg); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::OpenToDecode Synopsis : Open a signed message for decoding. Parameter: HCRYPTPROV hCryptProv - CSP handle or NULL for default CSP. HCRYPTMSG * phMsg - Pointer to HCRYPTMSG to receive handle. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::OpenToDecode (HCRYPTPROV hCryptProv, HCRYPTMSG * phMsg) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; DWORD dwFlags = 0; DWORD dwMsgType = 0; DWORD cbMsgType = sizeof(dwMsgType); DebugTrace("Entering CSignedData::OpenToDecode().\n"); // // Sanity check. // ATLASSERT(phMsg); // // Detached flag. // if (m_bDetached) { dwFlags = CMSG_DETACHED_FLAG; } // // Open a message for decode. // if (!(hMsg = ::CryptMsgOpenToDecode(CAPICOM_ASN_ENCODING, // ANS encoding type dwFlags, // Flags 0, // Message type (get from message) hCryptProv, // Cryptographic provider NULL, // Inner content OID NULL))) // Stream information (not used) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgOpenToDecode() failed.\n"); goto ErrorExit; } // // Update message with signed content. // if (!::CryptMsgUpdate(hMsg, m_MessageBlob.pbData, m_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_SIGNED != dwMsgType) { hr = CAPICOM_E_SIGN_INVALID_TYPE; DebugTrace("Error: not an singed message.\n"); goto ErrorExit; } // // If detached message, update content. // if (m_bDetached) { if (!::CryptMsgUpdate(hMsg, m_ContentBlob.pbData, m_ContentBlob.cbData, TRUE)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgUpdate() failed.\n",hr); goto ErrorExit; } } else { // // Retrieve content. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_CONTENT_PARAM, 0, (void **) &m_ContentBlob.pbData, &m_ContentBlob.cbData))) { DebugTrace("Error [%#x]: GetMsgParam() failed to get CMSG_CONTENT_PARAM.\n", hr); goto ErrorExit; } } // // Returned message handle to caller. // *phMsg = hMsg; CommonExit: DebugTrace("Leaving SignedData::OpenToDecode().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (hMsg) { ::CryptMsgClose(hMsg); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSignedData::SignContent Synopsis : Sign the content by adding the very first signature to the message. Parameter: CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo - Pointer to signer's CMSG_SIGNER_ENCODE_INFO structure. DATA_BLOB * pChainBlob - Pointer chain blob of PCCERT_CONTEXT. VARIANT_BOOL bDetached - Detached flag. CAPICOM_ENCODING_TYPE EncodingType - Encoding type. BSTR * pVal - Pointer to BSTR to receive the signed message. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::SignContent (CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo, DATA_BLOB * pChainBlob, VARIANT_BOOL bDetached, CAPICOM_ENCODING_TYPE EncodingType, BSTR * pVal) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; DATA_BLOB MessageBlob = {0, NULL}; CMSG_SIGNED_ENCODE_INFO SignedEncodeInfo; DebugTrace("Entering CSignedData::SignContent().\n"); // // Sanity check. // ATLASSERT(pSignerEncodeInfo); ATLASSERT(pChainBlob); ATLASSERT(pChainBlob->cbData); ATLASSERT(pChainBlob->pbData); ATLASSERT(pVal); ATLASSERT(m_ContentBlob.cbData); ATLASSERT(m_ContentBlob.pbData); try { // // Initialize member variables. // if (m_MessageBlob.pbData) { ::CoTaskMemFree(m_MessageBlob.pbData); } m_bSigned = FALSE; m_bDetached = bDetached; m_MessageBlob.cbData = 0; m_MessageBlob.pbData = NULL; // // Open message to encode. // if (FAILED(hr = OpenToEncode(pSignerEncodeInfo, pChainBlob, &hMsg, &SignedEncodeInfo))) { DebugTrace("Error [%#x]: OpenToEncode() failed.\n", hr); goto ErrorExit; } // // Update the message with data. // if (!::CryptMsgUpdate(hMsg, // Handle to the message m_ContentBlob.pbData, // Pointer to the content m_ContentBlob.cbData, // Size of the content TRUE)) // Last call { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgUpdate() failed.\n",hr); goto ErrorExit; } // // Retrieve the resulting message. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_CONTENT_PARAM, 0, (void **) &MessageBlob.pbData, &MessageBlob.cbData))) { DebugTrace("Error [%#x]: GetMsgParam() failed to get CMSG_CONTENT_PARAM.\n", hr); goto ErrorExit; } // // Now export the signed 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("ExportedSigned.asn", MessageBlob.pbData, MessageBlob.cbData); // // Update member variables. // // if (m_MessageBlob.pbData) { ::CoTaskMemFree(m_MessageBlob.pbData); } m_bSigned = TRUE; m_bDetached = bDetached; m_MessageBlob = MessageBlob; } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } CommonExit: // // Free resource. // if (hMsg) { ::CryptMsgClose(hMsg); } DebugTrace("Leaving CSignedData::SignContent().\n"); return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (MessageBlob.pbData) { ::CoTaskMemFree(MessageBlob.pbData); } goto CommonExit; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function : CSign::CoSignContent Synopsis : CoSign the content by adding another signature to the message. Parameter: CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo - Pointer to signer's CMSG_SIGNER_ENCODE_INFO structure. DATA_BLOB * pChainBlob - Pointer chain blob of PCCERT_CONTEXT. CAPICOM_ENCODING_TYPE EncodingType - Encoding type. BSTR * pVal - Pointer to pointer to SAFEARRAY to receive the signed message. Remark : ------------------------------------------------------------------------------*/ STDMETHODIMP CSignedData::CoSignContent (CMSG_SIGNER_ENCODE_INFO * pSignerEncodeInfo, DATA_BLOB * pChainBlob, CAPICOM_ENCODING_TYPE EncodingType, BSTR * pVal) { HRESULT hr = S_OK; HCRYPTMSG hMsg = NULL; DATA_BLOB MessageBlob = {0, NULL}; DebugTrace("Entering CSignedData::CoSignContent().\n"); // // Sanity check. // ATLASSERT(pSignerEncodeInfo); ATLASSERT(pChainBlob); ATLASSERT(pChainBlob->cbData); ATLASSERT(pChainBlob->pbData); ATLASSERT(pVal); ATLASSERT(m_bSigned); ATLASSERT(m_ContentBlob.cbData); ATLASSERT(m_ContentBlob.pbData); ATLASSERT(m_MessageBlob.cbData); ATLASSERT(m_MessageBlob.pbData); try { // // Open the encoded message for decode. // if (FAILED(hr = OpenToDecode(pSignerEncodeInfo->hCryptProv, &hMsg))) { DebugTrace("Error [%#x]: OpenToDecode() failed.\n", hr); goto ErrorExit; } // // Add the co-signature to the message. // if (!::CryptMsgControl(hMsg, 0, CMSG_CTRL_ADD_SIGNER, (const void *) pSignerEncodeInfo)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DebugTrace("Error [%#x]: CryptMsgControl() failed for CMSG_CTRL_ADD_SIGNER.\n",hr); goto ErrorExit; } // // Add chain to message. // if (FAILED(hr = ::AddCertificateChain(hMsg, pChainBlob))) { DebugTrace("Error [%#x]: AddCertificateChain() failed.\n", hr); goto ErrorExit; } // // Retrieve the resulting message. // if (FAILED(hr = ::GetMsgParam(hMsg, CMSG_ENCODED_MESSAGE, 0, (void **) &MessageBlob.pbData, &MessageBlob.cbData))) { DebugTrace("Error [%#x]: GetMsgParam() failed to get CMSG_ENCODED_MESSAGE.\n",hr); goto ErrorExit; } // // Now export the signed 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("ExportedCoSigned.asn", MessageBlob.pbData, MessageBlob.cbData); // // Update member variables. // // if (m_MessageBlob.pbData) { ::CoTaskMemFree(m_MessageBlob.pbData); } m_bSigned = TRUE; m_MessageBlob = MessageBlob; } catch(...) { hr = E_INVALIDARG; DebugTrace("Exception: invalid parameter.\n"); goto ErrorExit; } CommonExit: // // Free resource. // if (hMsg) { ::CryptMsgClose(hMsg); } return hr; ErrorExit: // // Sanity check. // ATLASSERT(FAILED(hr)); // // Free resource. // if (MessageBlob.pbData) { ::CoTaskMemFree(MessageBlob.pbData); } goto CommonExit; }