How rdp passwords are encrypted

mstsc icon Ever wondered how mstsc saves passwords? If you open an RDP file with a text editor like Notepad you can see the encrypted password. In this article I will show you how to encrypt and decrypt these passwords. Besides password recovery this enables you to create rpd files programmatically or perhaps update the password in many rdp files with a batch file.

So if we save a password with mstsc what does it look like? To see that open an rdp file with a text editor (eg Notepad) and you will see something like this:

As you can see the password is somehow encrypted before it was saved and automatically decrypted when you open the file with mstsc. .

I did some analysis on mstsc.exe and mstsc uses the functions CryptProtectData and CryptUnProtectData which are both exported by crypt32.dll. Since those API calls are documented let’s try to encrypt a password with mstsc and one ourselves and compare the output:

So we encrypt the password “secret” and compare this with the same password saved by mstsc
MSTSC:

CryptRDPPassword:

As you can see the length of a password encrypted by mstsc is 1329 bytes and the one from CryptRDPPassword is smaller. Actually mstsc generates fixed length passwords and our function’s length is based upon the password. Even more strange is that mstsc password is always 1329 bytes because the input password is unicode which should produce an even size length. It doesn’t really matter though because mstsc.exe accepts the smaller sized passwords without any problems.
For now I will leave with a demo tool I wrote to encrypt and decrypt rdp passwords. But in a followup article I will show you how te encrypt the passwords to the full length 1329 bytes the same way mstsc does.

RDP Screenshot

Remote Desktop Password Encryption & Decryption Tool (30852)

Do you find this article usefull, why not leave a comment?

Leave a Reply

  1. I always thought the RDP key was easy to decrypt, but it still seems safe because you can only use it for local rdp files. If I grab an RDP from another computer, I get a :
    I receive a “error decrypting: key not valid for use in specified state.”
    When using other security pen test tools they also do not work for non-local RDP files.
    Other then someone owning the remote computer (at which point I’m hosed anyway), what is the risk of storing passwords in RDP files?

  2. The password is encrypted and hashed with the SID (Security Identifier) of the Windows user account. So it’s reasonably safe since you need both the computer and the user account.

  3. Pingback: Jason Conger Blog » Blog Archive » RDP File Password Encryption

  4. Hello,

    I tried doing this in vc++ but the password never seems to work be able to decrypt with your program.

    DATA_BLOB in, out;
    BYTE *pbDataInput =(BYTE *)”secret”;
    DWORD cbDataInput = strlen((char*)pbDataInput)*sizeof(WCHAR);
    in.pbData=pbDataInput;
    in.cbData=cbDataInput;
    CryptProtectData(&in,L”psw”,NULL,NULL,NULL,CRYPTPROTECT_UI_FORBIDDEN,&out);

    char* encPass=(char*)calloc(out.cbData*2,1)
    int count=0;
    while ( count <= out.cbData ){
    sprintf(encPass,”%s%02X”,encPass,out.pbData[count]);
    count++;
    }
    MessageBox(NULL,encPass,”Encrypted password”,MB_OK);

    Can you verify what might be wrong here?
    I have tried several variations of the above code but no matter what I try it never seems to decrypt properly.

  5. You have to convert out.pbData (pointer to byte) to a string of hex, I think that’s where it goes wrong. Does your output resemble mine?
    I convert it like this:

    while (I > 0) do begin
    Dec(I);
    HexStr := HexStr + IntToHex(P^, 2);
    Inc(P);
    end;

    (where P = out.pbData, I=out.cbSize)

  6. Hi,
    i also tried with CryptProtectData, in Python and C++ but never got it to work.

    My Python solution:

    import win32crypt

    pwdHashStr=””
    pwdHash = win32crypt.CryptProtectData(“PASSWORD”,’psw’,None,None,None,0)

    ##Convert to hex
    for char in pwdHash:
    pwdHashStr+=”%02X” % ord(char)

    print “\nPWD Hash:”,pwdHashStr

    print “\nUncrypt:”
    print win32crypt.CryptUnprotectData(pwdHash,None,None,None,0)

    Output:

    PWD Hash: 01000000D08C9DDF0115D1118C7A00C04FC297EB010000004A27EBCD6B02F2448918F77C9638045A0000000008000000700073007700000003660000A800000010000000C6408D7DD8AAE0EB8A79D0C19FABAD8B0000000004800000A00000001000000082E84BC5CE10A8E7F26DE3364A317FA810000000C7890ADAB5BEA9E5A3B610801F4F2C971400000024083669349AC945DFEE697CB49F2D6404BE6E00

    Uncrypt:
    [u’psw’, ‘PASSWORD’]

    But this never worked in RDP, when I put my Hash in your RDP.exe to decode the string I always get ?? as result.

    What is wrong?

    PS: Victor, does it work at your site?

  7. PS: My Hash look’s like this:

    01000000D08C9DDF0115D1118C7A00C04FC297EB010000004A27EBCD6B02F2448918F77C9638045A0000000008000000700073007700000003660000A80000001000000057002AA83DA5F9A74789FC43EA60205D0000000004800000A000000010000000F788991DB7B4DFDE1B3D0AE3F31802FA10000000EA7B94B97D21A81E3AF8A4203B7389F5140000007F255A17BA47CCD8B1FB7DC9CC182B9B0F9F0AD1

    Your hash, on the same machine:

    01000000D08C9DDF0115D1118C7A00C04FC297EB01000000283D9E8DDFEB004A912EDC94A2CF3C730400000008000000700073007700000003660000A80000001000000095ECF426B3AC7BE103C0AF75821F389B0000000004800000A000000010000000AA374CEF59A65D7602669A09378A49D618000000873493205666BAE282951B87DF1E4474AF81C263B36E28A014000000F3C8A5933DC4F37728C5EFB4EEEC5951FC36EB26

  8. Dirk you are not using Unicode, that’s why your hash is too short. Convert your cleartext password to Unicode before passing it in to CryptProtectData. Maybe Victor has the same issue?

  9. Pingback: Encrypt RDP password in Python | RemkoWeijnen.nl

  10. Hey,

    you wrote: “But in a followup article I will show you how te encrypt the passwords to the full length 1329 bytes the same way mstsc does.”

    But where can i find this article?? :)

    Regards Zero

  11. Don’t worry the followup will appear shortly!
    I will write a new demo app to accompany the article that encrypts the full length password and can encrypt password that are valid for machine instead of user.
    This makes it possible (eg) to publish an rdp connection.

  12. I would like to generate the RDP file on the fly from a web server so that the file does not exist anywhere on disk. I would simply like to be able pass a function the password and have the return value as the hash that I could use in generating the RDP.

    In this way I could assign passwords to the TS accounts that the user never knows and the RDP file would be created and returned to the user so they can access their RDP applications. Since the user is already logged into my server via a web portal they could easily click on a link on the web page and access their RDP applications without logging in again.

  13. Simon, I don’t think this will work. An encrypted password is only valid for the Windows user who encrypted it (or in case op special encryption options for all users on the Windows machine it was encrypted on).

  14. Remko, since I am the administrator of the server I can setup the user and the passwords which means I can login as the required user and generate the hash. So I will simply store them in an encrypted table which I could then use to get the appropriated hash when dynamically creating the rdp file. I think this should work and achieve the same goal.

  15. @simon: do you need a commandlinetool for this (just like decksta)?

    @decksta: not yet, maybe you can use the Python version that was submitted by a reader? I plan to make a commandline version in the followup article though but time is lacking at the moment.

  16. At the moment I do not need a command line version at present but who knows down the road it might be handy.

  17. Hey,

    i write some Time before!
    I`m always waiting for the App that creates the real RDP Password String!
    PLS let me know if the Application is ready!

    Regards Zero!

  18. hi the information is helpfull.
    how ever can anybody help me with a compleate c++ class that can lunch the remote desktop client (with user name and password i have them) programatically.

    my idea is to create a .rdp file programatially using a given username and password then lunch the remote desktop client application using this file.

    I know it looks like I am a bit lazy , but its not so.
    I will learn deeply the code and change it to fit my project. I need a sort-cut to sturt learning this issue faster. (wish to go hier in code knowlege as fast as possible).

  19. Hi,
    I’ve tried to encrypt using CryptProtectData() and then converting the DATA_BLOB to string. In addition, I created a .RDP file using Remote Desktop Connection client with the same password.
    The hash value obtained from my program is always different from the one in the .RDP file.
    The string used is “PASSWORD”

    My Hash value:
    01000000d08c9ddf0115d1118c7a00c04fc297eb010000009448dc0c98616943b32c279bf72dedd8000000001e00000045006e00630072007900700074006500640020006400610074006100000003660000a800000010000000826a248787f46b3c72a842fe47c4e4540000000004800000a0000000100000005f2a9a1972fd97deb41a9fd1f981e9a318000000e70bf7d99b9a00c97e222ac9cdfb9e7724e0174b8fdd932a14000000956f52f5d0f96825fcebe5a0c28475160cb6810f

    Generated Hash Value:
    01000000D08C9DDF0115D1118C7A00C04FC297EB010000009448DC0C98616943B32C279BF72DEDD80000000008000000700073007700000003660000A8000000100000006847E172F2395916195B27E7DB40ED610000000004800000A00000001000000047C2181F972C3B7F7729E42873D392EA08020000982634568984F785E52AD05245AA9684C6EBC0ACF4E61540464C8CFC1121B4DC951F45E71425F4F7588D9AAF06F20BE2CE220CF46FC86242C828DC5AF1A3E26707FE6322EB2CCB473D6DE52C89677F2CA0EE644452E3E94F2B6B976883F37D2BAADF4393297A54A2006892F7CCF3F843A223C73EA28D1C219BEC85D6CB775F959893635E15DE95453A76DE1862C16B754902FA7A60FACED79B2DACC6E1255F93FAAD0525706E74A4B6897E13FF714F9451EB3223DBC2DD21E0EA3DA6C7495A27C7AE108D874907E1468AF955809BA2F3033AD37EA583BE04859D06167973919DDE833AED8F932879C38C4C21330EDA108F9D3BEE304060C85D493BD9F549825AD2064E62B0CD84444EAC63F8534F5548D1FA2F159297722BC4AB1E7349CDA02592F4ED9B4E03C3A874FD0C1CC812BBA8028F3DA5BDBB33B20F3401732E868B6F01599101C783197DB77A5787D78F2205F4A2524ECCAD3BFDA6AF2859F3BD139233658393C27BFB27BAEB24C8252ABA4E975EE4FB05D6E8D9F605A44E1B664B39569D919FBE2F6EB5DA927976BB3F9A7C1977B97077607D8F2E8759AEC8C5430279F2CEB71A48F7D71C40327B6D0A2469C923F40083441826C10010AA90AC4B0E286880F9FBFF84FB04CEE8D2A3F85D5D319E76732D677AD56A8F603C410F2E5AEEEEAA70745EE7E0B01AD959A7DE5D526E54C0B488938AEDB5FE35E5FD0E1847B2600374DB040DDF1400000062637475F860671DEAA7CBCD8D00092DFF1F40E40

    My Code snippet:

    std::wstring strTmp = CT2W(password.GetString());
    BYTE *pbDataInput = (BYTE *)(strTmp.c_str());
    DWORD cbDataInput = wcslen((wchar_t *)pbDataInput) * sizeof(wchar_t);

    CString encryptedData;

    DataIn.pbData = pbDataInput;
    DataIn.cbData = cbDataInput;

    if(CryptProtectData(&DataIn,
    L”cyr”,
    NULL,
    NULL,
    NULL,
    CRYPTPROTECT_UI_FORBIDDEN ,
    &DataOut))
    {
    //Convert the Byte to string

    BYTE* pb;
    pb = DataOut.pbData;

    for(DWORD i =0; i != DataOut.cbData; i++)
    {
    unsigned char ch =*pb;
    unsigned char ch0 = ch & 0xf0;
    ch0 = (ch>>4) & 0x0F;
    unsigned char ch1 = ch & 0x0F;

    encryptedData.AppendFormat(_T(“%x%x”),ch0,ch1);
    pb++;
    }

  20. hi,
    i have mstsc 6.0, but in my RDP file i dont find the line :
    password 51:b: !!!
    even when i check the checkedbox : save the password on the console…
    should i add the line by my self !?

  21. @mehdi: the version 6 client cannot save the password but can read passwords saved in .rdp file. So you can indeed place it in with notepad. I use also a modified version of the old client that can be used without installing the v6 client to save password in .rdp files.

  22. @Landon: yes the string generated by mstsc is longer, it’s always fixed size 3129 bytes. It doesn’t matter though your shorter hash should work as well. Please remember that you need to encode the password in unicode so LPWSTR (PWideChar in Delphi).

  23. Hello,
    I know it’s not really what you are working on here, but I want to be able to use remote desktop without the password encryption so the password stays with the file wherever it is (on any machine/any user). I have no great security concerns, and the encryption is a royal pain to me. I installed version 5.1 on Vista Ultimate. It was tricky, but I did it. I would be very grateful for a solution.

  24. @FrankO: You could use my LaunchRDP commandline tool. It accepts paramaters like servername, username and password. You could just save it in a .bat file.

  25. how to encrypt the password as similar as mstsc encryption( 1329) bytes. Is there is any code?

  26. thanks remko. how to encrypt the password as similar as mstsc encryption( 1329) bytes. Is there is any code?

  27. Any luck on the follow-up post?
    I’d like to have the demo of the encryptor with the full 1329 chars.

  28. Hi remko,

    I had following C code and generated RDP password string, but it doesn’t work when I pasted in .rdp file or use your tool to decrypted, but it can be decrypted by my program, what’s wrong with it?

    #include
    #include
    #include

    int main(void) {

    int count =0;
    char *encPass;
    DATA_BLOB DataIn;
    DATA_BLOB DataOut;
    LPWSTR description = NULL;
    LPWSTR pwd = L”ssl”;
    DataIn.pbData = (BYTE *)pwd;

    if (DataIn.pbData == NULL) {
    return 0;
    }

    DataIn.cbData = (DWORD)strlen((const char*)DataIn.pbData) + 1;

    if (CryptProtectData(
    &DataIn,
    (LPCWSTR)L”simple.wincrypt”,
    NULL,
    NULL,
    NULL,
    CRYPTPROTECT_UI_FORBIDDEN,
    &DataOut))
    {
    printf(“encryption phase worked. \n”);
    encPass = (char*)calloc(DataOut.cbData*2,1);
    for (count=0; count cl Printf11.c /link crypt32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80×86
    Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

    Printf11.c
    Microsoft (R) Incremental Linker Version 6.00.8168
    Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

    /out:Printf11.exe
    crypt32.lib
    Printf11.obj

    C:\workspace>Printf11
    encryption phase worked.
    01000000D08C9DDF0115D1118C7A00C04FC297EB010000005AC022B53B045C488238D63E13C750D8
    0000000020000000730069006D0070006C0065002E00770069006E00630072007900700074000000
    03660000A800000010000000A8A5C34F23D6424D9D95DFCA47DC830A0000000004800000A0000000
    10000000C477C79214DB8016909E9B2DFBBC56F0080000003933B47CC7B9D98314000000517D0F50
    FA8804EB2254B66D3518570D21D5395F

  29. Here is the code again:

    int main(void) {

    int count =0;
    char *encPass;
    DATA_BLOB DataIn;
    DATA_BLOB DataOut;
    LPWSTR description = NULL;
    LPWSTR pwd = L”ssl”;
    DataIn.pbData = (BYTE *)pwd;

    if (DataIn.pbData == NULL) {
    return 1;
    }

    DataIn.cbData = (DWORD)strlen((const char*)DataIn.pbData) + 1;

    if (CryptProtectData(
    &DataIn,
    (LPCWSTR)L”simple.wincrypt”,
    NULL,
    NULL,
    NULL,
    CRYPTPROTECT_UI_FORBIDDEN,
    &DataOut))
    {
    printf(“encryption phase worked. \n”);
    encPass = (char*)calloc(DataOut.cbData*2,1);
    for (count=0; count < DataOut.cbData; count++ ){

    sprintf(encPass, “%s%02X”, encPass, DataOut.pbData[count]);
    }
    printf(“%s”, encPass);

    LocalFree(DataOut.pbData);
    } else {
    printf(“encryption Fail. \n”);
    }
    return 0;
    }

  30. @askQ: I think you are passing a wrong size to the cbData parameter. You should pass length in bytes (not charcount!) without the #0 terminator.

  31. Remko,

    Thanks for providing your code and your utility.

    I have 2 issues if you don’t mind me asking:

    1) The only issue I have with the LaunchRDP util is that there is no “top bar (the one with minimize)” to get out of RDP, I have to ALT-TAB. Am I passing something wrong to the LaunchRDP util?

    2) .NET comes with a wrapper to use CryptProtectData(), but I couldn’t get it working. Has anyone done this in .NET (C# or VB)?

  32. I’ve got an encoding problem too, but I can’t figure out where it’s going wrong.

    The first part of my hashcode is coming out like this:
    1000D08C9DDF115D

    Running your enc util, your hash code comes out like this:
    01000000D08C9DDF0115D

    Hmm, looks like mine is 8-bit and yours is 16-bit or something?

    Sorry for the trouble…

  33. Maybe it’s my hex conversion that’s wrong, hmm, any ideas anyone? (See above post)

  34. For anyone else having this issue in .NET

    I seemed to have fixed it by doing 2 things:

    1) Altered this VB.NET sample code to return bytes in the main encrypt function instead of the base 64 string:
    http://www.obviex.com/samples/dpapi.aspxadding

    2) Added a for next loop to add leading 0’s to the hex conversion

    If someone needs the full code, let me know and I guess I’ll paste it, but it’s so long that I did not want to paste it in here.

    ‘Convert user inputted password to unicode
    Dim unicodePass As String = Encoding.Unicode.GetString(Encoding.ASCII.GetBytes(tb1.Text))

    Dim encBytes() As Byte = modDAPI.DPAPI.Encrypt(DPAPI.KeyType.UserKey, unicodePass, Nothing, “psw”)
    ‘tbOut.Text = encBytes.ToString

    Dim I As Integer

    For I = 0 To encBytes.Length – 1
    If encBytes(I).ToString.Length = 1 Then ‘Add 0’s because the .NET hex conversion returns no leading 0 placeholder for Hex
    tbOut.Text += “0” & Hex(encBytes(I))
    Else
    tbOut.Text += Hex(encBytes(I))
    End If

    Next

  35. @coderguy: so what’s your remaining problem? If you encrypt the same password the output size should be equal.

  36. Pingback: How rdp passwords are encrypted 2 | RemkoWeijnen.nl

  37. Any chance some one would be kind enough to post a command line tool for doing this?

  38. I am working with vb.net like coderguy and used the information provided by him when I wrote my encryption program. The output from your tool and the output from my tool vary slightly. Coderguy, do you have your tool working properly?

    Yours 01000000D08C9DDF0115D1118C7A00C04FC297EB01000000A4013C8EBA796A498B162E334AE3E05D0400000008000000700073007700000003660000A8000000100000003EBAC3BF9275344D8ECFED55E5E041A50000000004800000A0000000100000005B260261C9ACE8F62800A8F795019DB610000000EE00F714D561B363DFFD0944DA0AFE7C1400000073868F5861436ACB68FBF7233829208607C81C3F

    Mine 01000000D08C9DDF0115D1118C7A00C04FC297EB010000001EA7CE8D6F7424AB5507AE53C3AAF7B0000000008000000700073007700000003660000A800000010000000CC4F30EBC7B859F448C5CDF3CD542E40000000004800000A00000001000000006C5C696CEE5FEB1B3FFB78A37A2A28080000009E3025E68A1AE7961400000013220712975272B29EF1D7DA93FE73E8EE32EF7600

  39. Okay, I’m going bonkers here.

    I’ve used the sample code from the DPAPI page to build a command-line VB app. In the Encrypt() As String function, I replaced the line:

    Return Convert.ToBase64String( _
    Encrypt(keyType, _
    Encoding.UTF8.GetBytes(plainText), _
    Encoding.UTF8.GetBytes(entropy), _
    description))

    with:

    Dim result As Byte()
    Dim encrypted As String = “”
    Dim i As Integer
    result = Encrypt(keyType, _
    Encoding.Unicode.GetBytes(plainText), _
    Encoding.Unicode.GetBytes(entropy), _
    description)
    For i = 0 To result.Length – 1
    If result(i).ToString.Length = 1 Then
    encrypted = encrypted & “0” & Hex(result(i))
    Else
    encrypted = encrypted & Hex(result(i))
    End If
    Next
    Return encrypted

    I get what *looks* like a valid encoded password, but it neither works when dropped into an RDP file, nor does it decode correctly with Remko’s app.

    What am I doing wrong?

  40. coderguy, is it possible for you to post your full code somewhere and maybe link to it here?

  41. ok, strike that… just figured it out… kinda…

    1. first i am using the code from http://www.obviex.com/samples/dpapi.aspx that Remko described…

    2. modified the following function as per Justin

    Public Shared Function Encrypt _
    ( _
    ByVal keyType As KeyType, _
    ByVal plainText As String, _
    ByVal entropy As String, _
    ByVal description As String _
    ) As String
    If plainText Is Nothing Then
    plainText = String.Empty
    End If
    If entropy Is Nothing Then
    entropy = String.Empty
    End If

    ‘Return Convert.ToBase64String( _
    ‘ Encrypt(keyType, _
    ‘ Encoding.UTF8.GetBytes(plainText), _
    ‘ Encoding.UTF8.GetBytes(entropy), _
    ‘ description))

    Dim result As Byte()
    Dim encrypted As String = “”
    Dim i As Integer
    result = Encrypt(keyType, _
    Encoding.Unicode.GetBytes(plainText), _
    Encoding.Unicode.GetBytes(entropy), _
    description)
    For i = 0 To result.Length – 1
    If result(i).ToString.Length = 1 Then
    encrypted = encrypted & “0” & Hex(result(i))
    Else
    encrypted = encrypted & Hex(result(i))
    End If
    Next
    Return encrypted
    End Function

    3. Lastly, from my main app… i call the function as follows:
    DPAPI.Encrypt(Crypto.DPAPI.KeyType.MachineKey, “password”, Nothing, “psw”)

    4. then i use Remko’s tool http://www.remkoweijnen.nl/blog/download/rdp.zip to verify my outputed hash…

    The problem i’m running into is the hash is correct only if i encrypt the word password, if i try to encrypt anything else, the hash fails…
    anybody with ideas?

  42. Has anyone had any luck, or know how to generate the hash in PHP (on a linux based machine)?

    (Posted in wrong blog bost before)

  43. @Ben: The encryption uses Win32 API to encrypt so I don’t think this is possible (and requires either the user’s or machine’s sid). Your best bet would be to somehow fire a script or executable on a remote windows machine that can do the encryption for you…

  44. Yeah I guess that can work via a web interface and a downloadable batch file :)

    Thanks.

  45. Hi all,
    after lot of tests, I was able to use the generated hash in an RDP file by :

    1/ using DanielHeth code in post 49, that means using

    Encoding.Unicode.GetBytes(plainText)

    instead of

    Encoding.UTF8.GetBytes(plainText)

    2/ replacing :

    For i = 0 To result.Length – 1
    If result(i).ToString.Length = 1 Then
    encrypted = encrypted & “0″ & Hex(result(i))
    Else
    encrypted = encrypted & Hex(result(i))
    End If
    Next
    Return encrypted

    with (c# code):

    StringBuilder encrypted = new StringBuilder();

    for (int i = 0; i <= result.Length – 1; i++)
    encrypted.Append(
    Convert.ToString(
    result[i], 16).PadLeft(2, ‘0’).ToUpper());

    return encrypted.ToString();

    Or if you prefer, let the “if then” test, but instead of testing

    result(i).ToString.Length = 1

    you have to test

    Convert.ToString(result[i], 16).Length = 1

    because in case of byte value ‘1010’ for example, whose hex representation is “A”, the string representation is “10” and the leading “0” isn’t added.

    Another tip : if you still have unicode strings, don’t try to convert it again into unicode before calling the Encrypt method…

  46. Taking the DPAPI code, replacing the for loop (see post 47/49) with:

    For i = 0 To result.Length – 1
    encrypted = encrypted & Convert.ToString(result(i),16).PadLeft(2,”0″).ToUpper()
    Next

    and then calling the method with:
    DPAPI.Encrypt(DPAPI.KeyType.MachineKey, text, Nothing, “psw”)

    Yielded successful results. I now have a console app that I can call with a password argument and have it spit out a working .RDP password hash!

    Thanks again to everyone who helped figure this out in the comments, especially Daniel and Lolosoph.

  47. Pingback: Encrypting RDP Passwords in VB.NET | RemkoWeijnen.nl

  48. I would really REALLY benefit from a command line version. I’m looking to incorporating something like this into a batch script.

    Please please please!

  49. Hey guys,

    Has anyone come up with an RDP password encryptor for Windows CE by any chance?

    Thanks

    Mike

  50. Hi,

    I’m using another way of encoding valid rdp-passwordslike this:

    Private Shared Function GetEncodedPassword(ByVal password As String) As String
    Dim encoded As Byte() = DPAPI.Encrypt(DPAPI.KeyType.UserKey, System.Text.Encoding.Unicode.GetBytes(password), Nothing, Nothing)
    Dim temp As String = BitConverter.ToString(encoded).Replace(“-“, “”)
    Return temp
    End Function

    But my question is how to decrypt it back to a readable password. Please share your code to do this.

    Thanks,
    Jan

  51. OK, got it:

    Dim bytes As Byte() = HexToData(TextBox1.Text)
    Dim lunicodeencoding As New System.Text.UnicodeEncoding

    Dim lDecryptedBytes As Byte() = Unprotect(bytes, Nothing, Security.Cryptography.DataProtectionScope.CurrentUser)

    TextBox2.Text = lunicodeencoding.GetString(lDecryptedBytes)

    Private Shared Function HexToData(ByVal hexString As String) As Byte()
    If hexString Is Nothing Then
    Return Nothing
    End If
    If hexString.Length Mod 2 = 1 Then
    hexString = “0”c + hexString ‘ Up to you whether to pad the first or last byte
    End If

    Dim data As Byte() = New Byte(hexString.Length / 2 – 1) {}
    For i As Integer = 0 To data.Length – 1
    data(i) = Convert.ToByte(hexString.Substring(i * 2, 2), 16)
    Next
    Return data
    End Function

    Note that I’m using the build in class from the .net 2.0 framework for encrypting and decrypting rdp-passwords. It’s much nicer :)

    Jan

  52. Thanks Remko for the great article. I needed to have some way of automating the creation of RDP files with the password already included.

    After a crash course in VB.NET I was able to put together a utility that gives me the proper hash.

    I used http://www.obviex.com/samples/dpapi.aspx and made the suggested changes from Justin and DanielHeth (see comments 47, 49, and 55)

    I’ve never written a program before (and I’m not sure you can call what I did “writing a program”) but if anybody wants to use the utility I’ve posted it at this link http://www.petri.co.il/forums/showthread.php?t=25204

    BTW – I’m not the same Jeremy as the one above but I believe this is what he is looking for.

  53. Could someone PLEASE rewrite this code for Windows CE? That would be very very great!! Thanks!

  54. hello
    thanks for this solution I created a powershell script wich uses it to create a complete rdp file and it works fine see bellow.
    now I need to be able to decrypt the password and this part need the same modification as the encrypt part but I am not a c# specialist so can someone help me to modify this part of the c# code?
    I think it is this one

    —————-c#——————–
    public static string Decrypt( string cipherText,
    string entropy,
    out string description)
    {
    // Make sure that parameters are valid.
    if (entropy == null) entropy = String.Empty;

    return Encoding.UTF8.GetString(
    Decrypt( Convert.FromBase64String(cipherText),
    Encoding.UTF8.GetBytes(entropy),
    out description));
    }

    powershell script to create a rdp file and launch the TS

    powershell code to create a rdp file and launch the terminal session

    #———–code begin———————-
    $server = Read-Host “enter the server name”
    $dll =”./DPAPI.dll”
    [void][Reflection.Assembly]::LoadFile($dll)

    $secmdp = get-credential
    $ptr = [System.Runtime.InteropServices.Marshal]::

    
    SecureStringToBSTR($secmdp.password)
    $mdp = [System.Runtime.InteropServices.Marshal]::

    PtrToStringUni($ptr)

    $user = $secmdp.username

    $encrypted=[DPAPI]::Encrypt([DPAPI+KeyType]::`
    UserKey,$mdp,$null,”psw”)

    $fichier =
    “screen mode id:i:1
    desktopwidth:i:1024
    desktopheight:i:768
    session bpp:i:16
    winposstr:s:2,3,169,96,1201,897
    full address:s:$server
    compression:i:1
    keyboardhook:i:2
    audiomode:i:0
    redirectdrives:i:1
    redirectprinters:i:0
    redirectcomports:i:0
    redirectsmartcards:i:1
    displayconnectionbar:i:1
    autoreconnection enabled:i:1
    username:s:$user
    alternate shell:s:
    shell working directory:s:
    disable wallpaper:i:1
    disable full window drag:i:1
    disable menu anims:i:1
    disable themes:i:0
    disable cursor setting:i:0
    bitmapcachepersistenable:i:1
    password 51:b:$encrypted

    Set-Content -path “./$server.rdp” -Value $fichier
    mstsc “./$server.rdp”
    Start-Sleep -Seconds 2
    Remove-Item “./$server.rdp”

    #————-end of code ————————

  55. i have made the programe in VC that generate hash for admin is
    01000000D08C9DDF0115D1118C7A00C04FC297EB01000000E73580BB89962146AF1BFB1D1447A54500000000340000004C006900620070007500720070006C00650020005300650063007500720065002000500061007300730077006F0072006400000003660000A8000000100000005E9CF384DFD13810017BC4DA02F5C2620000000004800000A000000010000000ACB8381A053457FF958EA2582AC7FE8D08000000911206F22AB76EE614000000025EE3F451A6B5AB9FE61E60B2B7F2A460D692EE

    and i decript form ur software it give me “??” text . cam u give me the code how to decript

  56. Hi,

    I have the password encryption in Windows, but it doesn’t work for Mac, I am using Mac to connect to windows through RDP. Everything works fine except I have to use clear text password, such as:

    clear password:s:ssl

    Not this on MAC (Windows is fine):
    password 51:b:01000000D08C9DDF0115D1118C7A00C04FC297EB010000002297DDAFF6F9064D948A19CE48E83E810400000008000000700073007700000003660000A800000010000000EDB1E62CFD5EF09EAC46111D6794E8710000000004800000A00000001000000036F12B9702F981F8B16D8407EF0B2B95080000001A3094F22CD138EC14000000BF9F4CDB1EEAA4846DE9E390691D43B0A8FDB380

    Any idea for encrypting RDP password on MAC ??

    Thanks a lot

  57. bonjours,
    je veux crypter une image pixel par pixel utilisant la librairie lockbox sous delphi, j’ai cree le code suivant:
    .
    .

    .procedure TMainForm.btncptClick(Sender: TObject);
    var ImgCpt:TMDIChild;
    Str,StrC:String;
    i,j:Integer;
    P:PByteArray;
    cl:BytePixel;
    R:RealPixel;

    begin
    //Apparence de l’enfant ImgCpt
    ImgCpt:=(ActiveMDIChild as TMDIChild);
    Str:=’Image cryptée';

    //transfert des pixels de l’image vers la zone memoire
    for j:=0 to ht-1 do
    begin
    for i:=0 to wd-1 do
    begin
    cl:=getrvalue(ImgCpt.Image1.Picture.Bitmap.Canvas.Pixels[i,j]);
    setbyte(cl,Pimt,i+j*wd);
    end;
    end;

    //traitement (cryptage de l’image)
    for j:=0 to ht-1 do
    begin
    for i:=0 to wd-1 do
    begin
    cl:=getbyte(Pimt,i+j*wd);
    StrC:=IntToStr(cl); //Contiendra la valeur cryptée (0…..255)
    StrC:=LbRijndael1.EncryptString(StrC);//cryptage avec la composant AES
    //StrC=’ fgC7VcLjPJc05ejq30gOvA==’ pour cl=255 (exemple)
    Edit2.Text:=StrC; //Pour le test
    ShowMessage(”); //Pour le test
    Cl:=StrtoInt(strc); //pas efficace ………….(*)
    //le text strc=’ fgC7VcLjPJc05ejq30gOvA==’ doit etre converti en integer!!!!! maiiiiiiiis //comment
    setrealtype(cl,Pimt,i+j*wd);
    end;
    end;
    //Affichage
    PointerToBitmap1(Pimt,p,0,ImgCpt,Str);
    ImgCpt.Invalidate;
    end;
    le probleme se pose à la ligne (*) pour convertir le text en Byte.
    svp, Puvez vous m’aidez c’est pour mon projet de fin d’etude
    Merci d’avance.

  58. hi remko
    Thanks for the knowledge , I am trying to implement a very simple c program that will take plain text password
    and encript it (first do the conversion to unicode ,hash , and finally encrypt) I need a very siple C function for encription only.

    I find reverse engeneering your Java/VB programs is a little dificlt , I will be gratfull if you could help.

    Thanks.
    K.O.

  59. I am trying to make this work for Windows CE 5.0. I don’t believe cryptprotectdata API exists on Windows CE 5.0.
    I can, however, use Cryptology.ProtectedData.ProtectData.

    Does anyone know of a way to use this function to get the same result as above?

  60. The following C# version works for me when I add the resulting encrypted password to an .rdp file.
    Please feel free to remove/replace post 69 or at least the source code within it. I’m not sure what “Your comment is awaiting moderation.” means.
    Thanks.

    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Security.Cryptography;
    using System.Linq;
    using System.Text;

    namespace mstscpw
    {
    class Mstscpw
    {
    private const int CRYPTPROTECT_UI_FORBIDDEN = 0x1;
    // Wrapper for the NULL handle or pointer.
    static private IntPtr NullPtr = ( ( IntPtr ) ( ( int ) ( 0 ) ) );
    // Wrapper for DPAPI CryptProtectData function.
    [DllImport ( “crypt32.dll”, SetLastError = true,
    CharSet = System.Runtime.InteropServices.CharSet.Auto )]
    private static extern bool CryptProtectData (
    ref DATA_BLOB pPlainText,
    [MarshalAs ( UnmanagedType.LPWStr )]string szDescription,
    IntPtr pEntroy,
    IntPtr pReserved,
    IntPtr pPrompt,
    int dwFlags,
    ref DATA_BLOB pCipherText );
    // BLOB structure used to pass data to DPAPI functions.
    [StructLayout ( LayoutKind.Sequential, CharSet = CharSet.Unicode )]
    internal struct DATA_BLOB
    {
    public int cbData;
    public IntPtr pbData;
    }

    private static void InitBLOB ( byte[] data, ref DATA_BLOB blob )
    {
    blob.pbData = Marshal.AllocHGlobal ( data.Length );
    if ( blob.pbData == IntPtr.Zero )
    throw new Exception ( “Unable to allocate buffer for BLOB data.” );

    blob.cbData = data.Length;
    Marshal.Copy ( data, 0, blob.pbData, data.Length );
    }

    public string encryptpw ( string pw )
    {
    byte[] pwba = Encoding.Unicode.GetBytes ( pw );
    DATA_BLOB dataIn = new DATA_BLOB ( );
    DATA_BLOB dataOut = new DATA_BLOB ( );
    StringBuilder epwsb = new StringBuilder ( );
    try
    {
    try
    {
    InitBLOB ( pwba, ref dataIn );
    }
    catch ( Exception ex )
    {
    throw new Exception ( “Cannot initialize dataIn BLOB.”, ex );
    }

    bool success = CryptProtectData (
    ref dataIn,
    “psw”,
    NullPtr,
    NullPtr,
    NullPtr,
    CRYPTPROTECT_UI_FORBIDDEN,
    ref dataOut );

    if ( !success )
    {
    int errCode = Marshal.GetLastWin32Error ( );
    throw new Exception ( “CryptProtectData failed.”, new Win32Exception ( errCode ) );
    }

    byte[] epwba = new byte[dataOut.cbData];
    Marshal.Copy ( dataOut.pbData, epwba, 0, dataOut.cbData );
    // Convert hex data to hex characters (suitable for a string)
    for ( int i = 0; i < dataOut.cbData; i++ )
    epwsb.Append ( Convert.ToString ( epwba[i], 16 ).PadLeft ( 2, ‘0’ ).ToUpper ( ) );
    }
    catch ( Exception ex )
    {
    throw new Exception ( “unable to encrypt data.”, ex );
    }
    finally
    {
    if ( dataIn.pbData != IntPtr.Zero )
    Marshal.FreeHGlobal ( dataIn.pbData );

    if ( dataOut.pbData != IntPtr.Zero )
    Marshal.FreeHGlobal ( dataOut.pbData );
    }
    return epwsb.ToString ( );
    }
    }
    // Test code:
    class program
    {
    static void Main ( string[] args )
    {
    Mstscpw mstscpw = new Mstscpw ( );
    string epw = mstscpw.encryptpw ( “password” );
    Console.WriteLine ( “Encrypted password for \”password\” {0} characters: \r\n{1}”, epw.Length, epw );
    Console.ReadLine ( );
    }
    }
    }

  61. hi

    when i click decrypt, an error load as result :

    error decrypting : the data is invalid.

    or :

    error decrypting : key not valid for use in specified state.

    how to decrypt ? :-??

    if it works why show me errors.

  62. Amir, please provide some more details:
    Are you decrypting a key that was encrypted by the tool or an existing password from an rdp file. If it’s from an rdp file did the same windows user (on the same machine) encrypt it?

  63. no its an existing password on my RDP file in my document folder, i’m just testing, and i wanna know really anyone can decrypt it !

    like this :
    password 51:b:01000000D08C9DDF0115D1118C7A00C04FC297EB0100000090B029B6432BC8469DA2FAFA1AB328670000000008000000700073007700000003660000A800000010000000BEFAB53C2E5CB63E70E91FC1FD2DD6E60000000004800000A00000001000000077BFEB9C6D347E2934A1B8C80073674308020000128F00CFBDB8F5D1817C2E767A2BF57ABBEE92AC692AA581C800E9A2C6E349A69559493DE04AC0784A4FEA642835DEB5C2502FB07953A03A211A90A1F91D0DA634533949FE0E135C18F46435129503AF98F80BF07AF554A71ACFDD7E456ED6D58F9372BDD4838CFD3B6CE1BCE9F9763B5B191E4E4CA1A7B449EEB99A90B1C14B2802AF0D912DB7492B7A2984465F3C95D36B3EFB338F6D56491F555F86548D4C82DF76408F264F9DD06E44FB793D983BB6EBEEE271BADD6873D52DB0FD293857C74BD111A33C27A1017486E1053D44E3A056F520651793090532713064E0838FE604C1F3FABF372CB84FB6B97F96A4681A24BC509ECD25455FA347B90EBF020FE7584309A69539DE6179201BA22805A727A8858AAD81F809C34CFA565494095B79F7AE3276EA748FC949146BDBED54944417431680B52012BAEC3AB67C2A59D751F0CC5B0E75B6F9A0F45BAF0B9FBD97E1AA9D7028207F5AA01B70B5D0224E6D7A9B2E2A155445224EA36DCA0D03876B9DF451C53B546A2D444F6AF1D33929B65CB1BC254A6D0C2E92A472FC14BCBD3F9FC4DE0638C82ED62DFD5CBECDD587BDAD45CDC3050DC47700D3CA6543B47971D2B81FD81A42D85CE7318DFB76B10FEE785A7D24FC2C998095E391AE192E839E3886422E6719F42D0517D11CB17E4FF918010CC59B3064C087E52B8EF4C7D869B97FFE306C49E4BD0BD2FF60FEBAF1EE3F1B3800621D55A91400000011AF335B5BACED71A13676BA4C88C332F4D8B1AC0

  64. amir,
    I just double checked and triple checked. A password encrypted on one workstation is not portable to another, even for the same user in the domain. The encryption using CryptProtectData is specific to machine/user only.

  65. It does not work for RemoteApp. I tried your tool and the password is working for remote desktop RDP 6.1 and older. But when I put the user name and password in a .rdp file for a remoteapp on a windows2008 box, it does not work and ask me for credentials. But I can use the pasword for remote desktop to the windows2008 box and it also works if I change the rdp file to launch the remote application, though it gives me a full screen with the application running. I tried this because I want to use a vpn software NeoRouter (www.neorouter.com) to maintain my computers. Did anyone try this?

  66. Why does my RDP file from Windows Vista SP1 does not contain “password 51:b:”?

    Does your tool no longer work?

  67. @Ata: The 6.0 client version no longer saves passwords in the rdp file. However it can still read passwords already stored in rdp file. I use a patched version of the old client (v4) that can be used together with the v6 version.

  68. Pingback: 0 effort RDP “hacking” - The sneakybox

  69. On my VB 2005 app I have on page load:

    Dim strHashCode
    strHashCode = “Secret”
    Me.txtHashKey.Text = DPAPI.Encrypt(DPAPI.KeyType.UserKey, strHashCode, Nothing, “pwd”)

    This populates a textbox named txtHashKey with the encrypted text but how do I get this to what is reconised by RDP files…?

    Stewart

  70. Pingback: Save Password in .RDP File | Everyday Nerd

  71. This source is working very fine for me ! So use it without restriction. If it can help !

    DATA_BLOB DataIn, DataOut;
    WCHAR pwDescription [7];
    PWCHAR pwPassword;
    CString PwdHash;
    LPCSTR desc = “psw”;

    PwdHash = “”;
    DataOut.cbData = 0;
    DataOut.pbData = NULL;

    pwPassword = (WCHAR *) LocalAlloc (LPTR, passwd.GetLength () * sizeof (WCHAR));
    MultiByteToWideChar (CP_ACP, 0, passwd, passwd.GetLength (), pwPassword, passwd.GetLength () * sizeof (WCHAR));

    // RDP uses UniCode
    DataIn.pbData = (BYTE *) pwPassword;//(BYTE *) (LPWSTR) passwd.GetBuffer (passwd.GetLength ());
    DataIn.cbData = passwd.GetLength () * sizeof(WCHAR);

    // RDP always sets description to psw
    MultiByteToWideChar (CP_ACP, 0, desc, 3, pwDescription, 3 * sizeof (WCHAR));

    if (CryptProtectData (&DataIn,
    pwDescription,
    NULL,
    NULL,
    NULL,
    CRYPTPROTECT_UI_FORBIDDEN, // Never show interface
    &DataOut) )
    {
    CString encryptedData;
    BYTE* pb;
    pb = DataOut.pbData;

    for(DWORD i =0; i != DataOut.cbData; i++)
    {
    unsigned char ch =*pb;
    unsigned char ch0 = ch & 0xf0;
    ch0 = (ch>>4) & 0x0F;
    unsigned char ch1 = ch & 0x0F;

    encryptedData.AppendFormat(_T(“%x%x”),ch0,ch1);
    pb++;
    }
    // PwdHash est le mot de passe encodé en hexadécimal pour être utilisable par le RDP de Microsoft.
    PwdHash = encryptedData;
    }
    /*
    // Ceci est pour tester le décryptage de la donnée reçue.
    LPWSTR pDescrOut = NULL;
    CryptUnprotectData (&DataOut,
    &pDescrOut,
    NULL,
    NULL,
    NULL,
    CRYPTPROTECT_UI_FORBIDDEN,
    &DataIn);*/

    LocalFree (DataOut.pbData);
    LocalFree (DataIn.pbData);

    return PwdHash;

  72. in my last comment i omitted to say that i created a function with one parameter names passwd of CString type.

    This function is coded in C++.

  73. First of all, thanks for sharing this.

    A friend of mine was developing an intranet web application that lists all customers with additional information on how to access their servers. He asked me for help in writing a small utility that opens a rdp connection via a button click. I ended up with a signed java applet that gets the encrypted rdp password through JNI. A javascript function calls the “create rdpfile” method in the applet.

    I rewrote your Delphi-Code in C++ and than I got stuck for hours. Your tool and my library yield to different results. So I did some debugging on your tool and the good old OllyDbg showed up that the dwFlags Parameter in your tool is as follows:
    CRYPTPROTECT_UI_FORBIDDEN|CRYPTPROTECT_LOCAL_MACHINE

    After this change my code was running smoothly.

    Bernd

  74. Folks,

    I’m trying to create a program to generate .rdp files, but my password encryptation code is not working, it just encrypt the first character of any string. Can you help me?

    Source Code
    +++++++++++++++++++++++++++++++++++++++
    int main(){

    FILE *f;
    int count =0;
    char *enc;
    DATA_BLOB DataIn;
    DATA_BLOB DataOut;
    LPWSTR description = NULL;
    LPWSTR pwd = L”password”;
    DataIn.pbData = (BYTE *)pwd;

    if (DataIn.pbData == NULL) {
    return 1;
    }

    f=fopen(“c:\\temp.txt”,”wb”);

    DataIn.cbData = (DWORD)strlen((const char*)DataIn.pbData) + 1;

    if (CryptProtectData(&DataIn,(LPCWSTR)L”password”,NULL,NULL,NULL,CRYPTPROTECT_UI_FORBIDDEN,&DataOut)){
    enc=(char*)calloc(DataOut.cbData*2,1);
    for (count=0; count < DataOut.cbData; count++)
    sprintf(enc, “%s%02X”, enc, DataOut.pbData[count]);
    fprintf(f,”%s”, enc);
    LocalFree(DataOut.pbData);
    }else{
    printf(“Sorry!\n”);
    }
    fclose(f);
    return 0;
    }
    ++++++++++++++++++++++++++++++++++++++
    EOF

    Thanks

  75. @None: I think the error is here:
    DataIn.cbData = (DWORD)strlen((const char*)DataIn.pbData) + 1;

    Your input is a Unicode string but you give the count of chars in instead of bytes
    try
    DataIn.cbData = (DWORD)strlen((const char*)DataIn.pbData) * sizeof(WCHAR); (I don’t think you need to account for the 0 terminator)

  76. Thanks for your help.

    Actually, the error occurs on (DWORD)strlen((const char*)DataIn.pbData), it always returns 1. In my new code I’m passing a string as argument, so I’m using the strlen from the string itself.

    My new code:
    +++++++++++++++++++++++++++++++++++++
    int main(int argc, char **argv){
    FILE *f;
    DATA_BLOB in, out;
    BYTE *pbDataInput2;
    BYTE pbDataInput[1024];

    pbDataInput2=argv[1];

    MultiByteToWideChar(CP_ACP,0,pbDataInput2,strlen(pbDataInput2)+1,pbDataInput,1024);

    DWORD cbDataInput = strlen(argv[1])*sizeof(WCHAR);
    in.pbData=pbDataInput;
    in.cbData=cbDataInput;
    CryptProtectData(&in,L”psw”,NULL,NULL,NULL,CRYPTPROTECT_UI_FORBIDDEN,&out);

    f=fopen(“c:\\temp.txt”,”wb”);

    char* encPass=(char*)calloc(out.cbData*2,1);
    int count=0;
    while (count < out.cbData){
    sprintf(encPass,”%s%02X”,encPass,out.pbData[count]);
    count++;
    }
    fprintf(f,”%s”,encPass);
    fclose(f);

    }
    ++++++++++++++++++++++++++++++++++++
    EOF

    Regards,
    None

  77. This is my C# version.

    You can call the function with this code:

    byte[] cryptBytes = CryptPassword.DoRawEncryption(Password);
    EncryptedPassword = Encoding.UTF8.GetString(cryptBytes);
    EncryptedPassword = BitConverter.ToString(cryptBytes).Replace(“-“, “”);

    And this is the function:

    internal static class CryptPassword
    {
    #region KeyType enum

    public enum KeyType
    {
    UserKey = 1,
    MachineKey
    } ;

    #endregion

    private const int CRYPTPROTECT_LOCAL_MACHINE = 0x4;
    private const int CRYPTPROTECT_UI_FORBIDDEN = 0x1;
    private static readonly IntPtr NullPtr = ((IntPtr)((0)));
    private static KeyType defaultKeyType = KeyType.UserKey;

    public static byte[] DoRawEncryption(string strToEncrypt)
    {
    byte[] cryptBytes = Encrypt(KeyType.UserKey, Encoding.UTF8.GetBytes(strToEncrypt), null, null);

    return cryptBytes;
    }

    // Wrapper for DPAPI CryptProtectData function.
    [DllImport(“crypt32.dll”, SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool CryptProtectData(ref DATA_BLOB pPlainText, string szDescription, ref DATA_BLOB pEntropy, IntPtr pReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPrompt, int dwFlags, ref DATA_BLOB pCipherText);

    // Wrapper for DPAPI CryptUnprotectData function.
    [DllImport(“crypt32.dll”, SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool CryptUnprotectData(ref DATA_BLOB pCipherText, ref string pszDescription, ref DATA_BLOB pEntropy, IntPtr pReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPrompt, int dwFlags, ref DATA_BLOB pPlainText);

    // BLOB structure used to pass data to DPAPI functions.

    private static void InitPrompt(ref CRYPTPROTECT_PROMPTSTRUCT ps)
    {
    ps.cbSize = Marshal.SizeOf(typeof(CRYPTPROTECT_PROMPTSTRUCT));
    ps.dwPromptFlags = 0;
    ps.hwndApp = NullPtr;
    ps.szPrompt = null;
    }

    private static void InitBLOB(byte[] data, ref DATA_BLOB blob)
    {
    // Use empty array for null parameter.
    if (data == null)
    data = new byte[0];

    // Allocate memory for the BLOB data.
    blob.pbData = Marshal.AllocHGlobal(data.Length);

    // Make sure that memory allocation was successful.
    if (blob.pbData == IntPtr.Zero)
    throw new Exception(
    “Unable to allocate data buffer for BLOB structure.”);

    // Specify number of bytes in the BLOB.
    blob.cbData = data.Length;

    // Copy data from original source to the BLOB structure.
    Marshal.Copy(data, 0, blob.pbData, data.Length);
    }

    public static string Encrypt(string plainText)
    {
    return Encrypt(defaultKeyType, plainText, String.Empty, String.Empty);
    }

    public static string Encrypt(KeyType keyType, string plainText)
    {
    return Encrypt(keyType, plainText, String.Empty, String.Empty);
    }

    public static string Encrypt(KeyType keyType, string plainText, string entropy)
    {
    return Encrypt(keyType, plainText, entropy, String.Empty);
    }

    public static string Encrypt(KeyType keyType, string plainText, string entropy, string description)
    {
    // Make sure that parameters are valid.
    if (plainText == null) plainText = String.Empty;
    if (entropy == null) entropy = String.Empty;

    // Call encryption routine and convert returned bytes into a base64-encoded value.
    return Convert.ToBase64String(Encrypt(keyType, Encoding.UTF8.GetBytes(plainText), Encoding.UTF8.GetBytes(entropy), description));
    }

    public static byte[] Encrypt(KeyType keyType, byte[] plainTextBytes, byte[] entropyBytes, string description)
    {
    // Make sure that parameters are valid.
    if (plainTextBytes == null) plainTextBytes = new byte[0];
    if (entropyBytes == null) entropyBytes = new byte[0];
    if (description == null) description = String.Empty;

    // Create BLOBs to hold data.
    DATA_BLOB plainTextBlob = new DATA_BLOB();
    DATA_BLOB cipherTextBlob = new DATA_BLOB();
    DATA_BLOB entropyBlob = new DATA_BLOB();

    // We only need prompt structure because it is a required
    // parameter.
    CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();
    InitPrompt(ref prompt);

    try
    {
    // Convert plaintext bytes into a BLOB structure.
    try
    {
    InitBLOB(plainTextBytes, ref plainTextBlob);
    }
    catch (Exception ex)
    {
    throw new Exception(“Cannot initialize plaintext BLOB.”, ex);
    }

    // Convert entropy bytes into a BLOB structure.
    try
    {
    InitBLOB(entropyBytes, ref entropyBlob);
    }
    catch (Exception ex)
    {
    throw new Exception(“Cannot initialize entropy BLOB.”, ex);
    }

    // Disable any types of UI.
    int flags = CRYPTPROTECT_UI_FORBIDDEN;

    // When using machine-specific key, set up machine flag.
    if (keyType == KeyType.MachineKey)
    flags |= CRYPTPROTECT_LOCAL_MACHINE;

    // Call DPAPI to encrypt data.
    bool success = CryptProtectData(ref plainTextBlob, description, ref entropyBlob, IntPtr.Zero, ref prompt, flags, ref cipherTextBlob);
    // Check the result.
    if (!success)
    {
    // If operation failed, retrieve last Win32 error.
    int errCode = Marshal.GetLastWin32Error();

    // Win32Exception will contain error message corresponding to the Windows error code.
    throw new Exception(“CryptProtectData failed.”);
    }

    // Allocate memory to hold ciphertext.
    byte[] cipherTextBytes = new byte[cipherTextBlob.cbData];

    // Copy ciphertext from the BLOB to a byte array.
    Marshal.Copy(cipherTextBlob.pbData, cipherTextBytes, 0, cipherTextBlob.cbData);

    // Return the result.
    return cipherTextBytes;
    }
    catch (Exception ex)
    {
    throw new Exception(“DPAPI was unable to encrypt data.”, ex);
    }
    // Free all memory allocated for BLOBs.
    finally
    {
    if (plainTextBlob.pbData != IntPtr.Zero)
    Marshal.FreeHGlobal(plainTextBlob.pbData);

    if (cipherTextBlob.pbData != IntPtr.Zero)
    Marshal.FreeHGlobal(cipherTextBlob.pbData);

    if (entropyBlob.pbData != IntPtr.Zero)
    Marshal.FreeHGlobal(entropyBlob.pbData);
    }
    }

    #region Nested type: CRYPTPROTECT_PROMPTSTRUCT

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct CRYPTPROTECT_PROMPTSTRUCT
    {
    public int cbSize;
    public int dwPromptFlags;
    public IntPtr hwndApp;
    public string szPrompt;
    }

    #endregion

    #region Nested type: DATA_BLOB

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct DATA_BLOB
    {
    public int cbData;
    public IntPtr pbData;
    }
    #endregion
    }

  78. I can’t figure out how to convert it to a full length mstsc password. Are there any ideas to do this?

  79. Pingback: Como agregar la password a un archivo .rdp - GustavoG - Comunidad GrupoITPro

  80. Hi

    Im trying to do this in VB6, but Im not getting a valid result. Can someone tell me where I am going wrong.

    Email me on thompsdc AT gmail DOT com

    Here is the code I’ve done…
    ———————————————————————
    Option Explicit

    Private Type DATA_BLOB
    cbData As Long
    pbData As Long
    End Type

    Private Declare Sub CopyMemory Lib “kernel32″ Alias “RtlMoveMemory” ( _
    hpvDest As Any, _
    hpvSource As Any, _
    ByVal cbCopy As Long)

    Private Declare Function CryptProtectData Lib “crypt32.dll” ( _
    ByRef pDataIn As DATA_BLOB, _
    ByVal szDataDescr As String, _
    ByRef pOptionalEntropy As Any, _
    ByRef pvReserved As Any, _
    ByRef pPromptStruct As Any, _
    ByVal dwFlags As Long, _
    ByRef pDataOut As DATA_BLOB) As Long

    Public Function CryptRDPPassword(spPassword As String) As String
    Dim aDataIn() As Byte
    Dim udtDataIn As DATA_BLOB
    Dim r As Long
    Dim udtDataOut As DATA_BLOB
    Dim aDataOut() As Byte
    Dim s$, i&

    aDataIn = StrConv(spPassword, vbUnicode)
    udtDataIn.cbData = UBound(aDataIn) + 1
    udtDataIn.pbData = VarPtr(aDataIn(0))

    ‘StrConv(“psw”, vbUnicode),
    r = CryptProtectData(udtDataIn, _
    “psw”, _
    ByVal vbNullString, _
    ByVal vbNullString, _
    ByVal vbNullString, _
    0, _
    udtDataOut)

    If r Then
    ReDim Preserve aDataOut(udtDataOut.cbData)
    CopyMemory aDataOut(0), ByVal udtDataOut.pbData, udtDataOut.cbData

    s = “”
    For i = 0 To udtDataOut.cbData – 1
    s = s & Format(Hex(aDataOut(i)), “00”)
    Next
    CryptRDPPassword = s
    Else
    CryptRDPPassword = “Nothing”
    End If
    End Function

  81. Hello
    I search for a ASP or PHP version of this code. I hope to build a web rdp-generator and use it with a form in an intranet. The password generation is the only piece that i miss.

  82. Hi,

    I am using an intranet link to startup an RDP (stored on a network drive) using a batch file. I would like to start the RDP without creating it on the users desktop.

    I do not mind if the users know the password, I am just trying to eliminate the need for them to enter it when starting the RDP.

    Can I somehow use this solution to program the batch file to open the RDP and automatically add in the password for the specific user?

    Thanks

  83. Hi Every Body,

    I am searching java based application for the same.

    please help me..

  84. Hi everybody,

    With the help of all the information from over here and at Obviex, I’ve managed to write my own ‘login to Remote Desktop automatically from the command line’ utility, with some extra features. I wrote it for myself, but maybe other people will alsof find it useful. If anybody is interested, it can be found at http://www.donkz.nl/?p=58.

  85. Pingback: Too Much Information » Blog Archive » Remote Administration – RDP - Blogging instead of receiving that glazed-over look from my wife…

  86. You are a super star, all what I needed is working now.
    I have to automate remote desktop connection for number of users and it is working thanks to you.
    Well done.

  87. Hi,

    I try to compile your source with Delphi 7 and it doesn’t compile because the compiler don’t know the DATA_BLOB type and CryptProtectData() function.

    What unit should I use in the Uses line please ?

    Thanks

    Serge

  88. Hi! Have you tried this script to connect to a Windows Server 2008? I understand that you can no longer send username and password in the rdp file.

  89. I tested the example rdp file in vista home and xp professional . But does NOT work in xp professional. It still need to type in the password. So I think this is caused by the mstsc version or os security?

  90. Pingback: Remote desktop RDP file encryption password string of generation (c #)

  91. If any one is still interested,,, here is the VB .Net work that I have done to adjust some of the items to allow you to encrypt and decrypt correctly:

    1- Copy the same VB .Net example on the http://www.obviex.com/samples/dpapi.aspx.

    2- Change the codes with the same name as follow:

    Public Shared Function Encrypt(ByVal plainText As String ) As String
    Return Encrypt(defaultKeyType, plainText, String.Empty, String.Empty)
    End Function

    Public Shared Function Encrypt(ByVal keyType As KeyType, ByVal plainText As String ) As String
    Return Encrypt(keyType, plainText, String.Empty, String.Empty)
    End Function

    Public Shared Function Encrypt( ByVal keyType As KeyType, ByVal plainText As String, ByVal entropy As String ) As String
    Return Encrypt(keyType, plainText, entropy, String.Empty)
    End Function

    Public Shared Function Encrypt( ByVal keyType As KeyType, ByVal plainText As String, ByVal entropy As String, ByVal description As String ) As String
    If plainText Is Nothing Then
    plainText = String.Empty
    End If
    If entropy Is Nothing Then
    entropy = String.Empty
    End If
    Dim result As Byte()
    Dim encrypted As String = “”
    Dim i As Integer
    result = Encrypt(keyType, Encoding.Unicode.GetBytes(plainText), _
    Encoding.Unicode.GetBytes(entropy), description)
    For i = 0 To result.Length – 1
    encrypted += Convert.ToString(result(i), 16).PadLeft(2, “0”).ToUpper()
    Next
    Return encrypted
    End Function

    Public Shared Function Encrypt _
    ( _
    ByVal keyType As KeyType, _
    ByVal plainTextBytes As Byte(), _
    ByVal entropyBytes As Byte(), _
    ByVal description As String _
    ) As Byte()
    ‘ Make sure that parameters are valid.
    If plainTextBytes Is Nothing Then
    plainTextBytes = New Byte(0) {}
    End If

    If entropyBytes Is Nothing Then
    entropyBytes = New Byte(0) {}
    End If

    If description Is Nothing Then
    description = String.Empty
    End If

    ‘ Create BLOBs to hold data.
    Dim plainTextBlob As DATA_BLOB = New DATA_BLOB
    Dim cipherTextBlob As DATA_BLOB = New DATA_BLOB
    Dim entropyBlob As DATA_BLOB = New DATA_BLOB

    ‘ We only need prompt structure because it is a required
    ‘ parameter.
    Dim prompt As _
    CRYPTPROTECT_PROMPTSTRUCT = New CRYPTPROTECT_PROMPTSTRUCT
    InitPrompt(prompt)

    Try
    ‘ Convert plaintext bytes into a BLOB structure.
    Try
    InitBLOB(plainTextBytes, plainTextBlob)
    Catch ex As Exception
    Throw New Exception(“Cannot initialize plaintext BLOB.”, ex)
    End Try

    ‘ Convert entropy bytes into a BLOB structure.
    Try
    InitBLOB(entropyBytes, entropyBlob)
    Catch ex As Exception
    Throw New Exception(“Cannot initialize entropy BLOB.”, ex)
    End Try

    ‘ Disable any types of UI.
    Dim flags As Integer = CRYPTPROTECT_UI_FORBIDDEN

    ‘ When using machine-specific key, set up machine flag.
    If keyType = KeyType.MachineKey Then
    flags = flags Or (CRYPTPROTECT_LOCAL_MACHINE)
    End If

    ‘ Call DPAPI to encrypt data.
    Dim success As Boolean = CryptProtectData( _
    plainTextBlob, _
    description, _
    entropyBlob, _
    IntPtr.Zero, _
    prompt, _
    flags, _
    cipherTextBlob)

    ‘ Check the result.
    If Not success Then
    ‘ If operation failed, retrieve last Win32 error.
    Dim errCode As Integer = Marshal.GetLastWin32Error()

    ‘ Win32Exception will contain error message corresponding
    ‘ to the Windows error code.
    Throw New Exception(“CryptProtectData failed.”, _
    New Win32Exception(errCode))
    End If

    ‘ Allocate memory to hold ciphertext.
    Dim cipherTextBytes(cipherTextBlob.cbData – 1) As Byte

    ‘ Copy ciphertext from the BLOB to a byte array.
    Marshal.Copy(cipherTextBlob.pbData, cipherTextBytes, 0, _
    cipherTextBlob.cbData)

    ‘ Return the result.
    Return cipherTextBytes
    Catch ex As Exception
    Throw New Exception(“DPAPI was unable to encrypt data.”, ex)
    Finally
    If Not (plainTextBlob.pbData.Equals(IntPtr.Zero)) Then
    Marshal.FreeHGlobal(plainTextBlob.pbData)
    End If

    If Not (cipherTextBlob.pbData.Equals(IntPtr.Zero)) Then
    Marshal.FreeHGlobal(cipherTextBlob.pbData)
    End If

    If Not (entropyBlob.pbData.Equals(IntPtr.Zero)) Then
    Marshal.FreeHGlobal(entropyBlob.pbData)
    End If
    End Try
    End Function

    Public Shared Function Decrypt(ByVal cipherText As String ) As String
    Return Decrypt(cipherText, String.Empty, String.Empty)
    End Function

    Public Shared Function Decrypt(ByVal cipherText As String, ByVal entropy As String) As String
    Return Decrypt(cipherText, entropy, String.Empty)
    End Function

    Public Shared Function Decrypt( ByVal cipherText As String, ByVal entropy As String, ByRef description As String ) As String
    ‘ Make sure that parameters are valid.
    If entropy Is Nothing Then
    entropy = String.Empty
    End If
    Dim theBytes((cipherText.Length / 2) – 1) As Byte
    Dim Counter As Integer = 0
    For i = 0 To cipherText.Length – 1 Step 2
    If cipherText.Substring(i, 1) = “0” Then
    theBytes(Counter) = Convert.ToByte(cipherText.Substring(i + 1, 1), 16)
    Else
    theBytes(Counter) = Convert.ToByte(cipherText.Substring(i, 2), 16)
    End If
    Counter += 1
    Next
    Return Encoding.Unicode.GetString( _
    Decrypt(theBytes, _
    Encoding.Unicode.GetBytes(entropy), description))
    End Function

    Public Shared Function Decrypt _
    ( _
    ByVal cipherTextBytes As Byte(), _
    ByVal entropyBytes As Byte(), _
    ByRef description As String _
    ) As Byte()

    ‘ Create BLOBs to hold data.
    Dim plainTextBlob As DATA_BLOB = New DATA_BLOB
    Dim cipherTextBlob As DATA_BLOB = New DATA_BLOB
    Dim entropyBlob As DATA_BLOB = New DATA_BLOB

    ‘ We only need prompt structure because it is a required
    ‘ parameter.
    Dim prompt As _
    CRYPTPROTECT_PROMPTSTRUCT = New CRYPTPROTECT_PROMPTSTRUCT
    InitPrompt(prompt)

    ‘ Initialize description string.
    description = String.Empty

    Try
    ‘ Convert ciphertext bytes into a BLOB structure.
    Try
    InitBLOB(cipherTextBytes, cipherTextBlob)
    Catch ex As Exception
    Throw New Exception(“Cannot initialize ciphertext BLOB.”, ex)
    End Try

    ‘ Convert entropy bytes into a BLOB structure.
    Try
    InitBLOB(entropyBytes, entropyBlob)
    Catch ex As Exception
    Throw New Exception(“Cannot initialize entropy BLOB.”, ex)
    End Try

    ‘ Disable any types of UI. CryptUnprotectData does not
    ‘ mention CRYPTPROTECT_LOCAL_MACHINE flag in the list of
    ‘ supported flags so we will not set it up.
    Dim flags As Integer = CRYPTPROTECT_UI_FORBIDDEN

    ‘ Call DPAPI to decrypt data.
    Dim success As Boolean = CryptUnprotectData( _
    cipherTextBlob, _
    description, _
    entropyBlob, _
    IntPtr.Zero, _
    prompt, _
    flags, _
    plainTextBlob)

    ‘ Check the result.
    If Not success Then
    ‘ If operation failed, retrieve last Win32 error.
    Dim errCode As Integer = Marshal.GetLastWin32Error()

    ‘ Win32Exception will contain error message corresponding
    ‘ to the Windows error code.
    Throw New Exception(“CryptUnprotectData failed.”, _
    New Win32Exception(errCode))
    End If

    ‘ Allocate memory to hold plaintext.
    Dim plainTextBytes(plainTextBlob.cbData – 1) As Byte

    ‘ Copy ciphertext from the BLOB to a byte array.
    Marshal.Copy(plainTextBlob.pbData, plainTextBytes, 0, _
    plainTextBlob.cbData)

    ‘ Return the result.
    Return plainTextBytes
    Catch ex As Exception
    Throw New Exception(“DPAPI was unable to decrypt data.”, ex)
    ‘ Free all memory allocated for BLOBs.
    Finally
    If Not (plainTextBlob.pbData.Equals(IntPtr.Zero)) Then
    Marshal.FreeHGlobal(plainTextBlob.pbData)
    End If

    If Not (cipherTextBlob.pbData.Equals(IntPtr.Zero)) Then
    Marshal.FreeHGlobal(cipherTextBlob.pbData)
    End If

    If Not (entropyBlob.pbData.Equals(IntPtr.Zero)) Then
    Marshal.FreeHGlobal(entropyBlob.pbData)
    End If
    End Try
    End Function

  92. Pingback: Decrypting Dell vWorkspace .pit files - Remko Weijnen's Blog (Remko's Blog)

  93. Thanks guys! This worked great for me too! I was at the end of my rope trying to get Powershell to work, but this is even better and I don’t have to store the passwords in clear text either!

  94. the password 51 worked fine in windows environment
    but when I move the file to Mac it stops working it dose send some password because my setup locks the use after few wrong password attempts but dose not work even though it works fine in windows
    I use the new RPD download form Mac store