Introduction

This article is an attempt to share my WSE 3.0 investigations and give a working real-world example of securing web services.

The WSE 3.0 library provides a number of “Turnkey” security mechanisms based on X.509 certificates, Username tokens over SSL, or Kerberos tickets. All that stuff like X.509, SSL, Kerberos tickets is good in laboratory conditions or for enterprise level applications where the hosting environment (or deployment target) is under full control. Relatively simple applications require a much simpler implementation (but not less secure ), so we used to implement a security based on Username tokens derived from username and password. This technique doesn’t demand any specific requirements to the hosting environment.

This article gives answers to the following questions:

How to implement a custom WSE 3.0 policy assertion class

How to read assertion configuration settings from the policy file

How to implement output client and input service SOAP filters for use within a custom policy assertion

How to sign a SOAP message with Username token

How to encrypt the message body with Username token

How to encrypt custom SOAP headers with Username token

How to implement Username token manager for verifying and decrypting client SOAP requests

How to configure both client and server applications to enable security

Background

Of course, this article requires a good understanding of SOAP web services and some previous experience with the WSE 2.0 library. However, the code from this article is self-containing and can be used in your applications with minor or no changes.

Before including this code to your production application, take a look at the WSE development center to grab more information about what is this and how it can be used.

The WSE 3.0 October CTP library (the latest WSE release) that is required to run the samples below can be downloaded from here. Please note, the library is still in beta, but it works perfectly with the .NET 2.0 RTM. Microsoft promises that WSE will be released closer to the official VS.NET 2005 go-live date which is currently November 7, 2005.

So, don’t waste your time and start learning this exciting new technology right now and migrating your almost legacy WSE 2.0 code!

Using the code

Before describing the code snippets I’d like to say some words about the basic idea of the overall WSE 3.0 architecture. The first sight on WSE 3.0 leaves a feeling that it will be so hard and long to get it work for you and there is not any desire for implementing it. But after spending some time studying and experimenting with the code you start realizing that WSE 3.0 is a huge step forward in comparison to the previous WSE 2.0 allowing writing less code and giving more flexibility in configuration.

WSE 3.0 is based on policies. Each individual policy can be applied to either a web service class or a web service proxy (client) class. A policy is a set of assertions. I think “assertion” is quite an unsuitable word here, but it occurs that assertion is just a factory class for creating input/output SOAP filters that will be injected in the SOAP processing pipeline. All policies can be declared in a separate “policy cache” file and then can be assigned to the web service class with the help of an attribute or to the proxy class programmatically. A policy cache file is an XML document including:

the set of so-named “extensions” that are simply the list of named assertions with their corresponding type names and

policies themselves each of which is a list of extensions (assertions) with corresponding optional configuration elements inside.

Well, let’s start writing the code! The first step is to implement a custom assertion which will be applied to the web service proxy. The primary function of assertion is constructing four SOAP filters:

Client output filter (working with the SOAP request issued by the client)

Client input filter (working with the SOAP response received from the service)

Service input filter (working with the SOAP request received from the client)

Service output filter (working with the SOAP response issued by the web service)

The second optional function of assertion is parsing the assertion configuration elements from the policy cache file. This step is required if the assertion is applied declaratively through the policy cache file.

As our client assertion requires username and password, it will be assigned to the proxy imperatively in the code and thus, the assertion has a constructor for passing credentials. The credentials are just stored in the private fields and then can be accessed in output filter.

Our client assertion provides only one “ClientOutputFilter”. The primary purposes of the client output filter are:

It is pretty suitable to define assertion filters as inner classes, because they would have simple names and they can see parent assertion fields.

The base SOAP header class that all your headers should be derived from to be secured, looks like the following:

///<summary>/// This is base class for all custom SOAP headers
/// that should be encrypted in the response.
///</summary>publicclass SecureSoapHeader : SoapHeader
{
///<summary>/// This property is just a flag telling us
/// that this SOAP header should be encrypted.
///</summary> [XmlAttribute("SecureHeader",
Namespace="http://company.com/samples/wse/")]
publicbool SecureHeader;
}

The code encrypting custom SOAP headers is quite unique here. I was Googling the internet trying to find information about this subject and had no success. Then I decided to write my own solution. I spent a certain time to realize how it can be implemented using standard functionality. The code is based on the sample: how to sign custom SOAP headers, given in the WSE 3.0 documentation. When playing with signing custom headers, I noticed at least two inconsistencies in the provided sample:

Within the custom SOAP header you can’t declare a custom “Id” property mapped to the XML “Id” attribute with this namespace. Certainly, you can do this, but it doesn’t make sense, because when the initial SOAP message is serialized, this system namespace is re-declared with a random prefix, not the “wsu” prefix as expected. The answer to this problem is that if you want to add a service “wsu:id” attribute to your custom header, you should do this after the “wsu” namespace has been declared.

The second inconsistency is that when you are creating SignatureReference or EncryptionReference, to add it to the respective collection you should specify the reference URI in the form “#[id]” (where [id] is the unique identifier of the node – GUID), not “#Id:[id]” like the documentation suggests.

That’s all the required code for the client side. The code for how to specify this assertion for the client will be given below. For now let’s turn to the web service side and consider the service-side assertion. The code for UsernameServiceAssertion is:

Note the difference from the client assertion: we return only the service input filter and add two additional methods for working with the assertion configuration elements. The ReadXml method in our example is just reading an empty XML element declaring an assertion in the policy. Also, the documentation doesn’t mention these two methods, but when you apply the assertion declaratively in the policy cache file, it will fail if the ReadXml method is not overridden.

The main purpose of the service input filter is checking which SOAP elements are signed and encrypted (I’ve removed the code checking encrypted elements to make the example simpler).

The filter class is also implemented as an inner class for UsernameServiceAssertion.

In order to verify username tokens on the service side, you should provide a custom implementation of the Username token manager class. For our example, it may look as follows:

publicclass ServiceUsernameTokenManager : UsernameTokenManager
{
///<summary>/// Constructs an instance of this security token manager.
///</summary>public ServiceUsernameTokenManager()
{
}
///<summary>/// Constructs an instance of this security token manager.
///</summary>///<paramname="nodes">An XmlNodeList containing
/// XML elements from a configuration file.</param>public ServiceUsernameTokenManager(XmlNodeList nodes)
: base(nodes)
{
}
///<summary>/// Returns the password or password equivalent for the username provided.
///</summary>///<paramname="token">The username token</param>///<returns>The password (or password equivalent) for the username</returns>protectedoverridestring AuthenticateToken(UsernameToken token)
{
string username = token.Username;
// it's up to you where you will get a password for some user
// you may:
// 1) get the password hash from web.config or system registry
// if you are implementing per-server security
// 2) get the password from the database or XML file for the given user name
// for example purposes we just return a reversed value of username
char[] ch = username.ToCharArray();
Array.Reverse(ch);
returnnewString(ch);
}
}

The goal of the AuthenticateToken method is to return passwords corresponding to usernames.

Now let’s see how to use all the logic above to secure the communication between the client and the service.

The first step is enabling WSE 3.0 support for both the client and the service in the app.config and the web.config files respectively:

Add WSE configuration section handler.

Add the <soapServerProtocolFactory> element for WSE in the <webServices> element (service only).

Add the WSE configuration section.

Specify the custom security token manager class (service only).

Specify the name of the policy cache file (service only and possibly client, but not in our example).

For more details on how to enable WSE support, you may inspect the app.config and the web.config in the attached article sample.

The second step is creating a policy cache file for use within the web service. For our example, it may look as the follows:

The third step is applying a policy to the web service with the “Policy” attribute as follows:

...
using Microsoft.Web.Services3;
[WebService(Namespace = "http://company.com/samples/wse/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[Policy("ServerPolicy")]
// we define policy on service level,
// so each service method will be covered by it
publicclass Service : System.Web.Services.WebService
// look, you don't need to inherit
// your service from some custom class
{
...

The forth step is applying a policy to the web service proxy:

// create web service proxy
// NOTE!!! When updating web reference in Visual Studio,
// don't forget to change its base class
// to Microsoft.Web.Services3.WebServicesClientProtocol then
WseSample.Service srv = new WseSample.Service();
// create custom SOAP header and assign it to web service
WseSample.BankAccountSettings settings =
new WseSample.BankAccountSettings();
settings.PinCode = "1111";
srv.BankAccountSettingsValue = settings;
// create custom policy assertion and assign it to proxy
// for password we just use reversed username
// it's important, because UsernameTokenManager
// on the service side applies the same logic
// when looking for user password
UsernameClientAssertion assert =
new UsernameClientAssertion("admin", "nimda");
// create policy
Policy policy = new Policy();
policy.Assertions.Add(assert);
// and set it to web service
srv.SetPolicy(policy);
// invoke web service method
bool valid = srv.CheckAccountStatus("123456");

That’s all! Now all calls to your web service are authenticated and all sensitive request information is encrypted! You may verify this by checking the InputTrace.webinfo and OutputTrace.webinfo trace files for both the service and the client.

Conclusion

I’d like to give some ideas for how the sample from the article can be improved:

You may add authorization logic to your web service. Just add the code for creating custom IPrincipal in UsernameTokenManager and role checks inside web methods.

Signing and encrypting the SOAP response. You may implement additional service output and client input filters to sign/encrypt SOAP responses and verify them on the client side.

Compression assertion. You may write a custom assertion using System.IO.Compression classes to compress/decompress SOAP requests/responses.

You may implement assertions to make authentication/authorization work with SOAP clients written in other languages as Perl, PHP, and VBScript (SOAP Toolkit).

History

11/06/2005 – The initial version of the article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Comments and Discussions

I have a web-service and a client(web-service) both developed in c#.
I have managed to successfully get WSE 3.0 to use mutual x.509 certificates to sign and encrypt my messages.
I have also managed to successfully get WSE 3.0 to carry out user authentication via UsernameTokens.
The problem that I have is that I would like to have a mix of mutual x.509 certificates and UsernameTokens working within the same solution.
My client is itself a web-service that runs locally at user sites and acts as a geteway to my central service.
My hope is to have one client x.509 certificate per site.
I want to use x.509 certificates to encrypt the data and sign with a signature that represents which client site the request has come from. I also want to identify and verify the individual user that made the request via UsernameTokens.
It seems that the 'setup-wizard' for WSE only allows me to choose either certificate based or username based authentication as opposed to both certificate based and username based authentication.
I would be grateful if anyone could point me in the right direction to achieve this.

Hi, Thanks for the article. Would i be able to use this for removing a timestamp element from security header. My request needs the following tokens, so hardcoding these tokens in process message may not be an option. userX509certificateServerX509certificate

First of all, many thanks for the great article, it was really helpful to me, unfortunately i've to apply almost the same steps but using WSE 2.0 which doesn't contain Assertion classes, only Input & Output filters, can guide us with any example to achieve the same goal but using WSE 2.0 capabilities

Hi
First of all I would like to thank you for this great article.
But I am confsed in a concept.
In your code you said that you don't send the password over the network. you just use it to encrypt the message.

1)If you don't send the password over the network why did you set it on the Client side ?
2)what will be the case If I want to send the password as the token in order to authorize at Service Side ?
3)Will I need to encrypt the Token and the header if I will host the service over secure HTTPS ?

UsernameToken userToken = new UsernameToken(
parentAssertion.username,
parentAssertion.password,
PasswordOption.SendNone);
// we don't send password over network
// but we just use username/password to sign/encrypt message

hi
i use Custom WSE 3.0 Policy Assertions for Signing and Encrypting SOAP Messages with Username Tokens but
webmethod cache no effect in my source code
[WebMethod(CacheDuration=60)]
if i don't use it (wse 3.0)
this work correctly

Can you do Policy Assignment to the webservice in code dependant on, say protocol usage, port, etc..... we want one web service to support multiple policies, dependant on who is calling it... any ideas?

Congratulations for the sample, it is great value for my needs. Could you please clarify for me a small detail? Why there is no user authentication performed when method is executed from web browser? In your web service sample there is a "settings" protection to prevent execution from web browser, but till then I expect to have a block based on user/password assertion. Many thanks.

does these codes can chages WSDL or inside of WSDL policy? You know, if a client is JAVA or something so that client has no idea which authentication is needed to operatae with .NET server. From my understanding, WSDL gives all information to operate with web service Server. but this codes can not change WSDL policy.
IS THERE ANY WAY that i can give authentication policy information through WSDL or am i worng to approch to solve authentication notice problems?

can you give me an idea? plz? i have been search this authentication problems for 4 weeks

WSDL is designed to provide meta information in order to generate a proxy implementation for the service.

This means that architectural (Java, PHP, etc.) implementations of that standard are responsible for the interpretation of the WSDL.

The policy is a means of defining additional information required to interpret the details of a specific implementation. This information may or may not be a defined standard.

If the policy defines a non-standard implementation (such as here in this article) then it becomes your responsibility to understand and create a corresponding implementation in the architecture of your choice (Java, PHP, etc.)

I´m a bit confuised. I thought that this code (great by the way, the most readable I´ve seen)encrypted the header and the body of the message to the service. When I look at the input trace at the service I can clearly read username and other variables, f.x the pincode variable is in clear text.
How can I fix it so that the entire content of the message is encrypted?

I implemented this code, thank you for it! I would like the response to be encrypted so people can't see the results being sent back. In fact, I'm not sure the body being sent is encrypted either, I'd have to check. From what I understand you're only encrypting the soap header. I really need the body being sent and the response returned to be encrypted as well so I don't have to use HTTPS and buy unnecessary server certs. Any tips?

Okay, I have everything going TO the service fine. The header is encrypted, although I had a problem there as it was using the namespace of my hosting service and not what I put in the SecureSoapHeader area, and I have the body encrypted and signed. All is good there. Now all I want/need is to get the RESULT, the return value encrypted. Anyone know how to do this? I don't want snoopers seeing the values returned from the web service method calls.

I am getting an error while using the code given in the article. May be I am missing something. Can anyone please help.

1) The code for UsernameAssertionLibrary is exactly same as given

2) I have commented the following part to check how the code behaves (UsernameClientAssertion.cs)
//MessageSignature sig = new MessageSignature(userToken);
//security.Elements.Add(sig);
3) Update the reference

4) When I execute it for the first time it gives me proper error message

5) Now I uncomment the commented portion and update the reference

6) This time it gives the following error

System.Web.Services.Protocols.SoapHeaderException: Microsoft.Web.Services3.Security.SecurityFault: Security requirements are not satisfied because the security header is not present in the incoming message.
at Microsoft.Web.Services3.Design.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity(SoapEnvelope envelope, Security security)

Following is the client side code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security.Tokens;
using System.Security;
using System.Security.Cryptography;

using WSE30Test.WSE3Service;
using Microsoft.Web.Services3.Security;
using UsernameAssertionLibrary;

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security;
using Microsoft.Web.Services3.Security.Tokens;

I downloaded the above project...Its worth useful for thedevelopers trying to secure webmethods.
I could build it successfully...
I am unable to run the project.Can anyone tell me how to use or how to run this project..