Message-level Declarative Security Using WSE 3.0

Introduction

The information provided in this article applies to the following technologies: IIS 6.0, .Net Framework 2.0, Visual Studio 2005, WSE 3.0, and Windows Server 2003 Family OS.

Web Services Enhancement version 3.0 (WSE 3.0) is a SOAP extension managed API (Microsoft.Web.Services3.dll) compatible with the .Net Framework 2.0; it ships with a design-time tool (WSE Settings 3.0) that fully integrates with Visual Studio 2005. WSE 3.0 simplifies the creation of secure, interoperable, and scalable Web service-based distributed applications. This article explores WSE 3.0 in terms of implementing message-level declarative security to Web services and Web service clients.

Message-level security vs. Transport-level security

Web service security can be implemented either by sending SOAP messages over a secure transport layer such as Secure Socket Layer, in other words calling the Web Service using https, or by adding security credentials in the SOAP message itself. Even though SSL provides message confidentiality and integrity through asymmetric and symmetric encryption, the fact that it is a connection-oriented protocol that acts as an ISAPI filter makes the scalability of Web service applications extremely difficult- figure 1 depicts such a scenario.

service client

Figure 1. Service clients connect to a Web service application over secure transport

With WSE, a SOAP message holding one or more security credentials can be routed through one or more intermediaries before reaching its destination point where it is validated, hence allowing scalability and distribution of Web service applications.

message level

Figure 2. Message-level security and network transparency

In the above example, the digitally signed and sealed SOAP envelope containing security tokens is forwarded to a message router, which delegates the message to a Web server based on the configuration of the referral cache.

Declarative security model vs. Imperative security model

When it comes to specifying security requirements with WSE, there are two possible options available.

Defining security requirements for SOAP messages in an XML file either by using the WSE Settings tool's security settings wizard as shown in Figure 3, or by manually adding policy elements, child elements, and attributes to a policy file.

WSE security

Figure 3. WSE Security Settings Wizard

Security requirements can be defined in code, mostly when the deployment environment is known beforehand and is not likely to change, without using a policy file. To secure a Web service, we use a class that derives from Microsoft.Web.Services3.Design.Policy, and within this class, we create the required turnkey security assertion class instances to which we specify the respective security credentials, and then we add to an ordered list collection exposed by the Assertions property of the Policy class as shown in the following example.

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

// Class that derives from public class Microsoft.Web.Services3.Design.Policy
public class WebServiceSecurity : Policy
{
    public WebServiceSecurity() : base()
    {
        Assert();
    }

    private void Assert()
    {
        // Create a new instance of a turnkey security assertion, in this case, KerberosAssertion type.
        KerberosAssertion K_assertion = new KerberosAssertion();

        // Specify a security token provider for the Web service's security credentials.
        K_assertion.KerberosTokenProvider = new KerberosServerContext(true, ImpersonationLevel.Impersonation);

        // Add the assertion to the Assertions of the Policy
        this.Assertions.Add(K_assertion);
    }
}

Even if WSE allows us to define security in code and there are certain instances where we need to secure a service client using mixed policy and code, it is more practical and flexible to let administrators define and apply security declaratively, using a policy file, during application deployment.

Common security scenarios and assertions

Based on common SOAP message security scenarios, WSE 3.0 provides the so-called Turnkey Security Assertions which are classes and policy elements that allow us to express the level of security, credentials for authentication, and message signing and sealing. Custom security assertions can also be created when the ready-made security assertions do not meet the requirements of a particular security scenario. Below is a list and explanation of the Turnkey Security Assertions.

  • AnonymousForCertificate: In this scenario, the Web service is accessed from the Internet, or an Intranet, and the client remains anonymous while the message is secured using the server's X.509 certificate.
  • Kerberos: In this scenario, the client and the server reside within an Active Directory Domain or Forest- within an Intranet- and mutual authentication and message protection is implemented by using Kerberos tickets.
  • MutualCertificate10 & MutualCertificate11: In this scenario, the Web service is accessed from the Internet, and both the client, which mostly is a Gateway application- e.g. ASP.Net Application- and the Web Service exchange their X.509 certificates which they use for message protection and authentication. The numbers 10 and 11 represent compatibility with WS-Security 1.0 and WS-Security 1.1 specifications.
  • UsernameForCertificate: In this scenario, the Web service is accessed from the Internet, and the client is authenticated using username and password while the message is secured using the server's X.509 certificate.
  • UserNameOverTransport: In this scenario, the Web service is accessed from the Internet, and the client is authenticated using a username and password. Message confidentiality and integrity are implemented using transport-level security such as SSL.

Policy file structure

To secure SOAP messages declaratively with WSE, we use XML configuration files also known as Policy files. These files have a ".config" file extension and can be created manually or by using the WSE 3.0 Settings tool. The <policies><policies/> element stands as the root element of each policy file; within it, a collection of termed policies and policy extensions is defined. The following is an example of the structure of a policy file.

<policies>
    <extensions>
        <extension name="extension_A" type="..."/>
        <extension name="extension_B" type="..."/>
    </extensions>
   
    <policy name="Policy_A">
        <!-- Policy assertion elements and attributes -->
    </policy>
   
    <policy name="Policy_B">
        <!-- Policy assertion elements and attributes -->
    </policy>
</policies>

A demo of WSE 3.0 in action

If we take a common security scenario as an example, where we have a Smart Client application that consumes a Web service application hosted on the internet, to secure communication between the two, using WSE, we would need to do the following.

  • Decide which Turnkey Security Assertion to use. Based on our scenario, there are three security assertions available to us: the anonymousForCertificateSecurity, the usernameForCertificateSecurity, and the usernameOverTransportSecurity Turnkey Security Assertions. We can choose between the first two as usernameOverTransportSecurity relies on the transport for message confidentiality and integrity.
  • Issuing the Certificate For the anonymousForCertificateSecurity and the usernameForCertificateSecurity assertions, we require a private key pair so that our SOAP message requests and responses are digitally signed and encrypted. In order to do so, we need to issue, from a CA, a personal certificate with an asymmetric key for the web service and export the public key to the client by means of an X.509 digital certificate from a trusted CA.
  • WSE-enabling the Service In order to enable the service to respond using SOAP extensions, we need to configure its configuration file (Web. config) accordingly by adding the following directive.
<webServices>
    <soapExtensionImporterTypes>
        <add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </soapExtensionImporterTypes>
    <soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</webServices>

The WSE 3.0 Settings tool simplifies this process by checking a couple of checkboxes, as shown in Figure 4.

WSE4.gif

Figure 4. WSE-enabling the Web service

Creating the policy files

In order to secure both the client and the service using WSE and a declarative programming model, we need to create a policy file for each based on the security assertion that we deemed fit earlier. The following is the content of a policy file for a Web service generated using the WSE 3.0 Settings tool for the anonymousForCertificateSecurity assertion.

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
    <extensions>
        <extension name="anonymousForCertificateSecurity"
                   type="Microsoft.Web.Services3.Design.AnonymousForCertificateAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <extension name="x509"
                   type="Microsoft.Web.Services3.Design.X509TokenProvider, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <extension name="requireActionHeader"
                   type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </extensions>
    <policy name="WebServicePolicy">
        <anonymousForCertificateSecurity establishSecurityContext="false"
                                         renewExpiredSecurityContext="true"
                                         requireSignatureConfirmation="false"
                                         messageProtectionOrder="SignBeforeEncrypt"
                                         requireDerivedKeys="true"
                                         tlInSeconds="300">
            <serviceToken>
                <x509 storeLocation="LocalMachine"
                      storeName="My"
                      findValue="CN=SampleCertificateA"
                      findType="FindBySubjectDistinguishedName" />
            </serviceToken>
            <protection>
                <request signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody"
                         encryptBody="true" />
                <response signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody"
                          encryptBody="true" />
                <fault signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody"
                        encryptBody="false" />
            </protection>
        </anonymousForCertificateSecurity>
        <requireActionHeader />
    </policy>
</policies>

The following is the content of a policy file for a Web service client generated using the WSE 3.0 Settings tool for the anonymousForCertificateSecurity assertion.

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
    <extensions>
        <extension name="anonymousForCertificateSecurity"
                   type="Microsoft.Web.Services3.Design.AnonymousForCertificateAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <extension name="x509"
                   type="Microsoft.Web.Services3.Design.X509TokenProvider, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <extension name="requireActionHeader"
                   type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </extensions>
    <policy name="ClientPolicy">
        <anonymousForCertificateSecurity establishSecurityContext="true"
                                         renewExpiredSecurityContext="true"
                                         requireSignatureConfirmation="false"
                                         messageProtectionOrder="SignBeforeEncrypt"
                                         requireDerivedKeys="true"
                                         ttlInSeconds="300">
            <serviceToken>
                <x509 storeLocation="CurrentUser"
                      storeName="AddressBook"
                      findValue="CN=SampleCertificateA"
                      findType="FindBySubjectDistinguishedName" />
            </serviceToken>
            <protection>
                <request signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody"
                         encryptBody="true" />
                <response signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody"
                          encryptBody="true" />
                <fault signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody"
                        encryptBody="false" />
            </protection>
        </anonymousForCertificateSecurity>
        <requireActionHeader />
    </policy>
</policies>

It is important to note, however, that when the anonymousForCertificateSecurity assertion is used, the client signs and encrypts the SOAP request using EncryptedKeyToken security tokens generated using the service's X.509ServiceToken, while the service signs and encrypts the SOAP response using the EncryptedKeyToken security token used to encrypt the SOAP request. By using the EncryptedToken security token, therefore, we are able to secure both outgoing and incoming SOAP messages symmetrically even though our security credential consists of one asymmetric key pair.

How to Apply the Policy to a Web Service?

To apply the Policy to a Web service, we use the PolicyAttribute class of Microsoft.Web.Services3 namespace, which takes the name of the policy as a string parameter. The attribute is applied to the class of the Web Service as in the following example.

[WebService(Namespace = "http://my_Namespace")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[Policy("webServicePolicy")]
public class WSE_anonymousForCertificateService : System.Web.Services.WebService
{
    // Your web service code goes here
}

How to Apply the Policy to a Web service client

Once we've set a reference to our WSE-enabled Web service from our client, we can then apply the Policy by calling the SetPolicy method of the service proxy class, adding the name of our policy as a string parameter. The method is called from the instance of our service proxy class as in the following example.

class WSE_AnonymousForCertificateClient
{
    static void Main(string[] args)
    {
        // Create an instance of the Web service proxy
        WSE_AnonymousForCertificateServiceWse WebServiceproxy = new WSE_AnonymousForCertificateServiceWse();

        // Call the SetPolicy method with the client Policy name as a parameter
        WebServiceproxy.SetPolicy("ClientPolicy");

        // ...
    }
}

Conclusion

This article looked at how security for applications that consume Web services using WSE version 3.0 is simplified by the use of ready-made security assertions and a declarative programming model. Furthermore, it explored notions of distribution and scalability when implementing web service security and how the WSE 3.0 Settings tool trivializes the process of creating Policy files and WSE-enabling applications.


Similar Articles