1
votes

i am trying to use the CryptProtectData function so i can encrypt my password and use it inside my MAPI profile. I am using these 2 articles http://blogs.msdn.com/b/dvespa/archive/2013/05/21/how-to-mfcmapi-create-mapi-profile-exchange-2013.aspx and http://blogs.msdn.com/b/dvespa/archive/2013/07/15/create-profile-connect-mfcmapi-to-office-365.aspx for connecting to my hosted exchange(2013) account with MFCMAPI. When setting all my properties i am being prompted for my credentials, and there i got the problem that the field provided for the domain is too short for my domain. So i have to set these properties manually (howto is described in the second article).

Now i need to set username and password in my MAPI profile and it seems like i need to encrpyt the password on my own (i have to build an application to do so). I am using "MAPI Download configuration guidance.docx" (can be downloaded from www .microsoft.com/en-us/download/details.aspx?id=39045 the piece of code i am using is at the end of the document) for building my own application to encrypt my password (i am using the smaller example for just encrypting the password, not for creating the whole profile). There i got a lot of problems, the application didnt run on a 32bit Windows, than the crypt32.lib was missing (i had to create it by my own) and so on. Now i got it running on a 64bit machine, but now i am not sure how to provide my data to the program.

I have the following code:

  std::string stemp = "myPassword";
  std::wstring stemp1 = std::wstring(stemp.begin(), stemp.end());
  LPWSTR pwszPassword = (LPWSTR)stemp1.c_str();//stemp.c_str();//
  HRESULT     hr = S_OK;
  DATA_BLOB   dataBlobIn = {0};
  DATA_BLOB   dataBlobOut = {0};
  SPropValue  propValues[2] = {0};
  // Validate parameters

  // Encrypt password based on local user authentication
  dataBlobIn.pbData = (LPBYTE)pwszPassword;
  // Include NULL character
  dataBlobIn.cbData = (::wcslen(pwszPassword) + 1) * sizeof(WCHAR);

  CryptProtectData(
        &dataBlobIn,
        NULL,
        NULL,
        NULL,
        NULL,
        0,
        &dataBlobOut);

 std::cout<<"\n-- ";
 std::wcout<<(dataBlobOut.cbData);
 std::cout<<" --\n";
 std::wcout<<(dataBlobOut.pbData);                              

Now when outputting these 2 values, for dataBlobOut.cbData i mostly get "230" (i thought that this might change when i change the size of the password, but it does not, it has the same value for passwords like "aaa", "bbbbb", "cc" ...), and for dataBlobOut.pbData i get a Hexadezimal value (something like 0x2cde50) i think it is the address of the variable, since pbData is a pointer.

Since i am getting the exact same values for diffrente passwords i assume that my approach is not right. But what do i have to change to get my encrypted password so i can fill the property PR_PROFILE_AUTH_PASSWORD in my MAPI profile?

I have asked this question also on the Microsoft exchange forum , but i think that their forum is more technically oriented than software development.

Kind regards rimes

2
The fact that the size of the encrypted data doesn't change with the (small variations in) the size of the password doesn't by itself indicate a problem. I bet the algorithm involves some kind of padding, say to a nearest X-byte boundary. The real test is - does CryptUnprotectData give you the original data back?Igor Tandetnik
wcout << (LPCWSTR)(dataBlobIn.pbData)Igor Tandetnik
You seem to expect the result of the encryption to be some human-readable text. That's not what it is - it's going to be a block of random garbage-looking binary data.Igor Tandetnik
As far as I can tell from the articles you cite (I'm not familiar with MAPI myself), you are not supposed to copy/paste encrypted data into a text field in the UI. You are expected to configure your profile programmatically. There, you specify the password material in the form of SBinary structure, which is very similar to DATA_BLOB.Igor Tandetnik
That looks perfectly fine to me. The same code (in Delphi) works just fine. How exactly do you copy the data to the profile?Dmitry Streblechenko

2 Answers

0
votes

Sorry it seems like i didnt post the message with my right account (and with this i cannot write comments).

As i said i am trying to output the encrypted data into a text file:

std::ofstream myfile;
myfile.open ("encrypted.txt");
myfile << (LPCWSTR)(dataBlobIn.pbData);

But when i open my .txt i just get something like "0xca7ee8" (or it is empty when i write it without the (LPCWSTR) ). May this be the "right" output at all? I mean i was expecting a lot more chars but as Igor said that the size of the encrypted password does not neccesercly has to change when i change the size of the plain password, maybe this output is what i need ? (or is there any way so i can verify that this is the encrypted password and not some random address from the memory).

Edit:

I think i got the solution. It seems like i need to output my data this way:

for (unsigned int i=0;i<dataBlobOut.cbData;i++){ 
            myfile<<dataBlobOut.pbData+i; 
     }  

so i get a lot of nonsens data in my text file, but i think that this is my encrypted password. I will check it and report if it worked setting my (encrypted) password in my MAPI profile. But i still have one concern. Does the output hast always to be the same for the same input or can it also change (because when running my program i sometimes get different outputs for the same password) ?

0
votes

You can use something like this:

#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "crypt32")

int main(int argc, char** argv) {
    if(argc != 2) {
        fprintf(stdout, "usage: cpd <string>\n");
        exit(1);
    }

    const char* u = argv[1];

    DATA_BLOB db_i;
    db_i.cbData = static_cast<DWORD>(strlen(u));
    db_i.pbData = (BYTE*)(u);

    DATA_BLOB db_o = {0, NULL};
    if(!::CryptProtectData(&db_i, 
        NULL, NULL, NULL, NULL, 
        CRYPTPROTECT_UI_FORBIDDEN, 
        &db_o)) {
        fprintf(stdout, 
            "CryptProtectData failed with error %ld\n", 
            GetLastError());
    }
    else {
        short nl = 0;
        for(DWORD c = 0; c < db_o.cbData; c++) {
            if((c % 40) == 0)
                fprintf(stdout, "\n");
            fprintf(stdout, "%2.2x", db_o.pbData[c]);
        }
        LocalFree(db_o.pbData);
    }

    return 0;
}

The string is written as hex representation (max line 80). You can accumulate the db_o.pbData formatted into a std::string for serialization (there are multiple ways of doing it - I prefer to allocate a char* for db_o.cbData * 2 + 1 to have 2 chars for hex representation, plus 1 for NUL terminator, then write 2 chars sequentially for each BYTE from pbData).

If crypt32.lib is not available, you can use LoadLibrary/GetProcAddress to load the function(s) dynamically.