Export (0) Print
Expand All
Expand Minimize

Web Services Security Interoperability Using WSE 2.0 SP3 and WebLogic Workshop 8.1.4

 

Michel Barnett
Microsoft Corporation

June 2005

Applies to
   Microsoft .NET Framework 1.1
   Microsoft Visual Studio .NET 2003
   Microsoft Web Services Enhancements (WSE) 2.0 SP3
   WebLogic Platform 8.1.4

Summary: This article explains Interoperability based on OASIS Web Services Security (WS-Security) 1.0 between Microsoft WSE 2.0 SP3 and WebLogic Platform 8.1.4. This article is accompanied by a sample application (the Math sample). The Math sample demonstrates how to securely authenticate, sign, and encrypt SOAP messages exchanged between WSE 2.0 SP3 and WebLogic Platform 8.1.4. (50 printed pages)

Source Download:

Click here to download the source code for this article.

Contents

Overview
Math Sample
Other Interop Issues
Summary
Acknowledgements
References

Required Software and Skills to Run the Code Samples

Overview

Since about 1999 when the first SOAP specification was released, vendors have written stacks that allow simple messages to be exchanged between clients and services. The basic specifications we use today are SOAP 1.1 and WSDL 1.1. Even with these standards, interoperability can still be a challenge. This is mainly due to ambiguities in these specifications that allow vendors to implement them differently. Standards bodies such as the Web Services Interoperability Organization (WS-I) have eliminated many of these ambiguities and defined specific sets of rules for how these specifications should be employed. This has resulted in the WS-I Basic Profile Version 1.01. This is a set of Web services specifications, along with clarifications and amendments, which are designed to promote interoperability. It's now common for vendors to build stacks that are "Basic Profile compliant." The Basic Profile itself is mainly focused on use of SOAP 1.1, HTTP 1.1, and WSDL 1.1.

Since the advent of SOAP, WSDL, and the Basic Profile, Web service developers have become interested in doing more than exchanging simple messages with one another. Of particular interest to this article is the idea of adding security on top of simple message exchanges. The most important specification to address this is WS-Security2 which describes enhancements to SOAP messaging to provide message integrity and confidentiality. WS-Security also provides a general-purpose mechanism for associating security tokens with message content (that is, a way to pass credentials from a client to a service).

Similar to SOAP and WSDL, WS-Security defines a framework for adding security services to SOAP messages. But simply having two Web services stacks that implement WS-Security doesn't mean that they'll interoperate. There is now an OASIS Web Services Security 1.0 (WS-Security 2004) standard3 that defines specific ways that WS-Security should be used—all in the name of improving interoperability. The OASIS WSS 1.0 standard consists of the following specifications:

  • Web Services Security: SOAP Message Security V1.04.
  • Web Services Security: Username Token Profile V1.05.
  • Web Services Security: X.509 Token Profile V1.06.
  • Security Schema Files V1.07.

The SOAP Message Security V1.0 specification describes enhancements to SOAP messaging to provide confidentiality and integrity. The X.509 Token Profile V1.0 specification describes how to use X.509 certificates with the WS-Security specification (WS-Security provides a general purpose mechanism for associating security tokens to messages but does not get into specifics). The Username Token Profile V1.0 performs a similar purpose for username tokens.

Approach

This article aims to demonstrate interoperability between BEA WebLogic Workshop 8.1.4 and Microsoft WSE 2.0 SP3. Both stacks are designed to be WS-I Basic Profile V1.0 and OASIS WSS V1.0 compliant. Both represent the latest production released and supported Web services stacks from each vendor at the time of this writing.

This paper is written in the context of a sample application (the Math sample) that demonstrates security interoperability between the two platforms:

ms998291.wssinteropwblg01(en-us,MSDN.10).gif

Figure 1. Math SampleApplication Architecture

There are two basic scenarios demonstrated in the sample:

  • X.509 Signing & Encryption—The client sends a message to the service signed with its private key. The associated X.509 certificate is used for client authentication. The message is also encrypted for the server using the public key corresponding to the server's X.509 certificate.
  • Username Token over HTTPS—The client sends a message to the service accompanied by a username token. The service then uses the username/password in the token to authenticate the service. Because clear-text credentials are used, the underlying transport is encrypted using HTTPS.

Notice that the figure indicates that there are two versions of the Web service. For example if you look at the .NET implementation there are two Web service endpoints:

  • X509Math.asmx—Called by users wishing to authenticate with X.509 credentials.
  • UsernameMath.asmx—Called by users wishing to authenticate with username token credentials.

The separation exists because each version of the Web service has its own security policy. But as we'll see later each version of the Web service shares the same implementation of the server side logic (that performs the arithmetic operations).

To implement these scenarios the Math sample application includes a .NET and WebLogic code base. In turn, each code base has two components:

  • Client
  • Math Web Service

The Web service itself is trivial. A common interface is implemented by both sets of Web services (.NET and WebLogic) that returns the result of adding or subtracting two integers. Such a simple example was chosen for two reasons: 1) it's simple enough to allow the reader to focus on the security aspects of the service rather than the service itself 2) it's just complex enough to generate different results based on varying input (in other words, there's enough substance to prove that the Web service isn't just returning hard-coded values).

The person running the client is a fictitious user named Alice. Alice has her choice of calling one of two versions of the Math service—depending on the type of credentials she chooses. In the first case, Alice uses a username token to authenticate with the service. In this case she enters her username and password. When the request message is sent to the service, it's accompanied by a username token (normally sent over a HTTPS connection to the service).

In the second case, Alice uses a certificate to authenticate with the service. In this case she'll sign messages she sends to the service using her public/private key pair. When X.509 certificates are used, the Math service also requires all messages sent to it to be encrypted. To satisfy this requirement, the client encrypts messages it sends to the Math service with the service's public key. In turn, the Math service decrypts incoming messages with its private key.

The client is a graphical user interface that allows the user to invoke the Web service.

ms998291.wssinteropwblg02(en-us,MSDN.10).gif

Figure 2. .NET Client

Note that the client is implemented to enforce the choice the user must make in choosing the type of credentials to use (and therefore which version of the Web service to call). In either case, the .NET client essentially accepts three pieces of information:

  • The Web service endpoint URI.
  • Security credentials.
  • Web service parameters (the arithmetic input parameters to the Web service).

The client always exchanges messages directly with the Math service. The Math Service performs the requested arithmetic operation and returns a result that is displayed in the client interface.

To demonstrate interoperability, each client (the .NET and WebLogic version) can be pointed to either implementation of the Math service (this is why the client accepts an endpoint URI). If the two Web services stacks are truly interoperable, either client should work against either Math service without any problem.

The Math sample demonstrates the following:

  • Protecting message integrity—When using X.509 token credentials, request messages are signed with X.509 certificates, ensuring that messages haven't been tampered with in transit.
  • Protecting message confidentiality—When using X.509 token credentials, the client encrypts its request message to the Math service ensuring that no one except the Web service can read the message.
  • Authentication—The Math service uses the signature of the request message or the accompanying username token to prove the identity of the sender.
  • Authorization—The Math service authorizes the authenticated user to perform the requested operation.
  • Programmatic security—The client calls the Math service programmatically. This includes program code to sign and encrypt the request message to the Math service.
  • Policy based security—The Math service uses policy files to describe its security requirements for messages sent to it (from the client). The Math service never programmatically handles authentication, integrity, or confidentiality.

When using X.509 token credentials, the client signs and encrypts the messages it sends to the Math service. Signing is done mainly to authenticate the caller to the Math service. Encryption is done to obfuscate the messages sent from the client to the service. Note that signing and encryption do not need to be used together. In fact, another common scenario is to sign messages without encrypting them. It should be clear from the sample how to perform other combinations of signing and encryption if that's what's required in your environment.

Note that it's desirable whenever possible to use policy files to describe your security requirements rather than relying on programmatic security. Policy is more flexible and easier to implement. The reason programmatic security is used here is because it's required in the WebLogic client. Policy isn't an option for the client8. To keep the two implementations as close as possible, programmatic security is also implemented in the .NET client (but be aware that policy is an option for .NET clients). The choice of programmatic vs. policy based security is made here as a matter of necessity, not preference.

Running the Sample

This article explains the implementation of the Math sample application, but it does not explain the mechanics of installing and running it. Instead those details are left to a readme file that accompanies the sample application. The readme explains the structure of the source files, installation requirements, installation instructions, how to build the sample, and a walkthrough of its operation.

The intention is that you read this article before the readme. However, the latter is complete enough that you could skip directly to installing and running the sample application, before reading any further (you can consider the readme file a quick-start). Pick whichever order you're comfortable with.

Math Sample

The Math sample consists of the client that you've already seen as well as the Web service that provides the underlying implementation. Of course there are two implementations of each set—one written in .NET and the other using WebLogic.

To show how each is built, we'll walk through the construction of each implementation. We'll start by building the basic .NET client/service and follow that by building the WebLogic equivalent. Initially there will be no security. This will allow you to see how a basic client and service is built on each platform. Next we'll add support for username token credentials. Finally, we'll add support for X.509 certificates. Along the way I'll point out a few things to watch out for concerning interoperability between the two platforms.

The .NET and WebLogic implementations are purposely designed to be as similar in structure as possible. This includes the organization of the source code (down to class and variable names where possible). The idea is that if you're familiar with one platform and not the other, you should be able to pick up the concepts of the lesser understood implementation by mere pattern matching. Because of the platform differences this wasn't always possible, but you should still see a lot of parallelism between the two implementations.

Before we build either of the Web services we have to have a common definition of the interface that they'll expose. So we'll start by looking at the WSDL for the service interface.

Math Service Interface

An important part of the Math sample application is that the client be able to communicate with either (the WebLogic or .NET) implementation of the Math service. To achieve this, it's important that both implementations of the Math service implement the same interface (WSDL binding). To make absolutely certain of this, the WSDL definition for the Web service interface was written first and both implementations were generated from this definition. This is often referred to as a WSDL-first approach.

The WSDL definition in the Math sample is physically split into four files:

  • Math.XSD—The XML types used in the Math interface.
  • MathBindings.WSDL—The WSDL message, portType, and binding definitions. This file logically defines the Math interface. Math.XSD is imported by this file.
  • UsernameMath.WSDL—The WSDL service definition for the version of the Math service that expects username token credentials. This defines the endpoint address of the Math service. MathBindings.WSDL is imported by this file.
  • X509Math.WSDL—The WSDL service definition for the version of the Math service that expects X.509 token credentials. This is the same as UsernameMath.WSDL except the endpoint address refers to the X.509 version of the service.

I won't go into detail on the WSDL definition in this article (you can refer to the accompanying sample). The important point to keep in mind is that this definition implements a simple interface exposed by both versions of the Math service. The .NET binding of the WSDL definition looks like this:

public interface IMath
{
    int Add(int X, int Y);
    int Subtract(int X, int Y);
}

There are two operations in this interface: Add and Subtract. The first returns the sum of the given integers. The latter returns the difference.

Now that the interface is defined, let's move on to providing some underlying implementation. We'll start with.NET.

.NET Client

The client is where the processing in our sample application begins. The client accepts the endpoint address of the Web service, the credentials used to access the Web service, and the input parameters for the Web service itself.

ms998291.wssinteropwblg03(en-us,MSDN.10).gif

Figure 3. .NET Client

Let's cover each of these parameters in turn:

  • Username Token Credentials:
    • URI9—The URL of the Web service (UsernameToken version) that the client calls. By default this is the .NET Math service.
    • User Name—The name of the user that's authenticating with the service.
    • Password—The password for the given user.
  • X.509 Token Credentials:
    • URI—The URL of the Web service (X.509 version) that the client calls. By default this is the .NET Math service.
    • Signing Certificate Name—The name of the certificate used to sign the request message to the Math service. The private key of the sender is used to sign the message.
    • Encrypting Certificate Name—The name of the certificate used to encrypt the request message to the Math service. The public key of the recipient is used to encrypt the message.
    • Sign/Encrypt—These check boxes control whether the request message is signed and/or encrypted. The Math service requires both but turning one or both options off allows you to see what happens when the recipient's security policy is violated.
  • Math Web Service:
    • The text fields to the left of the buttons are the input parameters to the Web service. When the user presses the Add button, the two numbers are added and the sum is displayed. When the Subtract button is pressed the two numbers are subtracted and the difference is displayed.

.NET Client Source Code

The source code for the client is primarily contained in MainForm.cs and MathClient.cs. There's also a helper class in KeyUtil.cs (all are found in the .\dotNet\Client directory). The code for the user interface has been carefully separated from the code that calls the Web service. MainForm.cs contains the user interface code and MathClient.cs contains the Web service code.

Since this paper isn't about WinForms development, I'll skip any explanation of MainForm.cs except to note the calls to MathClient.Add() and MathClient.Subtract() in the click event handlers for the Add and Subtract buttons, respectively. Both methods are virtually identical. The MathClient.Add() method looks like this:

public static int Add(string serviceURI, int x, int y, 
    bool userNameToken, string userName, string password, bool sign, bool encrypt, 
    string signingCertificateName, string encryptingCertificateName)
{
    // create math proxy
    MathService.Math math = new MathService.Math();
    math.Url = serviceURI;

    // add security headers to SOAP message
    AddSecurityToMessage(math.RequestSoapContext, userNameToken, userName, password, 
        sign, encrypt, signingCertificateName, encryptingCertificateName);

    // call the web service
    int sum = math.Add(x, y);

    // return the response
    return sum;
}

Each method implements the same procedure:

  1. Create the math proxy/ set the service URI.
  2. Add security (signature, encryption, username token) to the message.
  3. Call the Web service.
  4. Return the result.

In the first step, the math proxy is represented by the MathService.Math class. This was generated for us by Visual Studio by adding a Web reference to the project.

Click here for larger image.

Figure 4. Visual Studio.NET Add Web Reference Dialog

All that's needed to add a Web reference is to enter the URI of the WSDL along with a name for the Web service being referenced. The generated class allows us to call the Web service just like any other .NET class. Note that adding a Web reference in Visual Studio.NET is equivalent in this case to executing the following command line:

wsdl.exe /namespace:Math.Net.Client.MathService http://localhost/Math.WSDL/UsernameMath.WSDL

Going back to our code, the next thing we do is set the URL property of the math service which sets the endpoint address. This overrides any endpoint address that may be found in the WSDL file's service definition10. This is the same service URI from the user interface.

Before we call the Web service we need to add appropriate security to the message. This is done with the helper method: AddSecurityToMessage. For now we'll just stub in this method.

Next we call the Web service (which in our proxy class is a simple method call). Finally, we return the result to the caller.

For our purposes calling a Web service is a just a matter of invoking a method on an object.

That's all there is to creating the .NET client. We'll add security later. But for now let's take a look at how the Web service is built.

.NET Math Web Service

The Web services are where the back-end processing of our sample application is performed. In this case, this means performing arithmetic operations on a given set of integers.

The Math service is always directly called by the client. It takes two integers as arguments, performs the requested arithmetic operation, and returns the result. That result is displayed in the client.

Creating the Web Service Stub

To simplify the design, each Web service implements the same interface. This is the IMath interface described in the WSDL definition above. So the first thing we need to do is to generate a stub implementation based on this definition.

The WSDL.EXE tool is used to create the server side stub from the WSDL definition. Recall that the WSDL.EXE tool was the same one used to create the client side proxy. When we generated the proxy we invoked the WSDL.EXE tool through Visual Studio.NET (by adding a Web reference to our project). In this case, we'll invoke the WSDL.EXE tool directly from the command line:

WSDL.EXE /language:CS /server /namespace:Math.Net /out:MathStub.cs http://localhost/Math.WSDL/UsernameMath.WSDL

The /server switch tells WSDL to generate a server side stub (the absence of this switch causes the tool to generate a client side proxy). The /language switch indicates that we want it to generate the stub in C#. We also specify the namespace with the /namespace switch, as well as the name of the output file with the /out switch. Finally, we specify the WSDL file (which in our sample application is located at the URL above).

The command line to generate the Math Web service is contained in the .\dotNet\Service\createstub.bat file.

The generated file is an abstract class that implements our Web service. The class is a simple .NET class called Math with two methods: Add and Subtract. The part that makes it a Web service implementation is the base class as well as the attributes that decorate it.

[System.Web.Services.WebServiceBindingAttribute(Name="IMath", 
    Namespace="urn:schemas-microsoft-com:mathwebservice:bindings")]
public abstract class Math : System.Web.Services.WebService {
  [System.Web.Services.WebMethodAttribute()]
  [System.Web.Services.Protocols.SoapDocumentMethodAttribute(
      "urn:schemas-microsoft-com:mathwebservice:operations:add", 
      RequestNamespace="urn:schemas-microsoft-com:mathwebservice:types", 
      ResponseNamespace="urn:schemas-microsoft-com:mathwebservice:types", 
      Use=System.Web.Services.Description.SoapBindingUse.Literal, 
      ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
  [return: System.Xml.Serialization.XmlElementAttribute("Sum")]
  public abstract int Add(int X, int Y);
         
  // Subtract() method excluded for brevity
}

The WebServiceBindingAttribute is a cue to the runtime that this class is a Web service. The Name and Namespace properties are picked up from the name of the WSDL binding. It's possible that a .NET class can implement operations from multiple bindings in which case there would be multiple WebServiceBindingAttributes on the class. The SoapDocumentMethodAttribute or the SoapRpcMethodAttribute can then be placed on each method and refer back to its respective binding by means of the binding property. In our sample, only one binding is implemented by this class so we don't have this situation. In fact, in this particular sample, the WebServiceBindingAttribute could be removed and everything would work fine.

Moving down to the internals of the Math class, we see three attributes decorating the Add method. The WebMethodAttribute decorates all Web methods. It's a cue to the runtime that this is a method that implements a Web service operation. The SoapDocumentMethodAttribute indicates that this Web method follows the document message style. The alternative is to decorate this with the SoapRpcMethodAttribute which would indicate that the method follows the RPC message style. However, since we specified the document style in the WSDL binding, the former attribute is used. The properties of the SoapDocumentMethodAttribute specify the following:

  • SOAPAction—The SOAPAction of the operation. This is how the incoming SOAP message is routed to the .NET method.
  • RequestNamespace/ResponseNamespace—These are a cue to the serializer. They specify the namespace of the request and response parameters. These are both the same and come from the schema we defined earlier.
  • Use—This is a cue to the serializer. It specifies the message format. This is picked up from the WSDL binding definition.
  • ParameterStyle—This is a cue to the serializer. 'Wrapped' indicates that parameters sent to and from the Web service are encapsulated within a single XML element following the Body element of the SOAP message. 'Bare' would have indicated that the parameters appear directly inside the Body element. The schema in the WSDL definition dictates that 'Wrapped' should be used.

Finally the XmlElementAttribute is a cue to the serializer. This indicates that the return value should be contained in an element called Sum, which our schema dictates.

Implementing the Web Service

Once the Web service stub is generated, I copied MathStub.cs to UsernameMath.cs and X509Math.cs (using a different name means that if I regenerate the stub I don't overwrite the production file). The two files represent the version of the Math service expecting username and X.509 token credentials respectively. These files were then included in a Visual Studio.NET C# Class Library Project. I removed the abstract modifier from the class and method definitions and filled in the implementation for each method directly.

What's left is the implementation found in the sample code. The UsernameMath class is shown here:

[System.Web.Services.WebServiceBindingAttribute(. . .)]
public class UsernameMath : System.Web.Services.WebService 
{
    public UsernameMath() {}
        
    [System.Web.Services.WebMethodAttribute()]
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute(. . .)]
    [return: System.Xml.Serialization.XmlElementAttribute(. . .)]
    public int Add(int X, int Y)
    {
        // authorize caller before proceeding
        AuthorizeCaller(RequestSoapContext.Current);

        // process parameters and produce a result
        return MathImpl.Add(X, Y);
    }

    // Subtract() method excluded for brevity
}

Each method contains only two lines. The first authorizes the (already authenticated) caller. Since we haven't included security yet, we'll just stub in this method and come back to it later.

The second line delegates to the MathImpl class. MathImpl provides the actual implementation for the Web service. This follows the same structure used in the client where we separated the user interface code from the business logic. In this case the Web service itself is found in UsernameMath.cs and the underlying implementation is found in MathImpl.cs.

MathImpl

The MathImpl.cs source file contains the implementation of the Math Web service.

public class MathImpl
{
    private MathImpl() {}

    public static int Add(int x, int y)
    {
        // perform calculation and return result
        return x + y;
    }

    // Subtract() method excluded for brevity
}

The only thing to do here is to perform the requested arithmetic operation and return the result.

That's it for the .NET Web service program code. The only thing left to talk about is configuration.

Web Service Configuration

The configuration file for the Math service controls various parameters of the Web service operation.

<configuration>
    <!-- configSections element excluded for brevity -->

    <microsoft.web.services2>
        <!-- policy element excluded for brevity -->
      
        <!-- security element excluded for brevity.  -->
      
        <!-- diagnostics element excluded for brevity -->
    </microsoft.web.services2>

    <system.web>
        <webServices>
            <!-- protocols element excluded for brevity -->
            
            <!-- adds the WSE SOAP extension -->
            <soapExtensionTypes>
                <add type="Microsoft.Web.Services2.WebServicesExtension, 
                    Microsoft.Web.Services2,Version=2.0.0.0, Culture=neutral, 
                    PublicKeyToken=31bf3856ad364e35" priority="1" group="0"/>
            </soapExtensionTypes>
         </webServices>   
    </system.web>
</configuration>

The soapExtensionTypes element allows us to install the WSE 2.0 SOAP Extension. This allows WSE to intercept SOAP messages sent to and from this virtual directory. Any WSE 2.0 Web service needs to have this extension installed. And later on, we'll learn that this gives WSE the opportunity to perform all of the security processing that we need for our sample.

That's it for the Web service and the entire .NET implementation. We should now be able to run the .NET client against the Web service and exercise the arithmetic operations.

WSE 2.0 Configuration Editor

The configuration files used in the Math sample (including the one we just looked at) are all hand-written. This was mainly done to comment the various configuration settings so that the reader understands what they're for. After all, this article is about explaining how these things work.

To make dealing with these configuration files easier, the WSE 2.0 SDK includes a tool called the Configuration Editor (this is found in the Start menu after installing the SDK).

Click here for larger image.

Figure 5. WSE 2.0 Configuration Editor

The Configuration Editor can be used to create and edit the configuration file we just looked at. It can also be used to create the security policy files we'll examine later.

In practice, it's useful to start with the Configuration Editor, use it to create your configuration files, and then make changes to them as needed. This approach gets you up and running more quickly but also helps avoid errors in creating the files from scratch. This will become more important when we get to the security policy files (which are a bit more involved than the configuration file we just looked at).

WebLogic Client

The WebLogic client is the Java equivalent to the .NET version we've already seen. Like its counterpart, the WebLogic client accepts the endpoint address of the Web service, the credentials used to access the Web service, and the input parameters for the Web service itself.

Click here for larger image.

Figure 6. WebLogic Client

Note that the parameters entered into the Java user interface are a bit different than those found in the .NET interface. This is mostly due to the Java keystore. First, we have to specify a path to a keystore (the .NET client assumes the location of the centralized Microsoft certificate store). Second, we have to specify passwords to access the keystore as well as private keys (the .NET client uses integrated security to access this same information in the Microsoft certificate store so passwords aren't necessary in the .NET client).

WebLogic Client Source Code

The source code for the client is primarily contained in MainFrame.java and MathClient.java. Additionally there's a helper class in KeyUtil.java. The code for the user interface has been carefully separated from the code that calls the Web service. MainFrame.java contains the user interface code and MathClient.java contains the Web service code (MainFrame.java serves the same purpose as MainForm.cs in the .NET client).

Since this paper isn't about Swing development, I'll skip any explanation of MainFrame.java except to note the calls to MathClient.Add() and MathClient.Subtract() in the click event handlers for the Add and Subtract buttons respectively. Both methods are almost identical. The MathClient.Add() method is shown here:

public static int Add(String serviceURI, int x, int y, 
    boolean userNameToken, String userName, String password, boolean sign, 
    boolean encrypt, String keyStorePath, String keyStorePassword, 
    String signingCertificateName, String signingCertificatePassword, 
    String encryptingCertificateName) 
{
    // create math proxy
    weblogic.jws.proxies.Math mathProxy = new Math_Impl(serviceURI);
    IMath math = mathProxy.getIMath();

    // add security headers to SOAP message
    AddSecurityToMessage(mathProxy.context(), userNameToken, userName, password, 
        sign, encrypt, keyStorePath, keyStorePassword, signingCertificateName, 
        signingCertificatePassword, encryptingCertificateName);

    // call the web service
    int sum = math.Add(x, y);
 
    // return the response
    return sum;
}

Each method follows the same procedure:

  1. Create the math proxy.
  2. Add security (signature, encryption, username token) to the message.
  3. Call the Web service.
  4. Return the result.

The math proxy is represented by the weblogic.jws.proxies.Math class. This was generated for us by WebLogic with the following command line:

java -cp C:\bea\jdk142_04\lib\tools.jar;C:\bea\weblogic81\server\lib\webservices.jar;
c:\bea\weblogic81\server\lib\weblogic.jar weblogic.webservice.clientgen -wsdl
 http://localhost/Math.WSDL/UsernameMath.WSDL -clientJar Math.jar -packageName weblogic.jws.proxies

There's a script containing this command line in .\weblogic\Client\clientgen.bat.

You can also generate this proxy JAR file using the IDE if you write the Web service first. In that case you can start the Web service in the WebLogic IDE and generate the JAR by clicking the Java Proxy button in the Workshop Test Browser.

Click here for larger image.

Figure 7. WebLogic Test Browser

Going back to our program code, we've now instantiated the Math proxy. But before we call the Web service we need to add security to the message. This is done with the helper method: AddSecurityToMessage. For now we'll just stub in this method.

Next we call the Web service (which thanks to our proxy class is a simple method call). Finally we return the result to the caller.

For our purposes calling a Web service is a just a matter of invoking a method on an object.

We're done creating the WebLogic client. We'll add security later. For now let's take a look at how the Web service is built.

WebLogic Math Web Service

As with the .NET version, the WebLogic Math service is always directly called by the client. It takes two integers as arguments, performs the requested arithmetic operation and returns the result. That result is displayed in the client.

Creating the Web Service Stub

As in the .NET implementation, we need to start our WebLogic Web service with the WSDL definition described earlier. The first thing we need to do is to generate a stub implementation based on this definition.

The WebLogic Math Web service started life as a new Empty Application in the WebLogic Workshop IDE. This application is called Math_WebLogic_Service. To this, was added a new Web Service project called Math. A folder was added to this project called WebLogic and within this folder is a copy of the WSDL from http://localhost/Math.WSDL/UsernameMath.WSDL.

ms998291.wssinteropwblg08(en-us,MSDN.10).gif

Figure 8. Math Project

This is a copy of the WSDL files for the .NET Web service, so we need to modify the endpoint address in each service definition to refer to this Web service. For example, here's the modified service definition in UsernameMath.WSDL:

<!-- Service -->
    <service name="Math">
        <port name="IMath" binding = "mathbindings:IMath">
            <soap:address 
                location="http://localhost:7001/Math/WebLogic/UsernameMath.jws" />
        </port>
    </service>

Now we're ready to generate the Web service. To accomplish that, all we need to do is right-click each WSDL file in the IDE and select Generate Web Service. This generates new files called UsernameMath.jws and X509Math.jws, respectively. These two files represent the two versions of the Web service we need for the username and X.509 credentials that we will be using.

Both UsernameMath.jws and X509Math.jws contain the same code. So as an example, let's look at UsernameMath.jws. This file contains a class that constitutes our username token version of the Web service. The part that makes it a Web service implementation is the base class as well as the javadoc-style annotations that decorate the class and its methods.

package WebLogic;

/**
 * @jws:wsdl file="#MathWsdl"
*/
public class UsernameMath implements com.bea.jws.WebService
{
    . . .
}

The annotations that decorate part of the implementation (the @jws annotations), are based on Java Specification Request (JSR) 181 titled Web Services Metadata for the Java Platform11. Similar to the attributes we saw in the .NET implementation, these annotations are cues to the J2EE runtime to provide additional services for the UsernameMath class.

The @jws:wsdl annotation on the Math class binds this implementation to the WSDL file that it implements (you can see the WSDL definition at the bottom of UsernameMath.jws in the @jws:define annotation).

The first thing we see inside the class definition is a set of embedded classes that represent the parameters of the Web method: Add, AddResponse, Subtract, and SubtractResponse. These are drawn directly from the types in the original WSDL definition.

    public static class Add implements java.io.Serializable
    {
        public int X;
        public int Y;
    }
    
    public static class AddResponse implements java.io.Serializable
    {
        public int Sum;
    }
    
    // Subtract and SubtractResponse excluded for brevity

Finally, we have the two methods that represent the operations in our IMath interface: Add and Subtract. Each method is decorated with its own set of javadoc annotations. @common:operation indicates that these are implementations of Web service operations. @jws:protocol disables access (in this case) to these operations using either HTTP POST or GET. The Add() method is shown here:

    /**
      * @common:operation
      * @jws:protocol form-post="false" form-get="false" 
      */
    public int Add (int X, int Y)
    {
    }

Now all that remains is to fill in the implementation of each Web method.

Implementing the Web Service

Now that we have the Web Service Control we can go back and fill in the implementation of our Web service:

    /**
      * @common:operation
      * @jws:protocol form-post="false" form-get="false" 
      */
    public int Add (int X, int Y)
    {
        // authorize caller before proceeding
        AuthorizeCaller(this.context);


        // process parameters and produce a result
        return MathImpl.Add(X, Y);
    }

    // Subtract() method excluded for brevity

Each method contains only two lines. The first authorizes the (already authenticated) caller. Since we haven't included security yet, we'll just stub in this method and come back to it later.

The second line delegates to the MathImpl class. MathImpl provides the actual implementation for the Web service. This follows the same structure used in the client where we separated the user interface code from the business logic. In this case the Web service itself is found in UsernameMath.cs and the underlying implementation is found in MathImpl.cs.

MathImpl

The MathImpl.cs source file contains the implementation of the Math Web service.

package WebLogic; 
public class MathImpl 
{ 
    private MathImpl() {}

    public static int Add(int x, int y)
    {
        // perform calculation and return result
        return x + y;
    }

    // Subtract() method excluded for brevity
}

The only thing to do here is to perform the requested arithmetic operation and return the result.

That's it for the WebLogic Web service program code. We should now be able to run the WebLogic client against the Web service and exercise the arithmetic operations.

Furthermore, either client (.NET or WebLogic) should run against either implementation of the service. The next thing to look at is how to add security.

Adding Username Token Support

Now that both sets of clients and servers are implemented, the next step is to add security. We'll begin by adding support for username tokens.

When the client chooses to invoke the Web service with a username token, they're indicating that they wish to add a username token to the request message. Nothing more than this is indicated. The message is not signed or encrypted with the token, it's simply passed along as a message header.

The Web service picks up the username token and uses it to authenticate the caller against an account database. For the .NET sample, this means authenticating against the local security authority (or domain). For WebLogic this means authenticating against the application server user database.

Passing the caller's credentials as a SOAP header means that the credentials are passed clear-text across the wire. But in a production scenario the transport would be relied upon for encrypting the connection. HTTPS is the obvious choice here.

Note that although the sample application defaults to HTTP for all communication, this is only done for simplicity's sake. The readme file accompanying the sample includes more information on setting up HTTPS.

To see how all of this is implemented, we'll look at each implementation of the Math sample, starting with the .NET version.

.NET Client

Modifying the .NET client to support username tokens means extracting the credentials from the user interface and including the code necessary to add the token to the request message. We've already seen the method that does this: AddSecurityToMessage. Now we'll look at the implementation. The portion of the AddSecurityToMessage method that handles username token support looks like this:

if (userNameToken)
{
    // the caller has chosen UsernameToken based security.  So we'll include a username 
    // token in the call
    UsernameToken usernameToken = new UsernameToken(userName, password, 
        PasswordOption.SendPlainText);
    context.Security.Tokens.Add(usernameToken);
}

The procedure is simple. We merely have to create a new instance of the UsernameToken instance (passing in the username and password of the caller). And then we add the instance to the context.Security.Tokens collection. This ensures that the username token is added as a header in our message.

.NET Web Service

On the server side, there's nothing we need to do to authenticate the caller. When the Web service call is received, WSE automatically authenticates the caller against the Windows user account database (for simplicity, the Math sample installation instructions calls for adding Alice as a user on the local machine).

Note that the implementation of the Math Web service (the Add and Subtract methods) are never called unless the caller is successfully authenticated.

This still leaves the issue of authorizing the caller.

Remember that our implementation of the Web service makes a call to AuthorizeCaller before it processes each operation. The implementation of that method (for the username version of the Math service) looks like this:

private void AuthorizeCaller(SoapContext context)
{
    // get the security token from the request message
    SecurityToken securityToken = GetSecurityToken(context);
    UsernameToken usernameToken = securityToken as UsernameToken;

    // get the name of the principal to which the token was issued
    string userName = usernameToken.Username;

    // make sure the user is who we think it is
    if (userName != "Alice")
    {
        string msg = string.Format("User '{0}' not authorized to use this service", 
            new Object[] {userName});
        throw new ApplicationException(msg);
    }
}

This code simply extracts the name from the username token and checks it against a hard-coded value (in a production application this check would be against a database).

In this case, we're expecting the caller to be Alice. If it isn't, we throw an exception. If it is, execution continues… where we perform the requested arithmetic operation and return the result to the caller.

Replay Detection

Earlier we looked at the configuration file for the .NET Web service. It included a <security> element that was left unexplained. In order to enable interoperability between WSE and WebLogic we need to configure this element as follows:

<security>
    <!-- X509 element excluded for brevity -->
         
    <!-- Replay detection must be disabled to accomodate WebLogic client -->
    <securityTokenManager qname="wsse:UsernameToken" xmlns:wsse="http://docs.oasis-
        open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <replayDetection enabled="false"/>
    </securityTokenManager>
</security>

Setting the enabled attribute of the <replayDetection> element to false means that WSE will not require replay detection. Requiring replay detection (which is the default) means that the WSE service expects additional information in the request message that the WebLogic client does not provide.

To ensure interoperability with WebLogic clients, replay detection must be disabled.

WebLogic Client

The WebLogic client has very similar code to its .NET counterpart. The WebLogic client also has an AddSecurityToMessage method, which is responsible for adding the username token to the request message. That code snippet looks like this:

if (userNameToken)
{
    // the caller has chosen UsernameToken based security.  So we'll include a 
    // username token in the call
    weblogic.xml.security.UserInfo ui = 
        new weblogic.xml.security.UserInfo(userName, password);
    context.getSession().setAttribute(WSSEClientHandler.REQUEST_USERINFO, ui);
    security.addToken(ui);       
}

The mechanics are different than in the .NET version, but the semantics are the same. We're just indicating that we want a username token added as a header to the request message.

WebLogic Web Service

As in the .NET version, there's nothing we need to do in the WebLogic service to authenticate the caller except to add our user (Alice) to the account database in the local application server. Once that's done, WebLogic automatically authenticates the caller against that account database. The Web service implementation is not called unless that authentication succeeds.

All that's left is to authorize the caller.

private void AuthorizeCaller(com.bea.control.JwsContext context) throws Exception
{
    // get the caller principal
    java.security.Principal principal = context.getCallerPrincipal();
    
    // get the name of the principal to which the token was issued
    String userName = principal.getName();
    
    // make sure the user is who we think it is
    if (userName.compareTo("Alice") != 0)
    {
        String msg = "User '" + userName + "' not authorized to use this service";
        throw new Exception(msg);
    }
}

Once again the mechanics are a bit different, but the logic is much the same as in the .NET implementation. In this case we retrieve the caller principal and compare the username against an expected list of users. In this case, if the caller isn't Alice then we throw an exception. Otherwise, execution continues and the requested arithmetic operation is performed.

That's all that's required for basic username token support. The next thing to look at is how to add support for X.509 certificate security.

Adding X.509 Token Support

If the user chooses to call the Math service using X.509 token credentials then they're indicating that they want to sign and encrypt the message sent to the service using X.509 certificates. As we saw in the overview of this paper, there are two certificates involved in this process:

  • Alice—This is the user's certificate that's installed with the client. Its private key is used to sign the message sent to the service. MathService verifies the signature in order to authenticate Alice.
  • MathService—This is the Web service's certificate. Alice has MathService's public key which she uses to encrypt the message sent to the service. MathService has sole access to its private key which it uses to decrypt the request message.

Also remember that we're calling a different Web service than we did before. When using the username token credentials we call the UsernameMath service. When using X.509 token credentials we call the X509Math service. Each has a different security policy and therefore a different service is used for each (although each service shares a common implementation through the MathImpl class that we've already seen).

In the following sections we'll walk through what's involved in modifying the .NET and WebLogic samples to support X.509 based security.

.NET Client

Actually signing and encrypting the request message is handled by the AddSecurityToMessage method. This method has a number of parameters. Those pertinent to X.509 security are as follows:

  • context—The SoapContext of the message (which allows us to manipulate the message headers).
  • sign—Flag indicating whether the message should be signed (normally it should be).
  • encrypt—Flag indicating whether the message should be encrypted (normally it should be).
  • signingCertificateName—The name of the certificate used to sign the message.
  • encryptingCertificateName—The name of the certificate used to encrypt the message.

To sign the message we need to add the X.509 token to the message header as well as the message signature.

if (sign)
{
    // retrieve the certificate we'll use to sign the message
    SecurityToken signingToken = 
        KeyUtil.GetSigningX509SecurityToken(signingCertificateName);

    // add a token to the context
    context.Security.Tokens.Add(signingToken);

    // specify that the SOAP message is signed using the X509 certificate
    MessageSignature signature = new MessageSignature(signingToken);
    context.Security.Elements.Add(signature);
}

if (encrypt)
{
    // retrieve the certificate we'll use to encrypt the message
    SecurityToken encryptingToken = 
        KeyUtil.GetEncryptingX509SecurityToken(encryptingCertificateName);

    // specify that the SOAP message is encrypted using the X509 certificate
    EncryptedData encryptedData = new EncryptedData(encryptingToken);
    context.Security.Elements.Add(encryptedData);
}

First we call the helper method KeyUtil.GetSigningX509SecurityToken(), which retrieves the certificate of the given name from the Microsoft certificate store.

Once the token is retrieved, adding it to the security header is a simple matter of adding it to the context.Security.Tokens collection. This collection contains the keys to be added to the SOAP message. In this case it contains the public key that the recipient will use to validate the signature.

Next we need to sign the message which is done by creating a new instance of MessageSignature. The signature is inserted into the message header by adding it to the context.Security.Elements collection.

Encrypting is even easier. Once we retrieve the security token to encrypt the message we create a new instance of EncryptedData. We complete the process by adding the encryptedData instance to the context.Security.Elements collection. This adds the appropriate security headers to the message and encrypts the body of the message itself.

After we sign or encrypt the message we set a time-to-live of 60 seconds on the message (to help prevent replay attacks). And we're done.

Certificate Stores

Before moving on, let's take a look at our certificate helper methods. These are contained in the KeyUtil class found in the client. The code in the snippet above calls two helper methods: GetSigningX509SecurityToken and GetEncryptingX509SecurityToken. Both of these methods call the same code to actually retrieve the certificate from the store: GetCertificateFromStore.

private static X509Certificate GetCertificateFromStore(string x509CertificateName)
{
    X509CertificateStore store = 
        X509CertificateStore.CurrentUserStore(X509CertificateStore.MyStore);
    store.OpenRead();
    X509CertificateCollection certificates = 
        store.FindCertificateBySubjectString(x509CertificateName);
    X509Certificate certificate = certificates[0];
    return certificate;
}

This method opens the current user store, retrieves the certificate with the given name, wraps it in an X509Certificate instance and returns it to the caller.

The point to notice here is where we're getting the certificate.

Instead of storing certificates in keystores as is common in Java, certificates in the Microsoft platform are commonly stored in the Microsoft certificate store. For our purposes, there are two certificate stores to be concerned with: the user store and the local machine store.

Each user on a Windows machine has her own certificate store. The store for the currently logged on user is the 'CurrentUser' store, thus that's the one we're accessing here.

Click here for larger image.

Figure 9. Current User Personal Certificate Store

Specifying the X509CertificateStore.MyStore enumeration corresponds to the 'Personal' store in the Microsoft Management Console.

For a client application such as this, this is the natural place to store certificates. Note that no credentials are provided to access the certificate store (it's simply opened and the specified certificate is retrieved). Instead, Windows Integrated security is used to access the store (the logged on user has access to her own certificate store). Recall from the overview that Alice is the user running the client. Presumably Alice is logged onto her workstation so when she runs her client (which is running under her credentials), she's allowed access to the certificates in her user store.

For our sample, Alice needs to have two certificates in her Personal store:

  • Alice—This is Alice's own certificate which she'll use to sign outgoing messages. This certificate contains a public key as well as a private key (which only Alice possesses).
  • MathService—This certificate is used to encrypt any outgoing message intended for the Math Web service. Alice has the public key. The Math Web service is in sole possession of the private key and uses it to decrypt any incoming messages.

The current user certificate store serves as a centralized repository for all certificates that the logged on user needs (in this case, Alice). When we look at the .NET Web service we'll see how the Local Machine certificate store serves a similar purpose for back-end services.

.NET Web Service

On the server side, there's no code needed to authenticate the caller or decrypt the incoming message. When the Web service call is received, WSE automatically validates the signed message (thus authenticating the caller), and decrypts the incoming message. The implementation of the Math Web service (the Add and Subtract methods) are never called unless the caller is successfully authenticated.

But we still need to authorize the caller. This is done by the AuthorizeCaller method found in the X.509 version of the Math Web service.

private void AuthorizeCaller(SoapContext context)
{
    // get the security token from the request message
    SecurityToken securityToken = GetSecurityToken(context);

    // get the name of the principal to which the token was issued
    X509SecurityToken x509SecurityToken = securityToken as X509SecurityToken;
    string certificateName = x509SecurityToken.Certificate.GetName();

    // make sure the user is who we think it is
    if (certificateName != "DC=com, DC=cert, CN=Users, CN=Alice")
    {
        string msg = string.Format("User '{0}' not authorized to use this service", 
            new Object[] {certificateName});
        throw new ApplicationException(msg);
    }
}

This code simply extracts the name from the X.509 token and checks it against a hard-coded value (in a production application this check would be against a database).

In this case, we're expecting the caller to be Alice. If it isn't, we throw an exception. If it is, execution continues… where we perform the requested arithmetic operation and return the result to the caller.

Note that unlike in the client we never write a single line of code that handles signatures or encryption. Instead, all of these details are handled declaratively in a policy file.

.NET Web Service Security Policy

The security for the .NET Web services in this sample are described in a policy file. These policy files follow WS-Policy12 and WS-SecurityPolicy13 as well as some extensions particular to WSE.

The policy file for the Math service is found at ...\dotNet\Service\Math.Net.Service.Policy.xml. As mentioned earlier, it's useful to initially generate this using the WSE 2.0 Configuration Editor (and then modify it as necessary). Essentially, this file defines the following policy:

  • Incoming messages to http://localhost/Math.NET/X509Math.asmx must be signed and optionally encrypted14.
    • The signature must conform to the following constraints:

      The message must be signed with an X.509v3 security token.

      The token issuer must be DC=com, DC=cert, CN=certserver.

      The message body must be signed.

      The security token used to sign the message is the IdentityToken.

    • If the message is encrypted, it must conform to the following constraints:

      The message must be encrypted with an X.509v3 security token.

      The token issuer must be DC=com, DC=cert, CN=certserver.

      The subject name of the token must be DC=com, DC=cert, CN=Users, CN=MathService.

      The subject key identifier of the token must be 0gaxHldpwKqgMvlcRlo0jHYQtVg=

      The message body must be encrypted.

To sum this up, incoming messages to the X.509 version of the Math service must be signed. Incoming messages must also be encrypted with MathService's private key.

We'll start examining the policy by looking at the mappings section of the policy file.

<policyDocument xmlns="http://schemas.microsoft.com/wse/2003/06/Policy">
  <mappings xmlns:wse="http://schemas.microsoft.com/wse/2003/06/Policy">
  
    <endpoint uri="http://localhost/Math.NET/X509Math.asmx">
      <defaultOperation>
        <request policy="#SignEncrypt-X.509" />
        <response policy="" />
        <fault policy="" />
      </defaultOperation>
    </endpoint>    
</mappings>

This section contains an endpoint element for each Web service for which we want to specify policy.

We're stating that there is a policy called SignEncrypt-X.509 for request messages (messages sent from the client to the Math service). There is no security policy for response messages or SOAP faults.

SignEncrypt-X.509 policy

The SignEncrypt-X.509 policy describes messages that must be signed and optionally encrypted.

<wsp:Policy wsu:Id="SignEncrypt-X.509">
    <wsp:MessagePredicate wsp:Usage="wsp:Required" 
        Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
            wsp:Body()
    </wsp:MessagePredicate>
    <wsp:MessagePredicate wsp:Usage="wsp:Optional" 
        Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
            wsp:Header(wsa:To) wsp:Header(wsa:Action) wsp:Header(wsa:MessageID) 
            wse:Timestamp()
    </wsp:MessagePredicate>
    <wssp:Integrity wsp:Usage="wsp:Required">
        <wssp:TokenInfo>
            <wssp:SecurityToken wse:IdentityToken="true">
                <wssp:TokenType>http://docs.oasis-open.org/wss/2004/01/
                    oasis-200401-wss-x509-token-profile-1.0#X509v3</wssp:TokenType>
                <wssp:TokenIssuer>DC=com, DC=cert, CN=certserver</wssp:TokenIssuer>
            </wssp:SecurityToken>
        </wssp:TokenInfo>
        <wssp:MessageParts Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
            wsp:Body()
        </wssp:MessageParts>
    </wssp:Integrity>
    <wssp:Confidentiality wsp:Usage="wsp:Optional">
        <wssp:KeyInfo>
            <wssp:SecurityToken>
                <wssp:TokenType>http://docs.oasis-open.org/wss/2004/01/
                    oasis-200401-wss-x509-token-profile-1.0#X509v3</wssp:TokenType>
                <wssp:TokenIssuer>DC=com, DC=cert, CN=certserver</wssp:TokenIssuer>
                <wssp:Claims>
                    <wssp:SubjectName MatchType="wssp:Exact">DC=com, DC=cert, 
                        CN=Users, CN=MathService</wssp:SubjectName>
                    <wssp:X509Extension OID="2.5.29.14" 
                     MatchType="wssp:Exact">
                        0gaxHldpwKqgMvlcRlo0jHYQtVg=
                    </wssp:X509Extension>
                </wssp:Claims>
            </wssp:SecurityToken>
        </wssp:KeyInfo>
        <wssp:MessageParts Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
            wsp:Body()
        </wssp:MessageParts>
    </wssp:Confidentiality>
</wsp:Policy>

The policy starts with a pair of MessagePredicate elements. The first MessagePredicate element indicates that the message body is required. It may seem odd that we're requiring a message body but later on we'll specify that the message body should be signed. So it's good form here to make sure it's in the message.

As for the second MessagePredicate, it specifies that the WS-Addressing headers are optional. WebLogic doesn't include WS-Addressing headers so these headers must be marked optional. Actually, this MessagePredicate could be eliminated all together (optional is the default). They're included here to demonstrate how the MessagePredicate element works. If this Web service only worked with WS-Addressing compliant stacks, these headers would normally be marked required.

Signature

Next is the Integrity element. This specifies the requirements for signing the message (which in this case is required).

Note the SecurityToken element which is marked with the attribute IdentityToken="true". Exactly one of the tokens in the SOAP message may be marked as the IdentityToken. It allows us to refer to the token elsewhere in the policy file. For example, this may be done to specify the IdentityToken as the one we want to use to encrypt the response message.

The TokenType element specifies that we require an X.509v3 security token to sign the message. And the TokenIssuer element indicates who the issuer of the token must be.

We could specify the exact token that should be used to sign the message (through a Claims element which we'll see later). However, the message will typically be signed using many different tokens (aside from Alice's), so we don't want to restrict the signing token to a single one. However, requiring a specific TokenIssuer does restrict the tokens used to sign the message to a specific trust domain.

Finally the MessageParts element indicates which part of the message this policy should be applied to. In this case we specify the body of the message (which must be signed).

Encryption

Next is the Confidentiality element. Here we specify that encryption is optional.

The SecurityToken element is the same one we saw before. We specify the TokenType as well as the TokenIssuer. However, the Claims element is new.

In this case we specify exactly which certificate must be used to encrypt the message. In this case it's the MathService certificate15. The certificate's SubjectName and subject key identifier are provided to specify the exact certificate16.

Finally the MessageParts element specifies which part of the message should be encrypted: In this case, the message body.

WSE Policy Check Failure

When we looked at the security policy file for the .NET Math service, we saw that incoming messages are not required to be encrypted (confidentiality is marked optional even though we would like it to be required). This is because of an interoperability problem between WebLogic and .NET.

When WebLogic encrypts the body of a message (as when the client encrypts the message sent to the Math service), it does so in a way that violates the WSE security policy.

For comparison let's look at a message generated by the .NET client.

<soap:Envelope>
    <soap:Header/>
    <soap:Body wsu:Id="Id-eeb3cd51-0754-4237-be0a-78fdb3b03665">
      <xenc:EncryptedData Id="EncryptedContent-2a794ca0-d231-4bef-acc1-8b0c646dc9ac" 
          Type="http://www.w3.org/2001/04/xmlenc#Content" 
          xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
        <xenc:EncryptionMethod 
            Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
        <xenc:CipherData>     
            <xenc:CipherValue>nvtHxOuqIbQ9FHN5AA4ghBx2hAGTT/dNcgu1zVBIa4qmYYupfiVx
            Myyscnz8axka+YtEiqtPx9U2EhWmLNoYU/k4ECmxVUFPb3NgoND8ld6PnmZ9/
            8eGMn3+IZXWIVBuC4oPgVMcDUOb9wYwq7BI7Q==</xenc:CipherValue>
        </xenc:CipherData>
      </xenc:EncryptedData>
    </soap:Body>
</soap:Envelope>

If you look at the EncryptedData element of the message body, you'll see that the Type is #Content. According to the XML Encryption specification17, this means that the EncryptedData element represents the encrypted contents of the element containing it (that is, the body element). In other words, the EncryptedData element contains the encrypted contents of the body. Semantically, this is exactly what we want—the message body to be encrypted.

In contrast, let's look at the message generated by the WebLogic client.

<env:Envelope>
    <env:Header/>
    <env:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/
         oasis-200401-wss-wssecurity-utility-1.0.xsd" 
         wsu:Id="Id-n3U21cHBalFBCPkqZs_JBRIZ">
      <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" 
          Id="Id-v4o1Xb03zmpnieuMbj2iW_yQ" 
          Type="http://www.w3.org/2001/04/xmlenc#Element">
        <xenc:EncryptionMethod 
            Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
        <xenc:CipherData>
          <xenc:CipherValue>WBEBdZq3Yifp7iXGKL7wLJI0oOa6/Sa6Di7+EhmTp
          VjKATdP4y1OltIQybwKDlEYX4LXeXZhnIASWcwGAFn7jmVEHVvfUNlAe
          BBbRbXerRLTFOyCiX2RHV2VvRcayPEUpjdb4sPBxsLRoRiS9DDhWa
          5c8wJ1fIcyrqwkO4QvHg9895adJcVAts9AOyTkLPol30X0KsXxHesP6y
          4g+4QirzBhz+50G4PLijaDwG4RYTClXWA96hailyuOvqeiSNFzALBEV
          W6R7GqtDEY6oobnWZ1kqeO2yoou5/JTycItig4xZz8dOBUbrg==</xenc:CipherValue>
        </xenc:CipherData>
      </xenc:EncryptedData>
    </env:Body>
</env:Envelope>

The EncryptedData element is a bit different. As you can see, the Type is #Element. According to the XML Encryption specification, this means that the EncryptedData element represents a direct replacement of the element it encrypts. We know what the original message body looks like:

<soap:Body>
    <Add xmlns="urn:schemas-microsoft-com:mathwebservice:types">
        <X>7</X>
        <Y>3</Y>
    </Add>
</soap:Body>

So the EncryptedData element in the WebLogic message is the encrypted version of the Add element.

So in both cases, the Add element is being encrypted. The important difference is in the EncryptedData Type.

When the .NET message uses the #Content EncryptedData Type, it's literally saying "The encrypted data represents the contents of the body." When the WebLogic message uses the #Element EncryptedData Type, it's saying "the encrypted data represents the first child element of the body."

Encrypting the contents of the Body element is semantically correct. Encrypting the first child element of the Body element is not (there could be more than one child of the Body element).

When a confidentiality policy is marked required, the WSE policy checker looks for (and requires) the Body element to contain an EncryptedData element with Type #Content. Since WebLogic doesn't encrypt its message this way, the policy check fails.

So the problem can be summarized like this:

The security policy for the Math service specifies that the Body of the message must be encrypted. This is designated in the policy file by the wsp:Body() message part selection function (used with the http://schemas.xmlsoap.org/2002/12/wsse#part dialect). Appendix II of the WS-PolicyAssertions specification indicates that wsp:Body identifies the "Body" of the message. Strictly speaking, what WebLogic is doing is encrypting the first child of the Body element (not the contents of the entire Body). In this particular case this is effectively the same as the .NET generated message. However, in the general case they are not equivalent (consider the case where the Body has multiple children). Since the WSE policy checker is looking for the contents of the entire body to be encrypted (as called for by WS-PolicyAssertions), the policy check fails.

Notice that I didn't call this issue a bug. Instead I called this a compatibility problem. After all, the issue is that WebLogic is generating a message that does not conform to WS-PolicyAssertions. However, WebLogic never claims to conform to that specification.

At best WebLogic is encrypting its messages in a way that's semantically incorrect. Nonetheless, those tasked with making these platforms interoperate have to deal with this issue.

Aside from the workaround offered here (making encryption optional), a fix is available from BEA support services (tracking ID: CR206836).

Web Service Configuration

The configuration file for the Math service controls various parameters of the Web service operation.

<configuration>
    <!-- configSections element excluded for brevity -->

    <microsoft.web.services2>
        <!-- This is how the policy file is bound to this web service -->
        <policy>
            <cache name="Math.Net.Service.Policy.xml" />
        </policy>
      
        <security>
          <!-- Retrieve certs from the LocalMachine store.  Don't allow test roots and
               always verify the trust chain.  -->
          <x509 storeLocation="LocalMachine" allowTestRoot="false" 
                verifyTrust="true" />

          <!-- securityTokenManager element excluded for brevity.  -->
        </security>
      
        <!-- diagnostics element excluded for brevity -->
    </microsoft.web.services2>

    <!-- system.web excluded for brevity -->
</configuration>

For our purposes, the most important part of the configuration file is the policy element. This specifies the name of the security policy file that describes the security requirements of this Web service. This configuration element is how the policy file is bound to the Web service.

The security element is also important. We specify that we want to retrieve our certificate from the LocalMachine store (which is typical for back-end Web services).

WebLogic Client

Actually signing and encrypting the request message is handled by the AddSecurityToMessage method. This method has a number of parameters. Those pertinent to X.509 security are as follows:

  • context—The WebServiceContext of the message (which allows us to manipulate the message headers).
  • sign– Flag indicating whether the message should be signed (normally it should be).
  • encrypt—Flag indicating whether the message should be encrypted (normally it should be).
  • keyStorePath—The path of the keystore containing the keys that we'll need.
  • keyStorePassword—The password to the Keystore.
  • signingCertificateName—The name of the certificate used to sign the message.
  • signingCertificatePassword—The password of the private key used to sign the message.
  • encryptingCertificateName—The name of the certificate used to encrypt the message.

To sign the message we need to add the X.509 token to the message header as well as the message signature.

if (sign)
{
    // retrieve the certificate we'll use to sign the message
    X509Certificate signingCertificate = 
        KeyUtil.getCertificate(signingCertificateName, keyStore);
    PrivateKey signingKey = KeyUtil.getPrivateKey(signingCertificateName, 
        signingCertificatePassword, keyStore);      
             
    // specify that the SOAP message is signed using the X509 certificate
    SecurityElementFactory factory = SecurityElementFactory.getDefaultFactory();
    Token x509Token = factory.createToken(signingCertificate, signingKey);
    SignatureSpec signatureSpec = SignatureSpec.getDefaultSpec();
    security = factory.createSecurity(null);
    security.addSignature(x509Token, signatureSpec);     
            
    // add a token to the context          
    security.addToken(x509Token);
}

if (encrypt)
{
    // retrieve the certificate we'll use to encrypt the message
    X509Certificate encryptingCertificate = 
        KeyUtil.getCertificate(encryptingCertificateName, keyStore);
    
    // specify that the SOAP message is encrypted using the X509 certificate
    SecurityElementFactory factory = SecurityElementFactory.getDefaultFactory();
    Token x509Token = factory.createToken(encryptingCertificate, null);
    EncryptionSpec encryptionSpec = EncryptionSpec.getDefaultSpec();
    if (security == null)
        security = factory.createSecurity(null);
    security.addEncryption(x509Token, encryptionSpec);
}

First we call the helper method KeyUtil.getCertificate(), which retrieves the certificate of the given name from the given Keystore (we'll cover more on the KeyUtil class later). We also need to retrieve the private key that we'll use to sign the message. This is done with KeyUtil.getPrivateKey().

Once the certificate and private key is retrieved, we need to create the signature and add it to the message. We do this by creating a new Token instance based on the signing certificate and key. Then we create a new SignatureSpec instance and add it to the security instance we created by calling SecurityElementFactory.getDefaultFactory().createSecurity(null).

Next we need to add the X.509 token to the message by calling security.addToken(). This is the token that the receiver will use to verify the signature.

Encrypting is a bit simpler. Once we retrieve the certificate used to encrypt the message we create a new Token instance from it. Then we create a new instance of EncryptionSpec. We complete the process by adding the encryptionSpec and Token instance to the security object by calling security.addEncryption().

After we setup the signature and encrypted text we associate it with the message by calling: context.getSession().setAttribute(WSSEClientHandler.REQUEST_SECURITY, security). This actually signs and encrypts the message.

Keystores

Before moving on, let's take a look at the KeyUtil class

package Math.WebLogic.Client; 

public class KeyUtil 
{
    public static KeyStore loadKeystore(String filename, String password)
    {
        final KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(new FileInputStream(filename), password.toCharArray());
        return ks;
    }
  
    public static PrivateKey getPrivateKey(String alias, String password, 
        KeyStore keystore)
    {
        PrivateKey result =
            (PrivateKey) keystore.getKey(alias, password.toCharArray());
        return result;
    }
  
    public static X509Certificate getCertificate(String alias, KeyStore keystore)
    {
        X509Certificate result = (X509Certificate) keystore.getCertificate(alias);
        return result;
    }  
}

This class has three helper methods. The first opens a keystore with the given password. This gives us a KeyStore instance that we can use to reference through the rest of the application.

The getPrivateKey method retrieves the private key of the given alias to the caller. This is how we retrieve Alice's private key that we use to sign the request message.

Finally, the getCertificate method is used to retrieve X509Certificates from the certificate store. This is how we retrieve Alice's certificate (which we include in the request message) and MathService's certificate (containing the public key used to encrypt the message).

WebLogic Web Service

On the server side, there's no code needed to validate the caller's signature or decrypt the incoming message. When the Web service call is received, WebLogic automatically validates and decrypts the incoming message. The implementation of the Math Web service (the Add and Subtract methods) are never called unless the incoming message is successfully validated and decrypted.

But we still need to authorize the caller. As we've seen before, we would normally do this in the AuthorizeCaller method found in the X.509 version of the WebLogic Web service. But this is where we run into a problem.

private void AuthorizeCaller(com.bea.control.JwsContext context)
{
    // get the caller principal
    java.security.Principal principal = context.getCallerPrincipal();
        
    // get the name of the principal to which the token was issued
    String userName = principal.getName();
    
    // !!!NOTE!!!
    // Even though the request message is signed with an X509 certificate (and WebLogic
    // validates the signature by the time the web method is called), we still can't get
    // access to the name of the caller principal (as we can when a username token is 
    // used).
        
    // When using certificates, principal.getName() always returns "<anonymous>".
    // So we can't include appropriate authorization logic.
}

As the comments indicate, when the request message is signed with an X.509 certificate the name of the caller's principal is always set to <anonymous>. WebLogic doesn't expose the caller principal in any way. Based on discussions with BEA support, there is no way in WebLogic 8.1 to retrieve the caller principal when the request message is signed with X.509 certificates. The only way to get the caller principal is to have the sender include a username token in the request message.

The consequence of this is that there's no way to authenticate the caller (WebLogic verifies the signature of the request message but doesn't do any user account mapping). There's also no way to authorize the caller.

The best work around is to have the sender include a username token (at least until WebLogic is updated to expose the caller's identity when X.509 certificates are used exclusively).

Nonetheless, we still need to indicate to WebLogic how to perform the decryption and signature validation required for the service. These requirements are described in a security policy file.

Security Policy

The security for the WebLogic Workshop Web services in this sample are described in a policy file. These policy files follow a proprietary format particular to WebLogic Workshop18.

There is one policy file for the X.509 version of the Math service:

X509Math.wsse—describes the security policy for messages sent to the X.509 version of the Math service (from the client to the Math service).

The contents of X509Math.wsse is included below.

<wsSecurityPolicy xsi:schemaLocation="WSSecurity-policy.xsd"
    xmlns="http://www.bea.com/2003/03/wsse/config"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        
    <wsSecurityIn>
        <encryptionRequired>
            <decryptionKey>
                <alias>MathService</alias>
                <password>password</password>
            </decryptionKey>
        </encryptionRequired>
        <signatureRequired>true</signatureRequired>
    </wsSecurityIn>
    
    <keyStore>
        <keyStoreLocation>c:/sourceroot/Math/Keys/mathservice.jks</keyStoreLocation>
        <keyStorePassword>password</keyStorePassword>
    </keyStore>
</wsSecurityPolicy>

This policy file essentially says the following:

  • Incoming messages must be signed and encrypted.
    • The decryption key for incoming messages is MathService and the password for the corresponding private key is 'password'.
  • The keystore holding the keys needed for this policy is located at c:\sourceroot\Math\Keys\mathservice.jks. The Keystore password is 'password'.

As with the .NET implementation, we can sum up these policies as follows: incoming messages to the Math service must be signed (presumably with Alice's private key) and encrypted with MathService's public key.

Note that these policy files follow a different format than their .NET counterparts. The tradeoff is that there's less that we can express using this policy file format:

  • We can't specify the type of token that should be used to sign or encrypt the message (X.509v3 certificate, for example)
  • We can't specify the token issuer (DC=com, DC=cert, CN=certserver, for example)
  • We can't specify the subject name (DC=com, DC=cert, CN=Users, CN=MathService, for example) or the subject key identifier (0gaxHldpwKqgMvlcRlo0jHYQtVg=). We can only specify the alias which may not be unique.
  • We can't specify which headers we require and which are optional.

So although the .NET policy files are more complex, they're also more expressive.

Binding the Security Policy Files

Now that it's been explained how to write the security policy files, the last thing is to bind them to the Web service and that we built. For this Web service, this can be accomplished by setting the ws-security-service file property on the Math Web service.

Click here for larger image.

Figure 10. Setting the ws-security-service Property

We simply set this property to: /WEB-INF/X509Math.wsse.

Behind the scenes, this decorates the Web service class with a @jws:ws-security-service annotation.

/**
 * @jws:wsdl file="#MathWsdl"
 * @jws:ws-security-service file="/WEB-INF/X509Math.wsse"
 */
public class Math implements com.bea.jws.WebService
{
    . . .
} 

Response Encryption Failure

Originally, part of the Math sample included encrypting the response from the Math service to the client (and not just the request). After all, if the request is sensitive enough to encrypt, often the response is as well. This feature was removed due to an interoperability issue.

The problem is that WebLogic encrypts the response in such a way that the WSE client can't properly decrypt it (and so it throws an exception). The problem is the reference to the key in the header. The WSE generated reference looks like this:

<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
    <wsse:SecurityTokenReference>
        <wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/2004/01/
            oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier">
            0Ial0lGGyDpDH9RBCa7P0KLEwyQ=
        </wsse:KeyIdentifier>
    </wsse:SecurityTokenReference>
</KeyInfo>

The WebLogic generated reference looks like this:

<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
    <dsig:KeyName>
        CN = Alice, CN=Users, DC=cert, DC=com
    </wsse:KeyName>
</KeyInfo>

In other words, WSE's reference is an X.509 subject key identifier. The WebLogic reference is a KeyName.

According to section 3.2 of the X.509 Token profile19:

In order to ensure a consistent processing model across all the token types supported by WSS: SOAP Message Security, the <wsse:SecurityTokenReference> element SHALL be used to specify all references to X.509 token types in signature or encryption elements that comply with this profile.

This constitutes a violation of the specification. In fact, what's strange about this problem is that for some reason WebLogic Workshop correctly encrypts request messages (using wsse:SecurityTokenReference), but not response messages.

With this issue such as it is, it's impossible for a WebLogic Web service to encrypt a response message and have it processed by a WSE client. The Microsoft WSE team is working with the WebLogic development team on the problem.

The chosen work-around in the Math sample is simply not to encrypt the response message. If you're interested, all of the code required to encrypt the response is included in the Math sample (it's just commented out).

Other Interop Issues

There are some challenges to security interop between WSE 2.0 and WebLogic 8.1 that have been covered over the course of this paper. But there are a few other known issues that weren't encountered in the Math sample, but might be found in your application. For completeness, these issues are listed here:

Signing SOAP Body that contains not visibly utilized prefixes—You may incur this problem if your message sent from a WebLogic client contains an xsi:type attribute or a soap-enc:arrayType attribute (for example, if you're using rpc/encoded WSDL binding style and working with arrays). WebLogic 8.1 incorrectly implements the XML Canonicalization algorithm in this particular case and as a result produces incorrect signature value. The fix for this issue is available from BEA support services (tracking ID CR189884).

Encrypting the message using X.509 without SKI extension—There is currently no interoperable mechanism for external references to X.509 certificates that do not have SubjectKeyIdentifier (SKI) extensions. It is strongly recommended to use certificates with SKI extensions. As a result WSE and WebLogic use different mechanisms to reference X.509 certificates that do not have SKI extensions. OASIS Web Services Security TC is expected to resolve this issue in the future. You can easily check if your certificate has an SKI extension by looking for it on the "Details" tab in the certificate browser. In particular, certificates produced using makecert.exe tool do not have a SKI extension.

Summary

The basic goal of this paper is to demonstrate the state-of-the-art in Web services security interoperability between WebLogic Workshop 8.1.4 and WSE 2.0 SP3 using username tokens and X.509 certificates.

For the most part, this is a success. Thanks a great deal to compliance to the OASIS Web Services Security 1.0 standard, basic security interoperability works. Messages can be signed and encrypted using one stack and successfully validated and decrypted by the other.

However, there are a few obstacles:

  • WebLogic encrypts response messages in such a way that they can't be properly processed by WSE clients.
  • WebLogic encrypts request messages in such a way that it prevents WSE from enforcing confidentiality policy checks (although a work-around is possible using programmatic security).

In part, these problems reflect the maturity of the standards and specifications that made the achieved interoperability possible. But it also reflects the maturity of the Web service stacks. The level of interoperability explained here, wouldn't have been possible even months ago. Even the current service pack of WebLogic isn't compatible with previous service packs of the same version20. The same is true with .NET where Microsoft does not guarantee backward compatibility between current and previous versions of WSE. That reflects how quickly these standards are changing but also how aggressively these vendors are working to keep up.

Over time, these bugs will be fixed and missing features will be added. When that happens it should become possible to fully implement the scenario outlined in this document.

Acknowledgements

Some of the analysis presented in this article could not be accomplished without productive collaboration between the development teams of Microsoft WSE and BEA WebLogic on interoperability issues analysis. The WSE team would like to thank Peter Dapkus from BEA for support and collaboration on interoperability issues investigation.

References

WSE 2.0 SP3 Documentation

WebLogic 8.1 Documentation

WS-Security

WS-SecurityPolicy

WS-PolicyAssertions

The JFC Swing Tutorial, 2nd Edition by Kathy Walrath, Mary Campione, Alison Huml, Sharon Zakhour (Addison-Wesley 2004)

Securing Web Services with WS-Security: Demystifying WS-Security, WS-Policy, SAML, XML Signature, and XML Encryption by Jothy Rosenberg and David Remy (SAMS 2004)

Java Cryptography Extension: Practical Guide for Programmers by Jason Weiss (Morgan Kaufmann Publishers)

J2EE Security For Servlets, EJBs and Web Services by Pankaj Kumar (Prentice Hall PTR 2004)

WS-Security Drilldown in Web Services Enhancements 2.0 by Don Smith. August 2004.

 

About the author

Michel Barnett is an Architect with Microsoft Consulting Services, where he assists a variety of customers develop solutions using the Microsoft platform. Michel has been focusing on security issues related to distributed applications—especially .NET Remoting and Web services. Recently, this includes Web services security interoperability between WSE and WebLogic.

Footnotes

  1. http://www.ws-i.org/Profiles/BasicProfile-1.0-2004-04-16.html
  2. http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0.pdf
  3. http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wss
  4. http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0.pdf
  5. http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf
  6. http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0.pdf
  7. http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd and http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd
  8. WebLogic allows policy to be associated with WebLogic clients but only if those clients are Web service controls (running within the WebLogic server). Since our Swing client runs outside the application server, policy association isn’t an option.
  9. Note that the URL here refers to a HTTP and not a HTTPS connection even though the scenario being demonstrated is username tokens over SSL. For simplicity the sample uses HTTP by default. But the readme included with the sample includes pointers for setting up SSL if you want to fully implement the scenario.
  10. Since we override the endpoint address it doesn’t matter from which WSDL definition we generate the proxy (UsernameMath.WSDL or X509Math.WSDL).
  11. http://www.jcp.org/jsr/detail/181.jsp
  12. http://msdn.microsoft.com/webservices/default.aspx?pull=/library/en-us/dnglobspec/html/ws-policy1202.asp
  13. http://msdn.microsoft.com/webservices/default.aspx?pull=/library/en-us/dnglobspec/html/ws-securitypolicy.asp
  14. Our plan calls for encryption to be required but is optional in this case due to an interoperability issue with WebLogic (described later).
  15. The credentials under which the Web service runs (typically the ASP.NET worker process) also needs to have access to the private key of the server’s certificate. This security configuration is explained in the readme file of the associated sample.
  16. The subject key identifier was retrieved by viewing the certificate in the WSE X509 Certificate Tool (the Base 64 Encoded value was cut and pasted into the policy file).
  17. http://www.w3.org/TR/2002/CR-xmlenc-core-20020304/
  18. http://e-docs.bea.com/workshop/docs81/doc/en/workshop/reference/configfiles/con_wsse_policyFile.html?skipReload=true
  19. http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0.pdf
  20. http://e-docs.bea.com/wls/docs81/notes/new.html
Show:
© 2014 Microsoft