Mukarram Mukhtar

Single Sign On (p 2)

Single Sign On (SSO) in .NET:

Okay so I’m assuming that you’ve already read the part 1 of this tutorial. If you have not, then please read it, because things won’t make much sense to you without reading it. So in this article we’ll see how to create a Service Provider for Single Sign On. As explained earlier, Service Provider is receiver of the data that is being passed to it from Identity Provider.

Creating Service Provider:

  • Start Microsoft Visual Studio 2005 or later.
  • Click Create New Project
  • In New Project dialog box, select Visual C# in Project types panel on left.
  • Select ASP.NET Web Application in the Templates panel on right.
  • Give a decent physical path and name to your project and select OK.

 

 

By doing this, IDE will create a web application project with a default form added into it.

  • The first thing you need to do is, copy ComponentSpace.SAML2.dll to bin directory of this web app. ComponentSpace.SAML2.dll will be found at the following location, if you selected default options during installation; if you did not, then please help yourself and locate where you installed SAML:

 

C:\Program Files\ComponentSpace\SAML v2.0 for .NET

  • After you copy ComponentSpace.SAML2.dll into your bin directory, add reference to this dll into your project.
  • The certificate file, as explained above, should be available at the following location, copy this file and paste it on the root of your project, then, include it in the project:

 

C:\Program Files\ComponentSpace\SAML v2.0 for .NET\Examples\SSO\IdP-Initiated\SAML2IdP\idp.cer

  • Add System.Security in the references of the project.
  • Next, rename your Form1.cs to frmReceiveData.aspx, right click this page and make it a Startup page of the application.
  • Right click the project node in solution explorer and add Global.asax.
  • Right click the project node in solution explorer and add Default.aspx.
  • Lastly, make the project run from IIS, instead of IDE. For more details on how to do this, get help from this link 

 

After completing the above steps your Solution Explorer should look something like this:

 

Now we are ready to start coding, let’s start with Global.asax. Following is the code that we’ll put in Global.asax.cs, if you have already read part 1 of this article then you don’t need any explanation of lines of code below, it’s pretty straight forward:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace prjServiceProvider1
{
    public class Global : System.Web.HttpApplication
    {
        // The identity provider's certificate file name - must be in the application directory.
        private const string idpCertificateFileName = "idp.cer";
        // The application key to the identity provider's certificate.
        public const string IdPX509Certificate = "idpX509Certificate";
        // As part of the HTTP artifact profile, an artifact resolve message is sent to the artifact resolution service,
        // typically using SOAP over HTTPS. In a test environment with self-signed certificates, override certificate validation
        // so all certificates are trusted.
        private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
        // Loads the certificate from file.
        // A password is only required if the file contains a private key.
        // The machine key set is specified so the certificate is accessible to the IIS process.
        private static X509Certificate2 LoadCertificate(string fileName, string password)
        {
            if (!File.Exists(fileName))
            {
                throw new ArgumentException("The certificate file " + fileName + " doesn't exist.");
            }
            try
            {
                return new X509Certificate2(fileName, password, X509KeyStorageFlags.MachineKeySet);
            }
            catch (Exception exception)
            {
                throw new ArgumentException("The certificate file " + fileName + " couldn't be loaded - " + exception.Message);
            }
        }
        protected void Application_Start(object sender, EventArgs e)
        {
            // In a test environment, trust all certificates.
            ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
            // Load the IdP certificate.
            String fileName = Path.Combine(HttpRuntime.AppDomainAppPath, idpCertificateFileName);
            Application[IdPX509Certificate] = LoadCertificate(fileName, null);
        }
        protected void Session_Start(object sender, EventArgs e)
        {
        }
        protected void Application_BeginRequest(object sender, EventArgs e)
        {
        }
        protected void Application_AuthenticateRequest(object sender, EventArgs e)
        {
        }
        protected void Application_Error(object sender, EventArgs e)
        {
        }
        protected void Session_End(object sender, EventArgs e)
        {
        }
        protected void Application_End(object sender, EventArgs e)
        {
        }
    }
}

Once global.asax.cs is set, next is the turn to write code in frmReceiveData.aspx.cs. The aspx file of this form needs no components and will be written as follows:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="frmReceiveData.aspx.cs" Inherits="prjServiceProvider1.frmReceiveData" %>

 

Yep, that’s right, literally nothing, just one line! But cs file of it won’t be like this, let me show you its code little by little, we need the following namespaces:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Security.Cryptography.X509Certificates;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;
using ComponentSpace.SAML2;
using ComponentSpace.SAML2.Assertions;
using ComponentSpace.SAML2.Protocols;
using ComponentSpace.SAML2.Bindings;
using ComponentSpace.SAML2.Profiles.ArtifactResolution;
using ComponentSpace.SAML2.Profiles.SSOBrowser;

Yes, it’s good to have all these namespaces added in your code. Let’s take a look at the Page_Load event:

protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                //Trace.Write("SP", "Assertion consumer service");
                // Receive the SAML response.
                SAMLResponse samlResponse = null;
                string relayState = null;
                ReceiveSAMLResponse(out samlResponse, out relayState);
                // Process the SAML response.
                ProcessSAMLResponse(samlResponse, relayState);
            }
            catch (Exception exception)
            {
                Response.Write(exception.ToString());
            }
        }

Page_Load does nothing special except calling appropriate methods who actually do the job. So without wasting further time, let’s take a look at ReceiveSAMLResponse method, which is the first function to receive SAML packet from Identity Provider:

// Receive the SAML response from the identity provider.
        private void ReceiveSAMLResponse(out SAMLResponse samlResponse, out string relayState)
        {
            //Trace.Write("SP", "Receiving SAML response");
            // Receive the SAML response.
01            XmlElement samlResponseXml = null;
02            ServiceProvider.ReceiveSAMLResponseByHTTPPost(Request, out samlResponseXml, out relayState);
            // Verify the response's signature.
            if (SAMLMessageSignature.IsSigned(samlResponseXml))
            {
                Trace.Write("SP", "Verifying response signature");
03                X509Certificate2 x509Certificate = (X509Certificate2)Application["idpX509Certificate"];
04                if (!SAMLMessageSignature.Verify(samlResponseXml, x509Certificate))
                {
                    throw new ArgumentException("The SAML response signature failed to verify.");
                }
            }
            // Deserialize the XML.
05.            samlResponse = new SAMLResponse(samlResponseXml);
            //Trace.Write("SP", "Received SAML response");
        }

In line # 1 we are declaring an XML Element object, this object will hold the information extracted from SAML packet. In line # 2 we are actually extracting SAML packet from HTTP Request and stuffing it into samlResponseXml object. In line # 3 we are instantiating an X509Certificate object via an application level variable which was created in global.asax.cs. Remember the SAML response extracted so far and stuffed so far in XML Element is encrypted one, so we need to decrypt it using the X509Certificate object. Line # 4 is in which we are verifying whether that samlResponseXml object can be decrypted by our X509Certificate or not. If not, then we throw an exception, otherwise it will decrypt the data and execute line # 5, in which we create a new SAMLResponse object, which is also an output argument of this method, and stuff all data in it.

So far we’ve decrypted and extracted the Response object from HTTP request, but we haven’t yet decrypted the actual data. That task will be done by ProcessSAMLResponse method, given as follows:

// Process the SAML response.
        private void ProcessSAMLResponse(SAMLResponse samlResponse, string relayState)
        {
            //Trace.Write("SP", "Processing SAML response");
            // Check whether the SAML response indicates success.
01           if (!samlResponse.IsSuccess())
            {
                throw new ArgumentException("Received error response");
            }
            // Extract the asserted identity from the SAML response.
02            SAMLAssertion samlAssertion = null;
            if (samlResponse.GetAssertions().Count > 0)
            {
03                samlAssertion = samlResponse.GetAssertions()[0];
            }
            else
            {
                throw new ArgumentException("No assertions in response");
            }
            // Enforce single use of the SAML assertion.
            if (!AssertionIDCache.Add(samlAssertion))
            {
                throw new ArgumentException("The SAML assertion has already been used");
            }
            if (samlAssertion.Subject.NameID != null)
            {
04                Session["MemberId"] = samlAssertion.GetAttributeValue("MemberId");
                Session["Name"] = samlAssertion.GetAttributeValue("Name");
                Session["Phone"] = samlAssertion.GetAttributeValue("Phone");
            }
            else
            {
                throw new ArgumentException("No name in subject");
            }           
            // Redirect to the requested URL.
05            Response.Redirect(relayState, false);
        }

Now let’s see what lines need explanation. Let’s talk about line 01, SAML Response object has a method which returns True or False based upon whether SAML Response was successfully created or not. Line # 2 is just a declaration of a SAML Assertion object. SAML Response contains of one or more assertions, called SAML Assertions. These assertions contain actual data. In line # 3 we are extracting 0th assertion from the response object. Since we are the senders ourselves, we know there was only one assertion in response object. After the assertion has been extracted successfully, now is the turn to extract the actual data, line # 4 and other lines in the same block are performing that task. Finally, in line # 5 we are redirecting to the actual web page that might have some contents in it; and that would be our Default.aspx.

Default.aspx is very straight forward. Let me show you it’s code real quick and then we’ll be all set to run our applications.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="prjServiceProvider1._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    </div>
    </form>
</body>
</html>

And its CS file looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace prjServiceProvider1
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (Session["MemberId"] != null && Session["Name"] != null && Session["Phone"] != null)
            {
                Response.Write("SAML Assertion Received Successfully<BR><BR>");
                if (Session["MemberId"] != null)
                    Response.Write("<BR>Member ID: " + Session["MemberId"].ToString());
                if (Session["Name"] != null)
                    Response.Write("<BR>Name: " + Session["Name"].ToString());
                if (Session["Phone"] != null)
                    Response.Write("<BR>Phone: " + Session["Phone"].ToString());
            }
            else
                Response.Write("SAML Assertion Failed to Receive.");
        }
    }
}

Very straight forward! No explanation needed. Now let’s run the Identity Provider app and see how it passes the data to Service Provider.

 

I ran the Identity Provider app and put some data in the fields, then I clicked Go SAML button. And the next window that I saw was the following:

 

Note the red circles in the last two snapshots, the address of application changed when I clicked on the Go SAML button, which means the correct data was passed from one web application to a different web application correctly and successfully.

Well folks that’s all from my side today. Please provide me with the feedback on this article and don’t forget to ask me for the free copy of the source code if you are interested.

Have a good day!

🙂

20 Comments »

  1. Mukarram, what an excellent tutorial. Thank you, because this is exactly what I’ve been looking for. Any chance I can get the source from you?
    Thanks very much!
    jr

    Comment by Jim Roth — December 29, 2010 @ 2:49 pm

  2. Good job Mukarram! Like Jim, any chance I can get the source from you?

    Comment by Tommy J. — April 7, 2011 @ 11:07 pm

  3. Hi Mukhtar,

    I have a doubt. Can we work with SAML without service provider like ComponentSpace? If so please let me know the way.

    Thanks & Regards,
    Prabhu.P

    Comment by Prabhu — June 3, 2011 @ 3:22 pm

    • The answer to your question is, Yes. SAML is just a protocol. You can implement it by writing your own base class libraries. But that will involve getting profound knowledge of the protocol and writing a few layers of classes to implement it; means, more time, research and effort. In my case time was bigger issue than cost, that’s why I implemented it with ComponentSpace.

      Good luck and thank you!

      Comment by Mukarram Mukhtar — June 3, 2011 @ 5:08 pm

      • Hi Mukhtar,

        Thanks for your valuable reply. What about the certificate and pfx files presented in service providers and identity providers? Do we need to create own certificate or need to purchase?

        Regards
        Prabhu.P

        Comment by Prabhu — June 6, 2011 @ 9:54 am

      • Prabhu,

        You’ll have to create your own certificates regardless you use ComponentSpace. Your network administrator should know everything about how to create those. I also had my company’s system engineer create those files for me.

        Comment by Mukarram Mukhtar — June 6, 2011 @ 2:10 pm

      • Hi Mukhtar,

        Thanks for your comments and suggestions.

        Thanks & Regards,
        Prabhu.P

        Comment by Prabhu — June 6, 2011 @ 4:23 pm

  4. Hi, thanks so much for the tutorial. May I have a copy of the source?

    Comment by Barb — November 16, 2011 @ 2:40 pm

  5. Great post! Can you also do a demo on the single log out part from both IdP and SP using ComponentSpace? Thank you.

    Comment by hanwawa — December 15, 2011 @ 5:09 pm

    • i think you actually brought up an excellent point, i should really write a Part 3 on logging out in SSO series. but the tricky part is, let’s see when do i get time to do it 😉

      Comment by Mukarram Mukhtar — December 15, 2011 @ 6:16 pm

  6. Great! I am looking forward to it 🙂

    Comment by hanwawa — December 15, 2011 @ 6:48 pm

  7. Hi Mukarram,

    Thank you so much for your useful post, I learned how to implement the SSO. Please provide me the source you used for this tutorial it will be very helpful for me.

    Thanks.

    Comment by Sam — December 23, 2011 @ 5:51 am

  8. Hi Mukarram thanks for a great solution. I am gettting an error saying private key is null can you help me.

    Comment by ankit — May 2, 2012 @ 12:10 pm

    • Sure, Ankit. I’ve e-mailed you project source files of this application. Good luck!

      Comment by Mukarram Mukhtar — May 3, 2012 @ 6:15 pm

  9. Hi Mukarram,

    thank you for your excellent post. I have found it while I was researching the info on SAML. Could you please email the source to me?

    Thanks in advance,
    KS

    Comment by Mujislav Zelenko — July 5, 2012 @ 3:17 pm

  10. Hi Mukarram;
    Very good job ..
    Can you send me the source code i shall be very thankful to you

    Thanks
    /Nasir

    Comment by Nasir — August 24, 2012 @ 2:29 pm

  11. 9.Hi Mukarram,

    Great start. Could you please email the source to me?

    Thanks in advance,
    William D Beaty

    Comment by William D Beaty — April 8, 2013 @ 4:38 pm

  12. I just like the helpful information you provide to your articles.

    I’ll bookmark your weblog and check once more right here frequently. I am somewhat sure I will be informed many new stuff proper right here! Best of luck for the following!

    Comment by Haarausfall — April 17, 2013 @ 8:02 am

  13. Hi MM,

    Could you please send your sample code to my email id. I had problems running my own code.

    Thanks in advance.

    Regards,
    TK

    Comment by VIm — May 23, 2013 @ 6:25 pm

  14. Hello Mukkaram
    I Liked your Post, Iam working on SAML too, I have hard time working with private and Public key … could you please Email me the source Code …
    Thank You in Advance…

    Comment by Qumar — May 29, 2013 @ 4:55 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: