Applies To:
The Information provided in this article applies to the following technologies: IIS 6.0, .Net Framework 2.0, Visual studio 2005, WSE 3.0, Windows Server 2003 Family OS
Introduction
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 scalability of Web service applications extremely difficult- figure 1 depicts such a scenario.
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.
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 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.
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, 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, 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 username and password. Message confidentiality and integrity is 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, 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 in the internet, to secure communication between the two, using WSE, we would need to do the following:
<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 to checking a couple of check boxes as shown in figure 4.
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 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 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 consisted of one asymmetric key pair.
Applying the Policy to a Web service
To apply the Policy to a Web service we use the PolicyAttribute class of the 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
{}
Applying 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.