Main Page
 The gatekeeper of reality is
 quantified imagination.

Stay notified when site changes by adding your email address:

Your Email:

Bookmark and Share
Email Notification
Project Touchit
Purpose
The purpose of this tutorial is to show how to use a C# .Net 4.6 windows application to synchronously touch a web resource, pass credentials (if needed) and handle the response. With regard to credential passing there are a few different ways with which it may be done to include (1) passing the credentials used to launch the application to authenticate to a target resource, (2) enter different credentials to authenticate to a target resource, (3) do not pass any credentials. With the latter, the reason you may not want to attempt to authenticate to a target resource may be because you just want to get the response headers and content (such as webpage source code) from a target resource.

In a network where policy is setup to automatically log you into target resources in a web browser (such as an Intranet portal) based on the credentials you used to log in to the computer, the same does not necessarily apply to this application. This can be very valuable if you need to ensure that an auto-login process is working or not, especially if you have cached content on your computer.

While the nuts-n-bolts of how the application "touchit" are shown below, you may want to download the executable or the complete, well-commented, source code to see exactly how it works (including getting a synchronous behavior from asynchronous activity).

Download touchit.exe (in zip file)
Download touchit source code (in zip file)

In some scenarios, you may need to authenticate a user of your application to Channel Secure on a website; this is a pretty rare requirement since Channel Secure is geared to providing security for a website, not an application. To learn more about this continue to the next section.

Core Functionality of The Application Is Shown Below:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
/* Classes For Making Web Requests and Processing Responses */
using System.IO;
using System.Net;
using System.Net.Http;

namespace touchit
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            /* Insert basic information pertaining to the user using the application
             * If a user just runs the application (double-clicks it) the useranme and domain are available via Environment.UserName and Environment.UserDomainName.
             * If the user chooses to "Run as different user" the Environment information reflects the "run as" user.
             * If System.Security.Principal.WindowsIdentity.GetCurrent().Name is used, that creates output such as domain\username...this is sort of old school.
             */
             if (Environment.UserName.ToString().Length > 0) { userNameBox.Text = Environment.UserName.ToString(); }
             if (Environment.UserDomainName.ToString().Length > 0) { domainBox.Text = Environment.UserDomainName.ToString(); }
        }

        private void submitBtn_Click(object sender, EventArgs e)
        {
            /* Clear UI text areas */
            responseCookieBox.Text = "";
            responseHeadersBox.Text = "";
            responseFullHeadersBox.Text = "";
            responseDataBox.Text = "";

            /* Get data to pass to touchit() */
            var tURL = targetURLBox.Text;
            var tURI = targetURIBox.Text;
            var sUser = userNameBox.Text;
            var sPass = passwordBox.Text; /* Note: In the properties of the passwordBox the smiley face as the passwordchar is entered by pressing Alt + 1 on the keypad */
            var sDomain = domainBox.Text;
            var sHandleCreds = (selAuthenticationBox.Text == "yes") ? true : false;

            /*
             * Call touchit() Task and wait synchronously for completion.
             * "touchit().Result" ensures a synchronous behavior where execution pauses at this line until touchit() is done and returns the string value into Task<>.
             * "await touchit()" would not stop execution at this line until touchit() was done.
             */
            var touchResult = touchit(tURL, tURI, sUser, sPass, sDomain, sHandleCreds).Result;

            /* Go through the list of results from touchit() in the form of List<KeyValuePair<string, string>> */
            var responseStatusCode = "";
            var responseCookie = "";
            var responseHeaders = "";
            var responseFullHeaders = "";
            var responseData = "";
            foreach (KeyValuePair<string, string> keyValuePairs in touchResult)
            {
                if (keyValuePairs.Key.ToLower() == "statuscode") { responseStatusCode = keyValuePairs.Value; }
                if (keyValuePairs.Key.ToLower() == "cookie") { responseCookie = keyValuePairs.Value; }
                if (keyValuePairs.Key.ToLower() == "headers") { responseHeaders = keyValuePairs.Value; }
                if (keyValuePairs.Key.ToLower() == "fullheaders") { responseFullHeaders = keyValuePairs.Value; }
                if (keyValuePairs.Key.ToLower() == "data") { responseData = keyValuePairs.Value; }
            }

            /* Dump response results to UI */
            statusCodeLabel.Text = responseStatusCode;
            responseCookieBox.Text = responseCookie;
            responseHeadersBox.Text = responseHeaders;
            responseFullHeadersBox.Text = responseFullHeaders;
            responseDataBox.Text = responseData;
        }

        private async Task<List<KeyValuePair<string, string>>> touchit(string tURL, string tURI, string sUser, string sPass, string sDomain, bool sHandleCreds)
        {
            /*
             * ARGUMENTS FOR touchit(tURL, tURI, sUser, sPass, sDomain, sHandleCreds)
             * tURL = Example: https://www.somesite.com/
             * tURI = Example: /folder/file.asmx
             * sUser = userName for NTLM authentication
             * sPass = userPassword for NTLM authentication
             * sDomain = network-domain for NTLM authentication
             * sHandleCreds = true/false (true = pass local credentials when no NTLM is present, false = do not pass local credentials when no NTLM is present)
             */
            /*
             * RESPONSE FOR touchit() is List<KeyValuePair<string, string>> in the form of:
             * statuscode = status code such as OK for 200
             * cookie = name = value pairs for cookies separated by Environment.NewLine
             * headers = basic response header information such as Content-Length and Content-Type
             * fullheaders = the entire response header
             * data = the body of the response; for example, the source code of a webpage
             */
            var targetURL = tURL;
            var targetURI = tURI;
            var userName = sUser;
            var password = sPass;
            var domain = sDomain;
            var useDefaultCredsCheckbox = sHandleCreds;

            /* Define http and cookie classes to utilize */
            CookieContainer cookies = new CookieContainer();
            HttpClientHandler handler = new HttpClientHandler();
            handler.CookieContainer = cookies;
            handler.UseDefaultCredentials = false;

            /* Handle credentials of the user */
            var cache = new CredentialCache();

            /* Determine if credentials should be passed with request or not */
            if (userName.Length > 0 && password.Length > 0 && domain.Length > 0)
            {
                /* User the user-specified NTLM credentials */
                var credential = new NetworkCredential(userName, password, domain);
                cache.Add(new Uri(targetURL), "NTLM", credential);
                handler.Credentials = credential;
                handler.UseDefaultCredentials = false;
            }
            else
            {
                /* Determine if default credentials should be passed with the request or not.  If this is false and no NTLM are specified (above), no credentials are passed with request. */
                if (useDefaultCredsCheckbox == true)
                {
                    /* Pass the current default credentials the user is using on the computer for the application that is running */
                    handler.UseDefaultCredentials = true;
                }
            }

            /* Make http request and get response */
            HttpClient client = new HttpClient(handler);
            client.BaseAddress = new Uri(targetURL);
            HttpResponseMessage response = client.GetAsync(targetURL + targetURI).Result;
            var responseStatusCode = response.StatusCode.ToString(); /* This will contain OK (OK for 200, Unauthorized for 401, NotFound for 404, etc) */
            var responseFullHeaders = response.ToString(); /* Contains the entire response header */
            var responseHeaders = response.Content.Headers.ToString(); /* Contains just basic response header information such as content-lengh, content-type, last-modified */

            /* Get the actual webpage source code */
            Stream responseStream = await response.Content.ReadAsStreamAsync();
            var responseData = "";
            using (StreamReader responseReader = new StreamReader(responseStream))
            {
                responseData = responseReader.ReadToEnd();
            }

            /* Get cookies */
            IEnumerable<Cookie> responseCookies = cookies.GetCookies(client.BaseAddress).Cast<Cookie>();
            var responseCookie = "";
            foreach (Cookie cookie in responseCookies)
            {
                responseCookie += cookie.Name.ToString() + " = " + cookie.Value.ToString() + Environment.NewLine;
            }

            /* Package response data so it can be returned */
            var responseList = new List<KeyValuePair<string, string>>()
            {
                new KeyValuePair<string, string>("statuscode", responseStatusCode),
                new KeyValuePair<string, string>("cookie", responseCookie),
                new KeyValuePair<string, string>("headers", responseHeaders),
                new KeyValuePair<string, string>("fullheaders", responseFullHeaders),
                new KeyValuePair<string, string>("data", responseData)
            };
            
            return responseList;
        }
    }
}


Adding Channel Secure
With Channel Secure in place on a website, if your application must authenticate to it (which is not on the website because it runs on a computer), one drawback is that Windows Authentication (at the time of this writing) must be turned off on the website (this can mean a lot of work to make code functional if it was built for Windows Authentication).

Once you have that out of the way and have included some configuration files for Channel Secure in your application (beyond scope of what we are addressing here), the next thing to be aware of is that your application may need to connect to multiple urls for authentication - that is, one url for the website where Channel Secure is running (and you authenticate to in order to get a list of Active Directory groups and other information the authenticating user has access to for that website) and a second url for Channel Secure directly.

This "multi-authentication" scheme does impose some significant changes to the code that you already saw (above). Examine the code below to see what minimum changes must occur for authentication to work.

Modified Functionality of The Application For "multi-authentication" Is Shown Below:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
/* Classes For Making Web Requests and Processing Responses */
using System.IO;
using System.Net;
using System.Net.Http;

namespace touchit
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            /* Insert basic information pertaining to the user using the application
             * If a user just runs the application (double-clicks it) the useranme and domain are available via Environment.UserName and Environment.UserDomainName.
             * If the user chooses to "Run as different user" the Environment information reflects the "run as" user.
             * If System.Security.Principal.WindowsIdentity.GetCurrent().Name is used, that creates output such as domain\username...this is sort of old school.
             */
             if (Environment.UserName.ToString().Length > 0) { userNameBox.Text = Environment.UserName.ToString(); }
             if (Environment.UserDomainName.ToString().Length > 0) { domainBox.Text = Environment.UserDomainName.ToString(); }
        }

        private void submitBtn_Click(object sender, EventArgs e)
        {
            /* Clear UI text areas */
            responseCookieBox.Text = "";
            responseHeadersBox.Text = "";
            responseFullHeadersBox.Text = "";
            responseDataBox.Text = "";

            /* Get data to pass to touchit() */
            var tURL = targetURLBox.Text;
            var tURI = targetURIBox.Text;
            var sUser = userNameBox.Text;
            var sPass = passwordBox.Text; /* Note: In the properties of the passwordBox the smiley face as the passwordchar is entered by pressing Alt + 1 on the keypad */
            var sDomain = domainBox.Text;
            var sHandleCreds = (selAuthenticationBox.Text == "yes") ? true : false;

            /*
             * Call touchit() Task and wait synchronously for completion.
             * "touchit().Result" ensures a synchronous behavior where execution pauses at this line until touchit() is done and returns the string value into Task<>.
             * "await touchit()" would not stop execution at this line until touchit() was done.
             */
            var touchResult = touchit(tURL, tURI, sUser, sPass, sDomain, sHandleCreds).Result;

            /* Go through the list of results from touchit() in the form of List<KeyValuePair<string, string>> */
            var responseStatusCode = "";
            var responseCookie = "";
            var responseHeaders = "";
            var responseFullHeaders = "";
            var responseData = "";
            foreach (KeyValuePair<string, string> keyValuePairs in touchResult)
            {
                if (keyValuePairs.Key.ToLower() == "statuscode") { responseStatusCode = keyValuePairs.Value; }
                if (keyValuePairs.Key.ToLower() == "cookie") { responseCookie = keyValuePairs.Value; }
                if (keyValuePairs.Key.ToLower() == "headers") { responseHeaders = keyValuePairs.Value; }
                if (keyValuePairs.Key.ToLower() == "fullheaders") { responseFullHeaders = keyValuePairs.Value; }
                if (keyValuePairs.Key.ToLower() == "data") { responseData = keyValuePairs.Value; }
            }

            /* Dump response results to UI */
            statusCodeLabel.Text = responseStatusCode;
            responseCookieBox.Text = responseCookie;
            responseHeadersBox.Text = responseHeaders;
            responseFullHeadersBox.Text = responseFullHeaders;
            responseDataBox.Text = responseData;
        }

        private async Task<List<KeyValuePair<string, string>>> touchit(string tURL, string tURI, string sUser, string sPass, string sDomain, bool sHandleCreds)
        {
            /*
             * ARGUMENTS FOR touchit(tURL, tURI, sUser, sPass, sDomain, sHandleCreds)
             * tURL = Example: https://www.somesite.com/
             * tURI = Example: /folder/file.asmx
             * sUser = userName for NTLM authentication
             * sPass = userPassword for NTLM authentication
             * sDomain = network-domain for NTLM authentication
             * sHandleCreds = true/false (true = pass local credentials when no NTLM is present, false = do not pass local credentials when no NTLM is present)
             */
            /*
             * RESPONSE FOR touchit() is List<KeyValuePair<string, string>> in the form of:
             * statuscode = status code such as OK for 200
             * cookie = name = value pairs for cookies separated by Environment.NewLine
             * headers = basic response header information such as Content-Length and Content-Type
             * fullheaders = the entire response header
             * data = the body of the response; for example, the source code of a webpage
             */
            var targetURL = tURL;
            var targetURI = tURI;
            var userName = sUser;
            var password = sPass;
            var domain = sDomain;
            var useDefaultCredsCheckbox = sHandleCreds;

            /* Define http and cookie classes to utilize */
            CookieContainer cookies = new CookieContainer();
            HttpClientHandler handler = new HttpClientHandler();
            handler.CookieContainer = cookies;
            handler.UseDefaultCredentials = false;

            /* Handle credentials of the user */
            var cache = new CredentialCache();


		csString = "https://www.website.com;https://www.channelsecureendpoint.com";
		csArray = csString.Split(';');


            /* Determine if credentials should be passed with request or not */
            if (userName.Length > 0 && password.Length > 0 && domain.Length > 0)
            {
                /* User the user-specified NTLM credentials */
                var credential = new NetworkCredential(userName, password, domain);

                //cache.Add(new Uri(targetURL), "NTLM", credential);
                //handler.Credentials = credential;
		handler.Credentials = CreateCredentialCache(credential, csArray);

		handler.UseDefaultCredentials = false;
            }
            else
            {
                /* Determine if default credentials should be passed with the request or not.  If this is false and no NTLM are specified (above), no credentials are passed with request. */
                if (useDefaultCredsCheckbox == true)
                {
                    /* Pass the current default credentials the user is using on the computer for the application that is running */

                    //handler.UseDefaultCredentials = true;
		    handler.Credentials = CreateCredentialCache(CredentialCache.DefaultNetworkCredentials, csArray);

                }
            }

            /* Make http request and get response */
            HttpClient client = new HttpClient(handler);
            client.BaseAddress = new Uri(targetURL);
            HttpResponseMessage response = client.GetAsync(targetURL + targetURI).Result;
            var responseStatusCode = response.StatusCode.ToString(); /* This will contain OK (OK for 200, Unauthorized for 401, NotFound for 404, etc) */
            var responseFullHeaders = response.ToString(); /* Contains the entire response header */
            var responseHeaders = response.Content.Headers.ToString(); /* Contains just basic response header information such as content-lengh, content-type, last-modified */

            /* Get the actual webpage source code */
            Stream responseStream = await response.Content.ReadAsStreamAsync();
            var responseData = "";
            using (StreamReader responseReader = new StreamReader(responseStream))
            {
                responseData = responseReader.ReadToEnd();
            }

            /* Get cookies */
            IEnumerable<Cookie> responseCookies = cookies.GetCookies(client.BaseAddress).Cast<Cookie>();
            var responseCookie = "";
            foreach (Cookie cookie in responseCookies)
            {
                responseCookie += cookie.Name.ToString() + " = " + cookie.Value.ToString() + Environment.NewLine;
            }

            /* Package response data so it can be returned */
            var responseList = new List<KeyValuePair<string, string>>()
            {
                new KeyValuePair<string, string>("statuscode", responseStatusCode),
                new KeyValuePair<string, string>("cookie", responseCookie),
                new KeyValuePair<string, string>("headers", responseHeaders),
                new KeyValuePair<string, string>("fullheaders", responseFullHeaders),
                new KeyValuePair<string, string>("data", responseData)
            };
            
            return responseList;
        }

	private static CredentialCache CreateCredentialCache(NetworkCredential credential, params String[] urls)
	{
		var cache = new CredentialCache();
		if (credential == null)
		{
			return cache;
		}
		foreach (var url in urls)
		{
			cache.Add(new Uri(url), "NTLM", credential);
		}
		return cache;
	}

    }
}


About Joe