Hatteras has three tiers: client, middle, and data. The middle tier is an ASP.NET web service on a Windows 2003 Server running IIS 6. When the client (we use C# for both it and the middle tier) connects to the middle tier, it must authenticate with IIS 6. Depending upon the IIS configuration, that may be negotiate, NTLM, Kerberos, basic, or digest authentication. Here's a page on Internet Authentication in .NET.
NOTE: The code below uses the .NET 2.0 framework (Visual Studio 2005).
In order to authenticate, the client must have credentials that the server recognizes as valid. For Windows Integrated Authentication (comprising NTLM and Kerberos) using the current logged-on user, the client can use CredentialCache.DefaultCredentials. Here's how it looks in code.
public static void Main(string args)
Uri uri = new Uri(args);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Credentials = CredentialCache.DefaultCredentials;
// Get the response.
using (HttpWebResponse res = (HttpWebResponse)req.GetResponse())
StreamReader sr = new StreamReader(res.GetResponseStream());
You can find that same type of sample code in MSDN. However, it gets more interesting if you want to use basic or digest authentication or use credentials other than the current logged-on user.
One interesting fact is that the HttpWebRequest.Credentials property is of type ICredentials, but it only uses instances of NetworkCredential and CredentialCache. If you implement ICredentials on your own class that is not one of those two classes, you can assign it to the Credentials property, but HttpWebRequest will silently ignore it.
To go further, we need to look at the CredentialCache class itself. This class is used to hold a set of credentials that are associated with hosts and authentication types. It has two static properties, one of which we used above, that are the "authentication credentials for the current security context in which the application is running," which means the logged-on user in our case.
The difference is very subtle. The documentation for DefaultCredentials says, "The ICredentials instance returned by DefaultCredentials cannot be used to view the user name, password, or domain of the current security context." The instance returned by DefaultNetworkCredentials, being new in .NET 2.0 and of type NetworkCredential, would presumably let you get the user name and domain, but it didn't work for me when I tried it with the following code (UserName returned an empty string).
Console.WriteLine("User name: " + CredentialCache.DefaultNetworkCredentials.UserName);
The NetworkCredential class implements both the ICredentials (NetworkCredential GetCredential(Uri uri, String authType)) and ICredentialsByHost (NetworkCredential GetCredential(String host, int port, String authType)) interfaces. The ICredentialsByHost interface is new in .NET 2.0.
The CredentialCache class has methods that let you add, get, and remove credentials for particular hosts and authentication types. Using this class, we can manually construct what setting req.Credentials = CredentialCache.DefaultCredentials accomplished in the original example.
CredentialCache credCache = new CredentialCache();
credCache.Add(new Uri("http://localhost"), "Negotiate",
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Credentials = credCache;
The authentication type can also be explicitly specified as "NTLM" and "Kerberos" in separate calls to Add(). This page on authentication schemes explains using Negotiate as follows.
Negotiates with the client to determine the authentication scheme. If both client and server support Kerberos, it is used; otherwise NTLM is used.
Let's say you want to work with basic or digest authentication. The documentation for CredentialsCache.DefaultCredentials and CredentialsCache.DefaultNetworkCredential says that neither will work with basic or digest. If we add basic to credentials cache, we get a runtime exception.
credCache.Add(new Uri("http://localhost"), "Basic",
The exception is thrown by the Add() method.
Unhandled Exception: System.ArgumentException: Default credentials cannot be supplied for the Basic authentication scheme.
Parameter name: authType
at System.Net.CredentialCache.Add(Uri uriPrefix, String authType, NetworkCredential cred)
So, in order to use basic or digest, we must create a NetworkCredential object, which is also what we need to do in order to authenticate as some identity other than the logged-on user. To do that, we create NetworkCredential object and add it to the CredentialCache as follows.
credCache.Add(new Uri("http://localhost"), "Basic" /* or "Digest" */,
new NetworkCredential("me", "foo", "DOMAIN"));
Basic authentication sends the password across the wire in plain text. That's okay for a secure connection, such as one using SSL, and for situations where you don't need much security. Digest authentication hashes the password along with other data from the server before sending a response over the wire. It's a significant step up from basic.
Now we need to have the user name and password to create the NetworkCredential object. There are two parts to this. First is prompting the user for the name and password. The second is storing the information. For prompting there is the Windows dialog that pops up any time you go to a web site that requires authentication. That dialog includes a "Remember my password" checkbox. I don't yet know what the managed API is for that.
To store and retrieve the information, there is the new managed DPAPI explained by Shawn Farkas in several blog postings.
- Managed DPAPI Part I: ProtectedData talks about persisting information securely using the new ProtectedData class
- Managed DPAPI Part II: ProtectedMemory talks about storing information in RAM securely using the new ProtectedMemory class
- Making Strings More Secure talks about the new SecureString class that makes use of ProtectedMemory
[Update 3:44pm] The Windows dialog used when IE prompts for name and password is created by the CredUIPromptForCredentials() function. CredUIConfirmCredentials() is used to save credentials that authenticated successfully, if desired. Duncan Mackenzie's MSDN article Using Credential Management in Windows XP and Windows Server 2003 explains how to use it from .NET.
[UPDATE 4/10/2006] I updated the MSDN links that were broken.