WSE 2.0 Specifications and How to Program Them Using .NET

WSE 2.0 Specifications and How to Program Them Using .NET

WSE 2.0—Web Service Enhancements: This is nothing but a .NET class Library for building or applying the WSE Specifications to baseline Web services. The WSE specifications are:

  • WS-Security
  • WS-Trust
  • WS-Policy
  • WS-Secure Conversation
  • WS Addressing
  • WS Messaging
  • WS-Attachments

You also can call WSE 2.0 to build a Web service without using HTTP protocol.

Today's Web services are secured by SSL (Secure Socket Layer) using HTTPS protocol. This works very well when communication is Point-to-Point (P2P). But, some limitations exist when you develop scalable distributed applications; specifically, when you develop an application that crosses security domains. For example, if SOAP messages are routed to one or more intermediaries before reaching the final destination and the entire route uses SSL, the ultimate receiver still has to communicate with the sender to authenticate the sender of the SOAP message. That pattern is difficult to scale well.

This is where WSE comes into the picture to develop better scalable and secured Web services. For this, WSE uses WS-Security. This specification adds security credentials to the SOAP message itself. When the SOAP message sender (client) sends a SOAP message to a Web service or SOAP requester in WSE, these credentials are called security tokens. These tokens are verified before executing the web service. WSE gives three options to secure Web services. They are adding security credentials to a SOAP message, digitally signing the SOAP message, and encrypting the SOAP message.

Now, let me show you how to add security tokens to your baseline Web service. These means basically that you are going to authenticate the user who calls your Web service.

Note: If you're looking for an article on baseline Web service, please browse the following URL: http://www.csharp-corner.com/members/rsramadurai.asp.

Step 1

First, open Visual Studio .NET IDE and create a new ASP.NE Web service project. Give WebServiceSecurity as the project name. After creating the project, right-click on it and select WSE 2.0 Settings, as shown here

Step 2

This option will open a new dialog box, as shown below. Check both the checkboxes. Enabling the first checkbox makes this project enabled to use WSE; Microsoft.Web.Services2.dll will be added to the project and the following entry will be added to the Web.config file. In addition to this, the Web references that are created from this point on will include WSE 2.0 support in the proxy generations.

When you check the first check box, the following entry is added to the web.config file:

   <configSections>
      <section name="microsoft.web.services2"
               type="Microsoft.Web.Services2.Configuration.
                     WebServicesConfiguration, Microsoft.Web.
                     Services2, Version=2.0.0.0, Culture=neutral,
                     PublicKeyToken=31bf3856ad364e35" />
   </configSections>

<system.web>

When you check the second check box, the following entry is added to the web.config file:

   <webServices>
      <soapExtensionTypes>
         <add type="Microsoft.Web.Services2.WebServicesExtension,
                    Microsoft.Web.Services2, Version=2.0.0.0,
                    Culture=neutral,
                    PublicKeyToken=31bf3856ad364e35"
                    priority="1" group="0" />
      </soapExtensionTypes>
   </webServices>
</system.web>

Now, add the following WebMethod to the Service1.asmx file. In this method, you have used the RequestSoapContext class to request the SOAP message that sent your client with security tokens.

Once you get the SoapContext, check whether this request is only a SOAP request. If it is other then a SOAP request, such as a HTTP-GET, HTTP-POST throws the exception saying that only a SOAP request will be accepted. Once verified that the request is a SOAP request, check whether the request has any tokens. This can done in a loop throwing the tokens collections. If UserToken exists in this collection, get the user taken. Verify the UserName if that matches the Windows user name and then allow the user to execute the web service. Otherwise, just return.

[WebMethod]
   public  int  Add (int Num1  ,int Num2  )
   {
      int total=0;
      SoapContext soapCntxt = RequestSoapContext.Current;

      if (soapCntxt==null)
         throw new Exception("Only SOAP requests are permitted.");

      foreach(SecurityToken token in soapCntxt.Security.Tokens)
      {
         if(token is UsernameToken )
         {
            UsernameToken usertoken =(UsernameToken )token;
            if(usertoken.Username=="baba23")
            {
               if (usertoken.Principal.IsInRole(System.Net.Dns.
                   GetHostName()+ @"\Users"))
               total=  Num1+Num2;
            }
         }
      }
      return total;
   }

Now you have finished the Web service that can handle the SOAP request with securiry tokens based on this. It can authenticate the user and authorize it to execute the Web method.

Now, you need to test this Web service. Add one Windows Console Application to this solution to consume this Web service. Once added, right-click on it and select the WSE 2.0 Settings as shown above. But, this time, the Dialog box only has one check box enabled because this project is not a ASP.NET Web Service project, showing that you cannot apply SOAP extensions to it. So, check the first check box and say OK to the dialog box.

Now, you need to add a Web Reference. After adding it, when you look at the proxy class, which is generated by .NET, you can see the difference between a baseline Web service proxy class and a WSE-supported Web service proxy class.

WSE 2.0 Specifications and How to Program Them Using .NET

Baseline Web Service Proxy

Baseline Web service Service1 is derived from the System.Web.Services.Protocols.SoapHttpClientProtocol class.

[System.Diagnostics.DebuggerStepThroughAttribute()]
   [System.ComponentModel.DesignerCategoryAttribute("code")]
   [System.Web.Services.WebServiceBindingAttribute(
      Name="Service1Soap", Namespace="http://tempuri.org/")]
   public class Service1 : System.Web.Services.Protocols.
                           SoapHttpClientProtocol {

      /// <remarks/>
      public Service1() {
         this.Url = "http://localhost/WebServiceSecurity/
                            Service1.asmx";
      }

WSE Supported Web Service Proxy

WSE supported Web service Service1 is derived from the Microsoft.Web.Services2.WebServicesClientProtocol class.

[System.Diagnostics.DebuggerStepThroughAttribute()]
   [System.ComponentModel.DesignerCategoryAttribute("code")]
   [System.Web.Services.WebServiceBindingAttribute(Name=
      "Service1Soap", Namespace="http://tempuri.org/")]
   public class Service1Wse : Microsoft.Web.Services2.
                              WebServicesClientProtocol {

      /// <remarks/>
      public Service1Wse() {
         this.Url = "http://localhost/WebServiceSecurity/
                            Service1.asmx";
      }

The first thing to notice about the above code segment is that in your client code you will be calling localhost.Service1Wse () instead of localhost.Service1().

The Service1Wse class is derived from the Microsoft.Web.Services2.WebServicesClientProtocol class; this class supports adding WS-Security tokens. To add the UsernameToken to the request, you simply create an instance of the UsernameToken class and add it to the Tokens collection of the RequestSoapContext.

Now, you call the Web method normally.

Client code

class Class1
   {
      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main(string[] args)
      {
         Console.Write(System.Net.Dns.GetHostName());

         localhost.Service1Wse proxy = new localhost.Service1Wse();
         UsernameToken tok = new UsernameToken("sreeni.net","baba",
                             PasswordOption.SendPlainText);
         proxy.RequestSoapContext.Security.Tokens.Add(tok);
         proxy.RequestSoapContext.Security.Elements.Add
            (new MessageSignature(tok));
         Console.Write(proxy.Add(10,30));
         //Console.Write(proxy.GetSecretMessage());
         Console.Write(proxy.HelloWorld("BABA"));
         Console.Read();

      }
   }

The UsernameToken authentication standard allows the client to send the password in one of the following three options.

  1. No password (username only)—This option is only useful if the user has already been authenticated, or if you trust the user. This option normally will be used the Web service that might be used in an intranet setting, where the user invoking the Web service has already been authenticated to log on to the intranet.
  2. Password in plain text—This sends the user's password in the SOAP Header in plain text. It is not a secure option when making a Web service call over an non-secure channel.
  3. A hashed digest of the password—This option is the best for sending password information over the Internet. Rather than sending the actual password, a one-way hash of the password is sent instead.

By default, the WSE 2.0 UserNameToken will authenticate only against the Windows user. Suppose, however, you may want to validate the users against a custom data source such as database, LADP, user credentials stored in XML File, and so forth. To accomplish this, you need to derive a custom class from UserNameTokenManager and override the AuthenticateToken(Token) method.

By default, the WSE 2.0 Toolkit's UsernameToken authentication piece authenticates users against the Windows User store. However, you may want to validate users against a custom user store, such as a database. To accomplish this, you can create your own class that derives from the WSE Toolkit's UsernameTokenManager and overrides, the reason being that overriding the hAuthenticateToken(token) method is to return the password for the user who is making a request to the Web service.

#region  Sreeni Custom Token Validation
public class WebServiceAuth:UsernameTokenManager 
{
   protected override string AuthenticateToken(UsernameToken token)
   {
      if (token == null)
         throw new ArgumentNullException();

      string userPassword="";
      string userName =token.Username;
      string CustomCredsinXML =
             "http://localhost/webserviceSecurity/
                     SreeniserviceSec.xml";

      XmlTextReader xmlTxtReader = new
         XmlTextReader(CustomCredsinXML);

      while(xmlTxtReader.Read())
      {
         if( (xmlTxtReader.LocalName == "Account") &&
             (xmlTxtReader.GetAttribute("username") == userName) )
         {
            userPassword = xmlTxtReader.GetAttribute("password");
            break;
         }
      }
      xmlTxtReader.Close();
      return userPassword;
   }
}
#endregion

Here, I am validating the user against the credentials stored in an XML file called SreeniServiceSec.xml. The structure of the XML file is this:

<?xml version="1.0" ?>
<Credientials>
   <Account username="baba23" password="baba23"/>
   <Account username="Sreeni" password="Ramaduari"/>
   <Account username="Pandey" password="Onkar"/>
   <Account username="Siddarth" password="Seenivasa"/>

</Credientials>

Once you have created the custom class for validating, you need to tell the WSE-supported Web service to use custom validation instead of the default approach against Windows user suthentication. To set this up now, right-click on the Web service project and select the WSE 2.0 Settings and select the Security tab. In the middle of this tab, you'll find a Security Tokens Managers section; click the Add button. This will display the dialog box shown below, which prompts you for three fields, as show below:

  • Type—Enter the fully qualified type of the custom class you created. The fully qualified type is denoted by Namespace.Classname, Assembly. Typically, the assembly and namespace are one in the same, so the Type field will look something like this: MyWebServiceProjectNamespace.AuthenticationManager, MyWebServiceProjectNamespace (here I'm assuming you named this custom class AuthenticationManager, as I did in the code snippet above).
  • Namespace—Here, you will need to enter the following hard-coded string: http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
  • QName—Here, you will need to enter the following hard-coded string: wsse:UsernameToken

Make sure that you have entered all correct values into these three textboxes. If you don't, this will cause many headaches about why UsernameToken authentication is not working in your Web service. Trust me, I speak from many hours of personal experience!

Once you have done this, the following entry will be added to the Web.Config file:

<microsoft.web.services2>
   <security>
      <securityTokenManager type="WebServiceSecurity.
                                  WebServiceAuth,WebServiceSecurity"
                                  xmlns:wsse=
                                  "http://docs.oasis-open.org/wss/
                                   2004/01/oasis-200401-wss-
                                   wssecurity-secext-1.0.xsd"
                                   qname="wsse:UsernameToken" />
    </security>
    <diagnostics />
  </microsoft.web.services2>

You can directly edit the above config entry, but it iseasy to do via the Visual Studio tool. (Here, Microsoft gets Full Credit.) Here, I've showed only how to add UsernameToken to the SOAP message. Like that, you can digitally sign the SOAP message as well as encrypt the message using a X.509 certificate.

In the next article, I will explain about WS-Address and WS-ReliableMessaging.



About the Author

SeenivasaRagavan Ramadurai

Seenivasaragavan Ramadurai is a .NET consultant, He has been working with .NET technology since pre beta releases. Seenivasa background includes Master's in Computer Science and B.Sc. Mathematics. He has over 9 years of software development experience with Microsoft technologies and has extensive experience developing client-server, distributed, Web services, and component based applications using Visual Studio .NET. Before moving to .NET, Seenivasa has worked on MFC, COM, ATL, and Visual C++ based applications. If you are looking for a consulting help, contact him at skbbaba23@gmail.com

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • QA teams don't have time to test everything yet they can't afford to ship buggy code. Learn how Coverity can help organizations shrink their testing cycles and reduce regression risk by focusing their manual and automated testing based on the impact of change.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds