Saving Credentials in the Credential Manager

One of the best features of Windows is the Remote Desktop Connection software and one of the most annoying features is Windows' inability (or rather unwillingness) to use the current credential (username and password).

EnterYourCredentials

Yes, "Remember me" works. But it means having to type the password again and again for every server one visits and saving it for each server individually. The Remote Desktop client stores the username and password in the Windows Credential Manager.

CredentialManager

It is possible to add a credential to the Credential Manager manually.

PS M:\> cmdkey /add:TERMSRV/telly /user:testuser /pass:password

CMDKEY: Credential added successfully.

PS M:\> cmdkey /list:TERMSRV/telly

Currently stored credentials for TERMSRV/telly:

    Target: TERMSRV/telly
    Type: Domain Password
    User: testuser

Adding to the Credential Manager programmatically is similarly simple.

Using native code, that is.

#include <Windows.h>
#include <wincred.h>

BOOL ok;
int error;

int main()
{
    // credential information
    LPWSTR sTargetName = L"TERMSRV/telly";
    LPWSTR sUserName = L"testuser";
    LPWSTR sPassword = L"password";
    DWORD cbCredentialBlobSize = (DWORD)(wcslen(sPassword) * sizeof(WCHAR));

    // create credential
    CREDENTIAL credential = { 0 };
    credential.Type = CRED_TYPE_DOMAIN_PASSWORD; // 2
    credential.TargetName = sTargetName;
    credential.CredentialBlobSize = cbCredentialBlobSize;
    credential.CredentialBlob = (LPBYTE)sPassword;
    credential.Persist = CRED_PERSIST_ENTERPRISE; // 3
    credential.UserName = sUserName;
    
    // write credential to credential store
    ok = CredWriteW(&credential, 0);
    error = GetLastError();
    wprintf(L"%d\n", error);
    
    return error;
}

It is actually a lot more complicated using managed code. The CREDENTIAL struct has to be rebuilt in managed code and the CredWriteW() API declared. The code is much longer and it is very easy to make mistakes.

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace CreateCredTestManaged
{
    class Program
    {
        static bool ok;
        static int error;

        // credential struct definition as per Windows API
        [StructLayout(LayoutKind.Sequential)]
        struct CREDENTIAL
        {
            public uint Flags;
            public uint Type;
            public IntPtr TargetName;
            public IntPtr Comment;
            public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
            public uint CredentialBlobSize;
            public IntPtr CredentialBlob;
            public uint Persist;
            public uint AttributeCount;
            public IntPtr Attributes;
            public IntPtr TargetAlias;
            public IntPtr UserName;
        }

        // CredWriteW from Windows API
        [DllImport("advapi32")]
        static extern bool CredWriteW(
            ref CREDENTIAL Credential,
            uint Flags
            );

        static void Main(string[] args)
        {
            // credential information
            string sTargetName = "TERMSRV/telly";
            string sUserName = "testuser";
            string sPassword = "password";
            uint cbCredentialBlobSize = (uint)Encoding.Unicode.GetByteCount(sPassword);

            // create credential
            CREDENTIAL credential = new CREDENTIAL {
                Type = 2, // correponds to CRED_TYPE_DOMAIN_PASSWORD
                TargetName = Marshal.StringToCoTaskMemUni(sTargetName), // copies managed into unmanaged memory
                CredentialBlobSize = cbCredentialBlobSize,
                CredentialBlob = Marshal.StringToCoTaskMemUni(sPassword),
                Persist = 3, // corresponds to CRED_PERSIST_ENTERPRISE
                UserName = Marshal.StringToCoTaskMemUni(sUserName)
            };

            ok = CredWriteW(ref credential, 0);
            error = Marshal.GetLastWin32Error(); // corresponds to GetLastError()
            Console.WriteLine(error.ToString());

            Marshal.FreeCoTaskMem(credential.TargetName); // releases bytes allocated in unmanaged memory
            Marshal.FreeCoTaskMem(credential.CredentialBlob);
            Marshal.FreeCoTaskMem(credential.UserName);

            Environment.Exit(error);
        }
    }//class
}//namespace

Both programs create a credential in the Credential Manager like the cmdkey command quoted before does. It took me hours figuring it out...

 © Andrew Brehm 2016