Click here to Skip to main content
Click here to Skip to main content
Go to top

WS-Security: Secure Web services through SOAP Message Level Security

, 27 Aug 2004
Rate this:
Please Sign up or sign in to vote.
This article describes the use of Web Services Enhancements (WSE) 2.0 to implement UsernameToken authentication for providing security to Web Services over an Unsecure Transport.

Sample Image - wssecurity_usernametokens.gif

Introduction

This article shows you how to secure a web service using a User Name and password. This article also shows you how to secure the password with a Password digest and thwart man-in-the-middle and replay attacks. The User Name and password must be known before hand to both parties (Server and Client).

Background

In this fast changing world of the web, Security is of paramount importance. There was no particular standard for Web Service security (WS-Security) until April 2004, and Web service developers had to rely on the transport layer to provide security (via SSL/TLS from HTTP) or develop their own custom security mechanism sacrificing interoperability in the process. The whole idea of developing web services is interoperability across all platforms. In April 2004, WS-Security was established as an approved OASIS open standard. Web Service Enhancements (WSE) 2.0 is the first release of the software from Microsoft implementing this approved standard version of WS-Security. This means that applications built with WSE 2.0 security features will interoperate with other Web service platforms as their WS-Security-compliant implementations become available.

Downloading and Installing WSE 2.0

You can download Web Services Enhancements 2.0 from the Microsoft Download Center. After download, choose the Visual Studio Developer option for installation. This will install complete support for Visual Studio .NET 2003. Now, this version of the WSE installation does not embed the WSE docs into Visual Studio itself. It puts it in a separate link in the Start > Programs menu under the Microsoft WSE 2.0 folder. Maybe a future version of the Installation will embed the docs right into Visual Studio where you can see them.

You will need to restart ASP.NET (aspnet_wp.exe or w3wp.exe) at a minimum for WSE 2.0 changes to take effect. It is recommended you restart IIS on your Web Server.

Configuring a Web service project to use WSE 2.0

The .NET assemblies that provide the WSE functionality are contained in the Micorosoft.Web.Services2 namespace. Before using the classes from this namespace, we first have to add a reference to this assembly. We can do this by hand and then make the necessary configuration changes that are outlined in the WSE documentation, or we can make the job a lot easier by using the WSE Visual Studio add-in. The add-in is accessible by right-clicking the Visual Studio project, and then selecting the WSE Settings 2.0... option on the context menu.

There are two check boxes shown on the General tab. The first check box enables use of the current project with WSE. This means that the Microsoft.Web.Services2.dll will be added to the project references and that changes will be made to the web.config file to add support for the WSE configuration handler. In addition, any Web References that are created from this point on will include WSE 2.0 support in the proxy classes generated.

The second check box is only enabled if this is an ASP.NET Web Service project. By selecting it, the WSE SoapExtension is added to the project, which will enable the additional protocol support to work within the ASP.NET Web service HTTP handler (for .ASMX files). This is accomplished by modifying the web.config file and adding the WSE SoapExtension to the list of .asmx SOAP Extensions for the virtual directory.

In the sample code supplied with this article, we have enabled the Visual Studio Web service project, the Visual Studio Class Library project, and the console application project for WSE support, by checking the appropriate boxes on this tab.

Code Sample

Let us now create a small 'Hello' Web Service and secure it via User Name and Password. Our sample code consists of 3 parts.

  1. The secure Hello Web service. SecureWS.dll
  2. The client code to access the Web Service. SecureWSClient.dll
  3. The security code. WSESecurity.dll

The secure Hello Web Service returns a string "Hello Authenticated user <name>" if the Username and password match, and throws a System.Web.Services.Protocols.SoapHeaderException otherwise.

The Secure Web Service

Fire up Visual Studio .NET and create an ASP.NET Web Service project. Name the project "SecureWS". Enable WSE support for this Web Service project by right clicking on the Project in Solution Explorer and selecting the WSE Setting 2.0... option on the context menu. Place a check mark in both checkboxes and click OK.

Add the following using statements to Service1.asmx.cs. (Rest assured, even though you are adding Microsoft specific libraries to the code, these libraries implement an open standard and will interoperate with other platforms.)

using Microsoft.Web.Services2;
using Microsoft.Web.Services2.Security.Tokens;

Add the following code to Service1.asmx.cs:

[WebMethod]
public string Hello(string name)
{
    //Get the current soap context
    SoapContext ctxt = RequestSoapContext.Current;
    if (ctxt == null)
    {
        //This request is using a different protocol other than SOAP.
        return "Please format the request as a SOAP request and try again.";
    }

    //Iterate through all Security tokens
    foreach(SecurityToken tok in ctxt.Security.Tokens)
    {
        if (tok is UsernameToken)
        {
            UsernameToken user = (UsernameToken)tok;
            return "Hello Authenticated user " + user.Username;
        }
    }
    return "Hello Liar";
}

In the above code, we iterate through each security header since a single SOAP message may contain many headers. We make sure that it has at least one UsernameToken kind of security header.

The Web Service Client Code

Fire up a new instance of Visual Studio .NET (you can do an Add Project to the existing solution. But don't forget to set the startup project to the correct project.) and create a Console Application. Name the project "SecureWSClient". Enable WSE support for this Console Application project by right clicking on the Project in Solution Explorer and selecting the WSE Setting 2.0... option on the context menu. Place a check mark in the first checkbox and click OK. (The second checkbox will be grayed out since this is not an ASP.NET project.)

Add a Web Reference to the project and provide the URL of the Web Service as http://localhost/SecureWS/Service1.asmx. Rename the Web reference to SampleWSE.

Add the following using statements to the Console application.

using Microsoft.Web.Services2;
using Microsoft.Web.Services2.Security.Tokens;

Replace the Main method in the Console application with the following code:

[STAThread]
static void Main(string[] args)
{
    Console.Write("Enter Name: ");
    string name = Console.ReadLine();
    Console.Write("Enter Password: ");
    string password = Console.ReadLine();

    SampleWSE.Service1Wse proxy = new SecureWSClient.SampleWSE.Service1Wse();
    proxy.RequestSoapContext.Security.Tokens.Add(new UsernameToken(name, 
                             password, PasswordOption.SendHashed));
    proxy.RequestSoapContext.Security.Timestamp.TtlInSeconds = 300;

    Console.WriteLine(proxy.Hello(name));
    Console.WriteLine("Hit enter to end.");
    Console.ReadLine();
}

Since we enabled WSE support for this console application, Visual Studio generates a proxy which contains an additional object with the name Service1Wse. If there was no WSE support enabled, we would have just had the standard Service1 object.

In the above code, an UsernameToken is sent as part of the SOAP header. The password is transmitted as a hash using the SHA1 hashing algorithm. This ensures confidentiality and thwarts man-in-the-middle style of attacks. The Password digest (hash) is automatically computed using the following formula.

Password_Digest = Base64( SHA1( nonce + created + password ) )

We also set an expiration time for the UsernameToken to thwart against replay attacks. We give a 5 minute validity here because of differences in the clocks of the client and the server. If the Client and Server clocks were synchronized, we could have given a much lesser validity period - just enough time for the SOAP message to reach the Server. In reality, a nonce (is a randomly generated value) is used and the WSE would invalidate the SOAP message if it detects duplicate nonce value within the same session. We should make sure that the Session timeout maintained by WSE on the Web Server is greater than the Expiration time given by the client or, in other words, the client should give an Expiration time (TtlInSeconds value) less than that of the WSE Session timeout on the Web Server. For further reading, refer to the OASIS UsernameToken Profile specification.

The Security Code

The default implementation of WSE UsernameToken will try to authenticate incoming credentials against Windows User accounts on the Web Server. But it is not always possible to create local Windows accounts for every user of your Web service. For example, you may want to validate the user against a User name and password stored in your database and not as a Windows User. For this, we need to do something extra.

Fire up yet another instance of Visual Studio .NET and create a Class library project. Name the project WSESecurity. Add WSE support to this library by right clicking on the Project in Solution Explorer and selecting the WSE Setting 2.0... option on the context menu. Place a check mark in the first checkbox and click OK. Rename Class1.cs to CustomAuthenticator.cs and replace the contents with the following code:

using System;
using System.Security.Permissions;
using Microsoft.Web.Services2.Security.Tokens;

namespace WSESecurity
{
    [SecurityPermissionAttribute(SecurityAction.Demand, 
           Flags=SecurityPermissionFlag.UnmanagedCode)]
    public class CustomAuthenticator : UsernameTokenManager
    {
        //Returns the password or password equivalent for a user name
        protected override string AuthenticateToken(UsernameToken token)
        {
            if (token == null)
                throw new ArgumentNullException();

            //perform a lookup in your database 
            //for the user name in 'token.Username'
            //and return the password as a string. 
            //If there is no match, return null.
            if (token.Username == "vamsi")
                return "mypassword";
            else
                return null;
        }
    }
}

This object is never exposed to the outside world but is used internally by WSE to authenticate incoming UsernameTokens within SOAP messages. As you can see, the CustomAuthenticator class inherits from UsernameTokenManager and overrides the AuthenticateToken method. This method should return a plain text password.

You will need to deploy the WSESecurity.dll to the GAC or just copy it to the bin folder of the Web Service. (Same folder where you have SecureWS.dll.)

Now, we need to plug this class into the WSE pipeline by adding the following code to the web.config file of the Web Service:

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

The above code must be added to the <configuration> section of your web.config file. Also, note that there already maybe a section marked <microsoft.web.services2> (Visual Studio .NET automatically adds this section at the bottom of the web.config file) in which case, you will just need to add the <security> section within the existing <microsoft.web.services2> section.

Conclusion

The Web Service Enhancements 2.0 library provides simple and powerful methods to manage message level security in Web Services. The user authentication module can be updated without recompiling the Web Service itself by making changes to the WSESecurity library and redeploying it in the GAC or just copying it into the bin folder of the Web Service. The web service code need not worry about security and can concentrate on business logic.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Vamsi Prattipati
Web Developer
United States United States
I like to program in C# and am delighted at the paradigm shift in languages coming out of Microsoft.
 
My experience is mostly with distributed applications using .NET Remoting and Web Services along with ASP.NET

Comments and Discussions

 
QuestionHello Vamsi Prattipati PinmemberKalo_022-Mar-14 18:16 
QuestionSOAP header Security was not understood PinmemberMember 930951811-May-13 22:38 
QuestionGreat Article PinmemberAlireza_13624-May-12 21:03 
GeneralDevelop Secure Web Service using WSE toolkit PinmemberINDRESH SINGH16-Mar-09 22:31 
GeneralMy vote of 2 Pinmembervinodkrebc16-Mar-09 20:28 
GeneralNot able to find the link to download the source code!! Pinmemberr_adi28-Nov-08 20:28 
GeneralCreate an object of web service PinmemberMember 419869112-Jun-08 4:59 
QuestionCan not find the Add-In on the context Menu. PinmemberHimanshu Shukla8-Jun-08 21:21 
QuestionUpdate for VS 2005, .net 2.0 and WSE 3? Pinmembervab312-May-07 10:31 
QuestionCode for article? Pinmemberzarar17-Jan-07 7:31 
GeneralEnabling WSE Support in MS Visual C# 2005 Express Edition Pinmemberzarar17-Jan-07 6:50 
QuestionNo RequestSoapContext PinmemberAndyReiser23-Jun-06 2:56 
GeneralException error Pinmemberdev20925-May-06 9:28 
Generalgood article PinmembersandyMimie17-May-06 3:25 
GeneralException PinmemberDhivya.S27-Feb-06 23:46 
GeneralRe: Exception Pinmembergafferuk4-Sep-06 16:16 
GeneralSoapContext cTxt = RequestSoapContext.Current; PinmemberDhivya.S27-Feb-06 23:10 
GeneralRe: SoapContext cTxt = RequestSoapContext.Current; Pinmembert0r0119-Sep-06 2:34 
GeneralRe: SoapContext cTxt = RequestSoapContext.Current; PinmemberKevMoore17-Oct-06 7:49 
GeneralIt a good article. Thanks! Pinmemberanichin27-Jan-06 10:25 
QuestionHow to access UsernameTokenManager in webservice itself PinmemberTony Truong9-Jan-06 10:40 
GeneralOOps got an error message Pinmemberchris lasater30-Nov-05 8:07 
QuestionRe: I have the same error Pinmembermbacyrille16-May-06 23:04 
AnswerRe: I have the same error PinmembersandyMimie17-May-06 3:19 
GeneralRe: I have the same error PinmemberMember 1097083230-Jul-14 23:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 28 Aug 2004
Article Copyright 2004 by Vamsi Prattipati
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid