#define INITGUID #define DEFINE_STRCONST #include #include #include #include #include "encode.h" #define CORg(command) \ if (FAILED(command)) { \ goto Error; \ } #define CPRg(command) \ if (! (command)) { \ goto Error; \ } static LPWSTR s_rgwszValues[] = { NULL }; #define DEBUGFILE 1 #define MAX_LAYERS 3 //------------------------------------------------------------------ //------------------------------------------------------------------ HRESULT WriteBSTRToMultibyteToStream( const BSTR bstrStr, IStream** ppStream ) { HRESULT hr = S_OK; LARGE_INTEGER liZero = {0}; // for ->Seek() char* pszMessage = NULL; int len = 0; if (!ppStream) return E_INVALIDARG; len = wcslen(bstrStr); CPRg(pszMessage = new char[len + 1]); WideCharToMultiByte(CP_ACP, 0, bstrStr, len, pszMessage, len + 1, NULL, NULL ); pszMessage[len] = '\0'; CORg(CreateStreamOnHGlobal(NULL, TRUE, ppStream)); CORg((*ppStream)->Seek(liZero, STREAM_SEEK_SET, NULL)); CORg((*ppStream)->Write(pszMessage, len, NULL)); CORg((*ppStream)->Seek(liZero, STREAM_SEEK_SET, NULL)); Error: if (pszMessage) delete[] pszMessage; return hr; } //------------------------------------------------------------------ SMimeEncode::SMimeEncode() : m_dwFlags(0), m_stmOutput(NULL), m_szSignAlg(NULL), m_szEncryptAlg(NULL), m_szBody(NULL), m_SigningCertInner(NULL), m_SigningCertOuter(NULL), m_EncryptionCert(NULL), m_hCryptProv(NULL), m_hMYCertStore(NULL), m_hCACertStore(NULL), m_hABCertStore(NULL), m_szSenderEmail(NULL), m_szSenderName(NULL), m_szRecipientEmail(NULL), m_szRecipientName(NULL), m_szOutputFile(NULL) { } #define APPEND_SEPERATOR(subject) \ if (lstrlen(subject)) { \ lstrcat(subject, " | "); \ } //------------------------------------------------------------------ SMimeEncode::~SMimeEncode() { // BUGBUG: Should clean up any allocated members } //------------------------------------------------------------------ HRESULT SMimeEncode::HrConfig( DWORD dwFlags, LPTSTR lpszBody, HCRYPTPROV hCryptProv, HCERTSTORE hMYCertStore, HCERTSTORE hCACertStore, HCERTSTORE hABCertStore, PCCERT_CONTEXT lpSigningCertInner, PCCERT_CONTEXT lpSigningCertOuter, PCCERT_CONTEXT lpEncryptionCert, LPTSTR lpszSenderEmail, LPTSTR lpszSenderName, LPTSTR lpszRecipientEmail, LPTSTR lpszRecipientName, LPTSTR lpszOutputFile ) { HRESULT hr = S_OK; static char szSubject[257] = ""; if (dwFlags & encode_Encrypt) { // specify an encryption algorithm // BUGBUG: Hardcoded in Encode } if (dwFlags & encode_InnerSign) { // specify a signing algorithm // BUGBUG: Hardcoded in Encode } if (dwFlags & encode_OuterSign) { // specify a signing algorithm // BUGBUG: Hardcoded in Encode } m_dwFlags = dwFlags; m_szBody = lpszBody; m_hCryptProv = hCryptProv; m_hMYCertStore = hMYCertStore; m_hCACertStore = hCACertStore; m_hABCertStore = hABCertStore; m_SigningCertInner = (PCERT_CONTEXT)lpSigningCertInner; m_SigningCertOuter = (PCERT_CONTEXT)lpSigningCertOuter; m_EncryptionCert = (PCERT_CONTEXT)lpEncryptionCert; m_szSenderEmail = lpszSenderEmail; m_szRecipientEmail = lpszRecipientEmail; m_szOutputFile = lpszOutputFile; // Set a meaningful subject lstrcpy(szSubject, ""); if (dwFlags & encode_InnerSign) { APPEND_SEPERATOR(szSubject); if (dwFlags & encode_InnerClear) { lstrcat(szSubject, "Clear Sign"); } else { lstrcat(szSubject, "Opaque Sign"); } } if (dwFlags & encode_Encrypt) { APPEND_SEPERATOR(szSubject); lstrcat(szSubject, "Encrypt"); } if (dwFlags & encode_OuterSign) { APPEND_SEPERATOR(szSubject); if (dwFlags & encode_OuterClear) { lstrcat(szSubject, "Clear Sign"); } else { lstrcat(szSubject, "Opaque Sign"); } } m_szSubject = szSubject; return(hr); } //------------------------------------------------------------------ HRESULT SMimeEncode::HrExecute(void) { // Using the SMIME engine: // // Build the message tree (attach the body) // CoCreateInstance( CLSID_IMimeSecurity ) // InitNew() // pSMIMEEngine->EncodeBody( IMimeMessageTree*, // hRoot, // SEF_??? | EBF_RECURSE ) OR ??? // HrEncodeOpaque( psi, pTree, hbody, pencoderoot, pstmOut ) ???? // HRESULT hr = S_OK; LARGE_INTEGER liZero = {0}; // for ->Seek() IStream* pBuildStream = NULL; // scratch stream IStream* pResultStream = NULL; // scratch stream IMimeMessage* pMimeRoot = NULL; // message in process IMimeBody* pMimeRootBody = NULL; // another version IMimeInternational* pCharSet = NULL; HCHARSET HCharset = 0; SYSTEMTIME stNow; PROPVARIANT var; HBODY hbBody; IMimeSecurity* pMimeSecurity = NULL; ULONG dwSecurityType = MST_NONE; IPersistFile* pIPFFileStore = NULL; HRESULT hrLocal = S_OK; WCHAR szwFileName[MAX_PATH + 1]; CHAR szFrom[2 * (MAX_PATH + 1) + 1]; // Multilayer stuff BOOL fTripleWrap = m_dwFlags & encode_OuterSign; ULONG ulSecurityLayers = 0; ULONG iEncryptLayer = (ULONG)-1; ULONG iInnerSignLayer = (ULONG)-1; ULONG iOuterSignLayer = (ULONG)-1; // Arrays of option values to set DWORD rgdwSecurityType[MAX_LAYERS] = {0}; PCCERT_CONTEXT rgdwCertSigning[MAX_LAYERS] = {0}; HCERTSTORE rgdwhCertStore[MAX_LAYERS] = {0}; // optional DWORD rgdwUserValidity[MAX_LAYERS] = {0}; // decode only DWORD rgdwROMsgValidity[MAX_LAYERS] = {0}; // decode only FILETIME rgftSigntime[MAX_LAYERS] = {0}; // optional PROPVARIANT rgpvAlgHash[MAX_LAYERS] = {0}; PROPVARIANT rgpvSymcaps[MAX_LAYERS] = {0}; PROPVARIANT rgpvAuthattr[MAX_LAYERS] = {0}; // optional PROPVARIANT rgpvUnauthattr[MAX_LAYERS] = {0}; // optional // This is the ALOGORITHM ID for SHA1, default supported signing alg const BYTE c_SHA1_ALGORITHM_ID[] = {0x30, 0x09, 0x30, 0x07, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A}; // This is the ALOGORITHM ID for RC2 -- 40 bit, the default encrypt const BYTE c_RC2_40_ALGORITHM_ID[] = {0x30, 0x0F, 0x30, 0x0D, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x02, 0x02, 0x01, 0x28}; // get the signing cert from my store if (! m_hCryptProv || ! m_hMYCertStore || ! m_hCACertStore || ! m_hABCertStore) { hr = E_FAIL; goto Error; } // Create the Message object // CORg(CoCreateInstance(CLSID_IMimeMessage, NULL, CLSCTX_INPROC_SERVER, IID_IMimeMessage, (LPVOID*)&pMimeRoot)); CORg(pMimeRoot->InitNew()); CORg(CreateStreamOnHGlobal( NULL, TRUE, &pBuildStream)); CORg(pBuildStream->Seek(liZero, STREAM_SEEK_SET, NULL)); CORg(pBuildStream->Write(m_szBody, lstrlen(m_szBody), NULL)); CORg(pBuildStream->Seek(liZero, STREAM_SEEK_SET, NULL)); CORg(pMimeRoot->SetTextBody(TXT_PLAIN, IET_8BIT, NULL, pBuildStream, &hbBody)); // Create the formatted From address if (m_szSenderName) { lstrcpy(szFrom, "\""); lstrcat(szFrom, m_szSenderName); lstrcat(szFrom, "\" "); } else { lstrcpy(szFrom, ""); } lstrcat(szFrom, "<"); lstrcat(szFrom, m_szSenderEmail); lstrcat(szFrom, ">"); var.vt = VT_LPSTR; var.pszVal = szFrom; // From Email CORg(hr = pMimeRoot->SetProp(PIDTOSTR(PID_HDR_FROM), 0, &var)); var.vt = VT_LPSTR; // ignored? var.pszVal = (LPSTR) STR_MIME_TEXT_PLAIN; CORg(pMimeRoot->SetBodyProp(hbBody, STR_HDR_CNTTYPE, 0, &var)); var.vt = VT_LPSTR; // ignored? var.pszVal = (LPSTR) STR_ENC_QP; CORg(pMimeRoot->SetBodyProp(hbBody, STR_HDR_CNTXFER, 0, &var)); // Set subject var.vt = VT_LPSTR; var.pszVal = (LPSTR) m_szSubject; CORg(pMimeRoot->SetBodyProp(hbBody, STR_HDR_SUBJECT, 0, &var)); CORg(pMimeRoot->BindToObject(HBODY_ROOT, IID_IMimeBody, (LPVOID*)&pMimeRootBody)); // // Set the security options // // How many layers? if (m_dwFlags & encode_InnerSign) { iInnerSignLayer = ulSecurityLayers; ulSecurityLayers++; } if (m_dwFlags & encode_Encrypt) { iEncryptLayer = ulSecurityLayers; // index in arrays ulSecurityLayers++; } if (m_dwFlags & encode_OuterSign) { iOuterSignLayer = ulSecurityLayers; ulSecurityLayers++; } // Set up for Inner Signing if (m_dwFlags & encode_InnerSign) { // specifiy the Security Type for this layer rgdwSecurityType[iInnerSignLayer] = m_dwFlags & encode_InnerClear ? MST_THIS_SIGN : MST_THIS_BLOBSIGN; dwSecurityType |= m_dwFlags & encode_InnerClear ? MST_THIS_SIGN : MST_THIS_BLOBSIGN; // Specify the Signing Time for this layer GetSystemTime(&stNow); SystemTimeToFileTime(&stNow, &rgftSigntime[iInnerSignLayer]); // specify the signature alg for this layer rgpvAlgHash[iInnerSignLayer].vt = VT_BLOB; rgpvAlgHash[iInnerSignLayer].blob.cbSize = sizeof(c_SHA1_ALGORITHM_ID); rgpvAlgHash[iInnerSignLayer].blob.pBlobData = (BYTE*)c_SHA1_ALGORITHM_ID; // Specify the signing cert for this layer rgdwCertSigning[iInnerSignLayer] = m_SigningCertInner; // HCERTSTORE rgdwhCertStore[MAX_LAYERS] = {0}; // optional // PROPVARIANT rgpvSymcaps[MAX_LAYERS] = {0}; // PROPVARIANT rgpvAuthattr[MAX_LAYERS] = {0}; // optional // PROPVARIANT rgpvUnauthattr[MAX_LAYERS] = {0}; // optional } // Set up for Outer Signing if (m_dwFlags & encode_OuterSign) { // specifiy the Security Type for this layer rgdwSecurityType[iOuterSignLayer] = m_dwFlags & encode_InnerClear ? MST_THIS_SIGN : MST_THIS_BLOBSIGN; dwSecurityType |= m_dwFlags & encode_OuterClear ? MST_THIS_SIGN : MST_THIS_BLOBSIGN; // Specify the Signing Time for this layer GetSystemTime(&stNow); SystemTimeToFileTime(&stNow, &rgftSigntime[iOuterSignLayer]); // specify the signature alg for this layer rgpvAlgHash[iOuterSignLayer].vt = VT_BLOB; rgpvAlgHash[iOuterSignLayer].blob.cbSize = sizeof(c_SHA1_ALGORITHM_ID); rgpvAlgHash[iOuterSignLayer].blob.pBlobData = (BYTE*)c_SHA1_ALGORITHM_ID; // Specify the signing cert for this layer rgdwCertSigning[iOuterSignLayer] = m_SigningCertOuter; // HCERTSTORE rgdwhCertStore[MAX_LAYERS] = {0}; // optional // PROPVARIANT rgpvSymcaps[MAX_LAYERS] = {0}; // PROPVARIANT rgpvAuthattr[MAX_LAYERS] = {0}; // optional // PROPVARIANT rgpvUnauthattr[MAX_LAYERS] = {0}; // optional } // Set up for Encrypting if (m_dwFlags & encode_Encrypt) { HCERTSTORE aCertStores[3]; // // BUGBUG: Hardcoded to RC2 40-bit var.vt = VT_BLOB; var.blob.cbSize = sizeof( c_RC2_40_ALGORITHM_ID ); var.blob.pBlobData = (BYTE*) c_RC2_40_ALGORITHM_ID; CORg(hr = pMimeRootBody->SetOption(OID_SECURITY_ALG_BULK, &var)); // for encryption, get to the right cert store.... // var.caul.cElems = 3; aCertStores[0] = CertDuplicateStore(m_hCACertStore); aCertStores[1] = CertDuplicateStore(m_hMYCertStore); aCertStores[2] = CertDuplicateStore(m_hABCertStore); var.caul.pElems = (ULONG*)aCertStores; CORg(hr = pMimeRootBody->SetOption(OID_SECURITY_SEARCHSTORES, &var)); var.vt = VT_VECTOR | VT_UI4; var.caul.cElems = 1; var.caul.pElems = (ULONG*)&m_EncryptionCert; CORg(pMimeRootBody->SetOption(OID_SECURITY_RG_CERT_ENCRYPT, &var)); #ifdef BUGBUG // This isn't right, is it? // include the cert... var.vt = VT_VECTOR | VT_UI4; var.caul.cElems = 1; var.caul.pElems = (ULONG*)&m_EncryptionCert; CORg(pMimeRootBody->SetOption(OID_SECURITY_RG_CERT_BAG, &var)); #endif // OLD_STUFF dwSecurityType |= MST_THIS_ENCRYPT; rgdwSecurityType[iEncryptLayer] = MST_THIS_ENCRYPT; } // Set the OID_SECURITY_TYPE if (fTripleWrap) { var.vt = VT_VECTOR | VT_UI4; var.caul.cElems = ulSecurityLayers; var.caul.pElems = rgdwSecurityType; CORg(pMimeRootBody->SetOption(OID_SECURITY_TYPE_RG, &var)); var.vt = VT_VECTOR | VT_FILETIME; var.cafiletime.cElems = ulSecurityLayers; var.cafiletime.pElems = rgftSigntime; CORg(pMimeRootBody->SetOption(OID_SECURITY_SIGNTIME_RG, &var)); var.vt = VT_VECTOR | VT_UI4; var.caul.cElems = ulSecurityLayers; var.caul.pElems = (DWORD *)rgdwCertSigning; CORg(pMimeRootBody->SetOption(OID_SECURITY_CERT_SIGNING_RG, &var)); var.vt = VT_VECTOR | VT_VARIANT; var.capropvar.cElems = ulSecurityLayers; var.capropvar.pElems = rgpvAlgHash; CORg(pMimeRootBody->SetOption(OID_SECURITY_ALG_HASH_RG, &var)); var.vt = VT_VECTOR | VT_VARIANT; var.capropvar.cElems = ulSecurityLayers; var.capropvar.pElems = rgpvAlgHash; CORg(pMimeRootBody->SetOption(OID_SECURITY_ALG_HASH_RG, &var)); } else { // Security Type var.vt = VT_UI4; var.ulVal = dwSecurityType; CORg(pMimeRootBody->SetOption(OID_SECURITY_TYPE, &var)); if (dwSecurityType & MST_SIGN_MASK) { // Signing Time var.vt = VT_FILETIME; memcpy(&var.filetime, &rgftSigntime[iInnerSignLayer], sizeof(FILETIME)); CORg(pMimeRootBody->SetOption(OID_SECURITY_SIGNTIME, &var)); // Hash Algorithm var.vt = VT_BLOB; memcpy(&var.blob, &rgpvAlgHash[iInnerSignLayer].blob, sizeof(BLOB)); CORg(hr = pMimeRootBody->SetOption(OID_SECURITY_ALG_HASH, &var)); // Signing Cert var.vt = VT_UI4; var.ulVal = (ULONG)m_SigningCertInner; CORg(pMimeRootBody->SetOption(OID_SECURITY_CERT_SIGNING, &var)); } } // Set the HWND for CAPI calls var.vt = VT_UI4; var.ulVal = 0; CORg(pMimeRootBody->SetOption(OID_SECURITY_HWND_OWNER, &var)); // all built, get rid of the shadow pointer we are holding // pMimeRootBody->Release(); pMimeRootBody = NULL; pMimeRoot->Commit(0); // SMIME Engine // CORg(CoCreateInstance(CLSID_IMimeSecurity, NULL, CLSCTX_INPROC_SERVER, IID_IMimeSecurity, (LPVOID*) &pMimeSecurity)); CORg(pMimeSecurity->InitNew()); // ERRORMESSAGE( Unable to encrypt/encode string ) CORg(pMimeSecurity->EncodeBody(pMimeRoot, HBODY_ROOT, EBF_RECURSE | SEF_SENDERSCERTPROVIDED | SEF_ENCRYPTWITHNOSENDERCERT | EBF_COMMITIFDIRTY)); // Get an Hcharset to force the encoding correctly // CORg(CoCreateInstance(CLSID_IMimeInternational, NULL, CLSCTX_INPROC_SERVER, IID_IMimeInternational, (LPVOID*)&pCharSet)); CORg(pCharSet->FindCharset("UTF-8", &HCharset)); CORg(pMimeRoot->SetCharset(HCharset, // HCharset CSET_APPLY_ALL)); // Applytype // dump it to a file // MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, m_szOutputFile, -1, szwFileName, MAX_PATH); CORg(pMimeRoot->QueryInterface(IID_IPersistFile, (LPVOID*)&pIPFFileStore)); CORg(pIPFFileStore->Save(szwFileName, FALSE)); // extract the whole message into a stream // CORg(CreateStreamOnHGlobal(NULL, TRUE, &pResultStream)); CORg(pMimeRoot->Save(pResultStream, FALSE)); Error: if (pBuildStream) pBuildStream->Release(); if (pResultStream) pResultStream->Release(); if (pMimeRoot) pMimeRoot->Release(); if (pMimeRootBody) pMimeRootBody->Release(); if (pCharSet) pCharSet->Release(); if (pMimeSecurity) pMimeSecurity->Release(); if (pIPFFileStore) pIPFFileStore->Release(); return(hr); }