Using HttpWebRequest with Credential Manager


A little know fact is that the .NET framework will use the stored credentials in the Credential Manager when accessing a network resource if the credentials exist for that particular resource (host).  I intend to clear up how this functionality works for the HttpWebRequests (you could extend this to WebService calls as well).


In Internet Explorer, you can access a website that challenges you for credentials by filling in a username and password when the dialog is presented and asks you for authentication.  If you check ‘Remember my password’ these credentials will be stored in protected storage (Credential Manager).  You can see the stored credentials by running this command: Control Keymgr.dll.  These credentials are stored in protected storage in each user’s My folder.  You can store these credentials by using control Keymgr.dll, by using the interface available in Internet Explorer or by using C++/C and the Credentials API’s (Such as CredWrite).  If you do not check ‘Remember my password’ then these credentials are not stored in Credential Manager and .NET will not be able to use these credentials.


The .NET framework can use the currently logged on user’s credentials to authenticate but if you have a web site that requires different credentials you can use store these credentials in Credential Manager and .NET will use these credentials instead of the currently logged on user credentials.  To illustrate this I put together a real simple sample.


The target web server in my sample is jpsandershv2003.  I only enabled Windows Integrated Authentication in IIS on that box.  I then created a User ID JPSGuest on that IIS machine.  Finally I wrote a real simple .NET console application to create an HttpWebRequest and get the response.


   HttpWebRequest aReq;


   HttpWebResponse aResp;


        


   aReq = WebRequest.Create(http://jpsandershv2003/) as HttpWebRequest;


   aReq.UseDefaultCredentials = true;


   aResp = aReq.GetResponse() as HttpWebResponse;


   aResp.Close();


When you run this code it will first look in the credential manager first for the credentials for attaching to jpsandershv2003 and if found use those, otherwise the Currently Logged on User credentials will be used.  In my example since I was logged on as jpsanders to my machine I expect that the credentials used should be my domain credentials jpsanders.  I turned on logging and ensured the IIS log was logging the username and sure enough, jpsanders was authenticated in IIS.


My next goal was to see if I could change this default behavior and make it log on using the jpsandershv2003\JPSGuest user credentials.  Using the command control keymgr.dll I was able to add the credential as a Windows logon credential:



 


 



Now when I ran the console application I saw success in the IIS log!  I was logging in now as JPSGuest.


Taking a fiddler (http://fiddlertool.com) trace of Internet Explorer also confirmed I was using the stored credentials and these were NTLM credentials. 


Domain: jpsandershv2003
User: JPSGuest
Host: JPSANDERS3


Can I store credentials and have it use Kerberos?  In theory yes, simply type domain based credentials in.  However I could not test this because I only have one logon to my domain and you cannot save your currently logged on credentials in the Credential Manager. 


What about basic and digest?  NO, this would be a huge security hole.  Can you tell me why (it should be obvious)?


Note that the credentials used are independent of the PORT used.  So if I change the server to bind HTTP traffic on port 8089 then change my code to access http://jpsandershv2003:8089 the code will still access the web site using the stored credentials.


To revert back to the interactive user, simply delete the credentials stored for jpsandershv2003.


Further observations


What is the second radio button for (A Web site or program credentials)?  This is for programs that are writing against the credentials API directly.  Some examples of this are Terminal Server (RDP) and Windows Live credentials.  Here are some great links about the APIs:
http://msdn.microsoft.com/en-us/library/aa374789(VS.85).aspx
http://msdn.microsoft.com/en-us/library/aa480470.aspx


NOTE: Because the credentials are stored in the context of the logged on user, this technique will not work for non interactive user accounts such as the service accounts and ASP.NET applications.


 


Please send me a quick note if you found this Blog helpful!


 


 

Comments (7)

  1. mms says:

    What about WCF service calls that use message security with the UserName clientCredentialType. It works fine if we use Windows credential type but not UserName. I have entries for all kinds of hosts, both the domain that is being connected to and the certificate identity used to validate the certificate, nothing works.

    Is there a way to manually talk to the credential manager and extract password data or somehow tell WCF to use it?

  2. jpsanders says:

    There are API’s for CredMan.  They are listed here:

    CredRead:

    http://msdn.microsoft.com/en-us/library/aa374804(VS.85).aspx

    Vista changes:

    http://msdn.microsoft.com/en-us/library/cc540483.aspx

    Pre Vista used Pstore but I am pretty sure after IE 8, everything will be stored in CredMan.

  3. Mike Elliott says:

    I’ve been interested by this feature in Windows 7.  Is there a way to log queries to the Credential Manager by Windows.  e.g. I access a corporate resource or website when using a non-domain joined client.  I get prompted for some creds.  Is there a way i can check which creds need to be added to Cred Manager to prevent the prompt next time around?

    There seem to be occasions where I tick a box to store the creds during authentication, but then I get propmted again next time.

  4. jpsanders says:

    Mike,

    I assume you are talking about Internet Explorer?  Is that true?  You should note what occasions this works and for which ones it does not to further troubleshoot this.

  5. Mike Hilsher says:

    JP,

    First thanks for writing this up you're one of the few who've delved deep into this.

    Now we tried this for connecting to a SQL Server backend on port 1433.  Works great for interactive accounts.  For a service account we temporarily turned it into an interactive account, stored the credential, and then disallowed interactive again.  (This was before I saw your blog!)

    It didn't fail in a way that you would think it might.  It worked, and you could track the "aliased" login(s) as event ID 4648 in the OS Security log.  However at some point 1-3 hours in it suddenly fails and starts getting errors.  The errors are quite correct as it reverted to using the actual context instead of the one Credential Manger maps to.

    No mystery if it had just failed to work at all, but working for a while and then "timing out" without any message or event???  We were using it to connect a Web App to SQL so we've found if you reset IIS then it starts working again, once more for 1-3 hours.

  6. jpsanders says:

    Mike Hilsher,

    Strange that it worked for a while…  Without debugging this further it would be difficult to determine why it worked for a time.  Perhaps it is using a Kerberos ticket and when you reset it is able to get the initial ticket and then after the timeout period the ticket is no longer valid and that is why you start getting the errors.  I would expect that time period to be more definitive however instead of such a wide range.  If indeed you are using Kerberos you could try enabling Kerberos logging to troubleshoot this further: support.microsoft.com/…/262177 .  Don't forget to turn it off after your investigation!

    -Jeff

  7. Michael Holm says:

    Is there anyway to force the WCF service to use the users credentials (jpsanders in your example) instead of the stored credentails (JPSGuest)??