WS-I BSP Sample Application for WSE 3
The
"WS-I Basic Security Profile Sample Application"
preview for WSE 3.0 is out, you can get it in the
GDN workspace.
This sample illustrates how to build secure and
interoperable web services based in the specification
WS-I Basic Profile 1.1.
When we started to develop this application, we
faced some challenges, all of them related to the new policy
framework shipped in WSE 3.0.
Some parts of the
policies used by the previous version of this application
weren't easy to migrate, so we had to develop some custom
assertions.
In this post, I will give a brief
description about the new WSE "Policy framework", and the
custom assertion shipped in this preview.
(CustomX509Assertion)
Policy framework

Policies
A policy allows to apply different claims for incoming and outgoing messages on the client and the service (All messages to a particular endpoint).
It is used to describe the requirements for a service and as a factory for runtime objects - Pipeline and Assertions.
As you can see in the image, the policy is converted in a pipeline at runtime. This pipeline contains an ordered list of assertions,
each assertion performs different message transformations through the use of Filters.
Policy definition sample:
<policies>
<extensions>
<extension
name="mutualX509Security"
type="Microsoft.Web.Services3.Design.MutualX509Assertion,
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" />
</extensions>
<policy
name="MyPolicy">
<mutualX509Security
establishSecurityContext="false"
renewExpiredSecurityContext="true"
signatureConfirmation="true"
protectionOrder="SignBeforeEncryptingAndEncryptSignature"
deriveKeys="true" actor="">
<clientToken>
<x509
storeLocation="CurrentUser" storeName="My"
findValue="CN=WSE2QuickStartClient"
findType="FindBySubjectDistinguishedName" />
</clientToken>
<serviceToken>
<x509
storeLocation="CurrentUser" storeName="AddressBook"
findValue="CN=WSE2QuickStartServer"
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>
</mutualX509Security>
</policy>
</policies>
A policy can be assigned to a proxy or a service using a declarative or imperative way. The following examples show how to assign a policy
to a proxy and a service:
Client proxy with PolicyAttribute:
[PolicyAttribute("MyPolicy")]
public partial class
MyService :
Microsoft.Web.Services3.WebServicesClientProtocol
{
public string HelloWorld() {
object[]
results = this.Invoke("HelloWorld", new object[0]);
return
((string)(results[0]));
}
}
Imperative use of policy on the client
MyService service = new MyService();
service.SetPolicy("MyPolicy");
Web Service with PolicyAttribute:
[PolicyAttribute("MyPolicy")]
public class MyService :
System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello world";
}
}
Assertions
An assertion uses filters to perform message processing in different stages. Each assertion is capable of creating up to four soap filters:
An assertion may not create a filter in one of the four locations. In that case, this assertion does not affect message processing in this location.
For example, a security assertion could use the output stages to protect a soap document, and the input stages to validate that protection
Assertion code sample :
class MyAssertion : PolicyAssertion
{
public
override SoapFilter
CreateClientInputFilter(FilterCreationContext context)
{
return
null;
}
public override SoapFilter
CreateClientOutputFilter(FilterCreationContext context)
{
return null;
}
public
override SoapFilter
CreateServiceInputFilter(FilterCreationContext context)
{
return null;
}
public
override SoapFilter
CreateServiceOutputFilter(FilterCreationContext
context)
{
return null;
}
}
This assertion does not return any filter, it is useless in a real scenario.
WSE includes the following list of assertions:
| AnonymousOverCertificateAssertion | The client is not authenticated and the security protection is via a server's X.509 certificate |
| CertificateMutualAuthenticationProfileAssertion | X.509 certificates are used for authentication and message protection. It doesn't use the WS-Security 1.1 extensions |
| KerberosAssertion | Kerberos tickets are used for authentication and message protection |
| MutualCertificateAssertion | X.509 certificates are used for authentication and message protection. It uses the WS-Security 1.1 extensions |
| UsernameOverCertificateAssertion | The client is authenticated via a suplied "UsernameToken" ( user and password ) and the security protection is performed by a X.509 certificate |
| UsernameOverTransportAssertion | The client is authenticated via a supplied "UsernameToken" and the security protection is performed at the transport level |
| AuthorizationAssertion | It performs authorization checks using the identity token. The identity token is determined after the client authentication |
Filters
Soap filters use lower level mechanisms to perform different message processing tasks. For example, a security filter could use signatures and encryption tokens to protect a message.
All filters inherit from the base class "SoapFilter" and implement the abstract method "ProcessMessage".
public abstract class SoapFilter
{
protected
SoapFilter();
public virtual T GetBehavior
public abstract SoapFilterResult
ProcessMessage(SoapEnvelope envelope);
}
That method returns a "SoapFilterResult" class, which is used to control the pipeline execution. It can take the following values:
WSE also includes the classes "SendSecurityFilter" and "ReceiveSecurityFitler" to build security filters. These classes inherit from "SoapFilter", but they implement the "ProcessMessage" method in order to parse the security headers included in the soap document.
public abstract class SendSecurityFilter : SoapFilter
{
protected
abstract void SecureMessage(SoapEnvelope envelope,
Security security);
}
public abstract
class SendSecurityFilter : SoapFilter
{
protected
abstract void ValidateMessageSecurity(SoapEnvelope
envelope, Security security);
}
Custom Security Assertion
The "MutualCertificateAssertion" did not solve some
security aspects required
by the application, so we
had to develop a custom security assertion (
"X509CustomSecurityAssertion" )
This assertion
allows us to fulfill the following requeriments:
1. Sign and encrypt the messages with
different X509 certificates ( One certificate to sign the
message and
another to encrypt it ). The
"MutualCertificateAssertion" assertion only signs and
encrypts the messages with a key derived from
the
same certificate.
2. Different certificates for
requests and responses. The "MutualCertificateAssertion"
does not allow to specify that
3. Custom headers
encryption. The "MutualCertificateAssertion" only signs
custom headers, but it does not encrypt them.
4.
Different protection order for the request and response.
The "MutualCertificateAssertion" specifies the same
protection order
for the request and the response (
Protection order =
SignBeforeEncryptingAndEncryptSignature,
SignBeforeEncrypting, EncryptBeforeSigning).
This
policy is used by client application to consume the
Retailer services:
<policy name="RetailerServices">
<customX509Security
actor="">
<request>
<clientToken>
<!--
WebClient Signing Certificate -->
<x509
storeLocation="LocalMachine" storeName="My"
findValue="8d5f67d8991bc6517785b3266a333fb871cf2c6f"
findType="FindBySubjectKeyIdentifier" />
</clientToken>
<serviceToken>
<!--
Retailer Encrypting Certificate -->
<x509
storeLocation="LocalMachine" storeName="My"
findValue="944e5a12f31f6f8456ae6ad479581792af15eb6b"
findType="FindBySubjectKeyIdentifier" />
</serviceToken>
</request>
<response>
<clientToken>
<!--Retailer
Signing Certificate-->
<x509
storeLocation="LocalMachine" storeName="My"
findValue="4cd379e9caff8759da99b17c85ffa15b66c60dcb"
findType="FindBySubjectKeyIdentifier" />
</clientToken>
<serviceToken>
<!--
WebClient Encrypting Certificate -->
<x509
storeLocation="LocalMachine" storeName="My"
findValue="399cb4ee8d3339c36618f667dfa03183948f145c"
findType="FindBySubjectKeyIdentifier" />
</serviceToken>
</response>
<!-- getCatalog -->
<protection
requestAction="getCatalog">
<request
signatureOptions="IncludeAddressing, IncludeTimestamp,
IncludeSoapBody" encryptBody="false"
protectionOrder="SignBeforeEncrypting">
<customHeader name="UsernameToken"
ns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
signed="true"
encrypted="false"></customHeader>
</request>
<response
signatureOptions="IncludeTimestamp, IncludeSoapBody"
encryptBody="true"
protectionOrder="SignBeforeEncryptingAndEncryptSignature"
/>
</protection>
<!--
submitOrder -->
<protection
requestAction="submitOrder">
<request
signatureOptions="IncludeAddressing, IncludeTimestamp,
IncludeSoapBody" encryptBody="true"
protectionOrder="SignBeforeEncryptingAndEncryptSignature">
<customHeader name="Configuration"
ns="http://www.ws-i.org/SampleApplications/SupplyChainManagement/2002-08/Configuration.xsd"
signed="true"
encrypted="false"></customHeader>
<customHeader name="UsernameToken"
ns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
signed="true"
encrypted="true"></customHeader>
</request>
<response
signatureOptions="IncludeTimestamp, IncludeSoapBody"
encryptBody="true"
protectionOrder="SignBeforeEncryptingAndEncryptSignature"
/>
</protection>
</customX509Security>
</policy>
Many changes were introduced to the policy. It
supports different X509 certificates for requests and
responses ("Request" and "Response" elements ).
Also,
it specifies different protection requirements for custom
headers and different protection order for each message.
If you are interested to build secure and
interoperable web services, then you should look this
application, it is a good starting point.
That is all
for now, I will blog more about WSE and WSI soon.