Forcing HttpWebRequest to use Kerberos authentication
First, let me state that I am not recommending to only use a certain type of authentication in your applcations. It is not secure to do so. However, in some cases, it is convenient to be able to do so - mainly for verification or for debugging purposes.
Example 1: You do not own the server and you are not sure what type of authentication it supports. By using the code snippet below you can verify whether or not it supports Kerberos / whether or not it supports NTLM, whether or not it supports Basic, etc, etc. Of course you can verify this using Netmon or Ethereal traces and looking at the WWW-Authenticate header value the server is sending, but I find this way easier.
Example 2: You have a custom server which only supports a certain authentication type that you want to test.
Example 3: You have an intermittent authentication error which you suspect only happens when a certain type of authentication is used (for example Kerberos) but you’re not sure.
Now to the point: forcing HttpWebRequest to use a certain type of authentication like NTLM, Kerberos, Negotiate, Digest, or Basic is very easy – you can achive it by using AuthenticationManager.Unregister() method to unregister all the other authentication modules supported by HttpWebRequest. For example, if you want it to force it to use Kerberos only you unregister Basic, Digest, NTLM and Negotiate modules. If you want to force it to use NTLM only you unregister Basic, Digest, Negotiate and Kerberos, and so on.
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Collections;
using Microsoft.Win32;
namespace Mssc.Services.Authentication
{
class TestAuthentication
{
private static string username, password, domain, uri;
// This method invoked when the user does not enter the required input parameters.
private static void showusage()
{
Console.WriteLine("Attempts to authenticate to a URL");
Console.WriteLine("\r\nUse one of the following:");
Console.WriteLine("\URL username password domain");
Console.WriteLine("\URL username password");
}
// Display registered authentication modules.
private static void displayRegisteredModules()
{
IEnumerator registeredModules = AuthenticationManager.RegisteredModules;
Console.WriteLine("\r\nThe following authentication modules are now registered with the system:");
while (registeredModules.MoveNext())
{
Console.WriteLine("\r \n Module : {0}", registeredModules.Current);
IAuthenticationModule currentAuthenticationModule = (IAuthenticationModule)registeredModules.Current;
Console.WriteLine("\t CanPreAuthenticate : {0}", currentAuthenticationModule.CanPreAuthenticate);
}
}
// The getPage method accesses the selected page and displays its content
// on the console.
private static void getPage(String url)
{
try
{
// Create the Web request object.
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Credentials = new NetworkCredential(username, password, domain);
req.Proxy = null;
// Issue the request.
HttpWebResponse result = (HttpWebResponse)req.GetResponse();
Console.WriteLine("\nAuthentication Succeeded:");
// Store the response.
Stream sData = result.GetResponseStream();
// Display the response.
displayPageContent(sData);
}
catch (WebException e)
{
// Display any errors. In particular, display any protocol-related error.
if (e.Status == WebExceptionStatus.ProtocolError)
{
HttpWebResponse hresp = (HttpWebResponse)e.Response;
Console.WriteLine("\nAuthentication Failed, " + hresp.StatusCode);
Console.WriteLine("Status Code: " + (int)hresp.StatusCode);
Console.WriteLine("Status Description: " + hresp.StatusDescription);
return;
}
Console.WriteLine("Caught Exception: " + e.Message);
Console.WriteLine("Stack: " + e.StackTrace);
}
}
// The displayPageContent method display the content of the
// selected page.
private static void displayPageContent(Stream ReceiveStream)
{
// Create an ASCII encoding object.
Encoding ASCII = Encoding.ASCII;
// Define the byte array to temporarily hold the current read bytes.
Byte[] read = new Byte[512];
Console.WriteLine("\r\nPage Content...\r\n");
// Read the page content and display it on the console.
// Read the first 512 bytes.
int bytes = ReceiveStream.Read(read, 0, 512);
while (bytes > 0)
{
Console.Write(ASCII.GetString(read, 0, bytes));
bytes = ReceiveStream.Read(read, 0, 512);
}
Console.WriteLine("");
}
//Initialize the Uri and the credentials
private static void Init(string[] uriAndCreds)
{
if (uriAndCreds.Length < 3)
{
showusage();
return;
}
// Set the uri and the user credentials and
uri = uriAndCreds[0];
username = uriAndCreds[1];
password = uriAndCreds[2];
if (uriAndCreds.Length == 3)
domain = string.Empty;
else
// If the domain exists, store it.
// By default the domain name is the name of the server hosting the Internet resource.
domain = uriAndCreds[3];
}
public static void Main(string[] args)
{
Init(args);
Console.WriteLine("Listing all authentication modules before unregistering");
displayRegisteredModules();
// Unregister the standard Basic, NTLM and Negotiate and Digest modules, leaving only Kerberos
AuthenticationManager.Unregister("Basic");
AuthenticationManager.Unregister("NTLM");
AuthenticationManager.Unregister("Negotiate");
AuthenticationManager.Unregister("Digest");
//AuthenticationManager.Unregister("Kerberos");
// Display what Authentication modules are left registered
displayRegisteredModules();
// Read the specified page and display it on the console.
getPage(uri);
return;
} //end Main()
} //end class TestAuthentication
}
Comments
- Anonymous
January 20, 2014
Thank you Mariya! This was a lifesaver for me. I could not figure out why my call to a web service was failing. A quick look with WireShark showed me that the code was trying to use NTLM for authentication... it was supposed to be using Kerberos instead. After I unregistered NTLM as you showed me in your code, everything worked great! 5 karma points to you. Use them wisely ;)