ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


WS-Security in the Enterprise, Part 2: The Framework

by Denis Piliptchouk
03/30/2005

This is the second article in this series about implementing and using WS-Security-based protection in enterprise environments. The first article reviewed existing setups and gaps, and proposed plans for development of a WSSE Toolkit, which is intended to address some of those gaps. This article introduces the framework of the proposed WSSE Toolkit (a subset of current project's source is also available for download), and explains a high-level mapping of WSSE features to the object-oriented realms of the Java language.

Client View

As stated in the Toolkit's requirements, one of the intentions for this project was the simplification of dealing with WS-Security in today's programming environments. This burden should be moved to the framework, with the intent of eventually delegating it to the infrastructure layer, where it actually belongs. Therefore, before reviewing the Toolkit's structure and its relationship to the WSSE standards, I would like to take a look from the consumer's side; i.e., view the Toolkit as a black box for now.

The code example below shows how a client would access the Toolkit:


	WsseHeaderToken wsseHeader = new WsseHeaderToken();
	
	// Add Timestamp element to the WSSE Header
	TsToken ts = wsseHeader.AddTimestamp(60);
	
	// Sign the timestamp element with default certificate
	WsToken[] sigTokens = new WsToken[] {ts};
	wsseHeader.AddSignature(sigTokens);
	
	// Encrypt the signature and body elements with default key
	WsTokenRef[] encTokens = new WsTokenRef[] 
		{ new DSigTokenRef(), new SoapBodyRef() };
	wsseHeader.AddEncryption(encTokens);
	
	wsseHeader.ProcessHeader();
	
	Element soap = wsseHeader.GetSoapEnvelope();

This simple example creates a new WSSE Header with a child timestamp element, and signs the timestamp using the XML Signature provider implementation, retrieved from the Toolkit's configuration file. The signature and timestamp elements in the security header are then encrypted by the configured XML Encryption provider.

The difficulty of processing WSSE information lies in the procedural nature of XML parsing, leading to stacks of unwieldy spaghetti code. The code, which implements complicated security specifications, tries to map its procedural rules onto object-oriented realms. The Toolkit's objects, representing particular WSSE elements (see the WSSE Mapping section), will isolate this tedious parsing process, creating an object-oriented wrapper on top of XML-handling code.

The WsseHeaderToken class, provided by the Toolkit, implements several convenience wrappers to isolate complexities of the framework from its clients. However, if necessary, the Toolkit objects may be created and accessed directly from the code--for instance, when a new type of processing, not covered by the existing helpers, is desired, a corresponding processor object may be created and inserted into the header. This is demonstrated in the example below, which copies elements from one WSSE header to another using a sample processor and token.


	// Reference and read WSSE header with null actor
	WsseHeaderRef ref = WsseHeaderRef.CreateFromFile(filename, null);
	WsseHeaderToken wsseHeader = ref.GetWsseHeader();
	
	// Reference a sample element in the retrieved header
	SampleTokenRef sampleRef = new SampleTokenRef(wsseHeader,"sample1");
	SampleToken sample = sampleRef.GetSampleToken();
	
	// Create a new WSSE header and add the element
	WsseHeaderToken wsseNew = new WsseHeaderToken();
	wsseNew.InsertToken(sample);
	
	// Add sample element's processing in the new WSSE header
	SampleProcessor sampleProcessor = new SampleProcessor();
	sampleProcessor.SetReplaceTokens(true);
	sampleProcessor.AddToken(sample);
	wsseNew.AddProcessor(sampleProcessor);
	
	wsseNew.ProcessHeader();
	
	Element soap = wsseNew.GetSoapEnvelope();

The Framework

Related Reading

Java vs. .NET Security
By Denis Piliptchouk

The framework's hierarchy maps, to a large extent, to the XML blocks described in the WSSE specification (PDF). The Toolkit contains the following types of objects:

It is worth mentioning here that not all of these objects are going to be available right away--rather, they will be introduced and added to the Toolkit according to the phases, laid out in the Conclusion section of part one. Implementation of those elements of WSSE Headers will be covered in later articles of this series.

Figure 1 below graphically shows the relationships between the different types of the Toolkit framework's objects and its clients.

Figure 1
Figure 1. Toolkit's operation

There are several important points about the Toolkit's operation that can be added for better understanding of Figure 1:

Since the Toolkit provides a rather lightweight object layer, a new set of objects, with its own context, is instantiated for processing each client, and, therefore, concurrency is not an issue. As a result, the Toolkit's classes generally do not provide synchronization locks, with the exception of the wsse.Toolkit.Utils.Xml.XmlFactory and wsse.Toolkit.Utils.Config.ConfigHelper classes, which do contain some shared functionality. However, the configured Directory and SAML providers must be thread-safe, as the same provider implementations will be accessed from all executing clients.

WSSE Mapping

Now it is necessary to review the mapping of the Toolkit's elements to the major building blocks of the WSSE specification. A word of caution--according to Section 5 of the specification (PDF), "the <wsse:Security> header block provides a mechanism for attaching security-related information targeted at a specific recipient in the form of a SOAP actor/role." In other words, this header is extensible by design, and it is not feasible trying to address all of its possible applications. Therefore, the Toolkit's framework only attempts to map the tokens and relationships, existing within the main specification, while allowing for adding new types of tokens and processing later on.

WS-Security machinery is actually defined of a number of related documents, which are depicted in Figure 2.

Figure 2
Figure 2. WS-Security documents hierarchy

First, there is the main WSSE specification (PDF), which lays out the ground rules about the data passed in security headers, and the rules of inserting them into the SOAP messages. This specification defines the essential part, or XML "skeletons" of the contained security tokens, leaving it up to the so-called profile documents (referred to in the appropriate sections below) to fill the details about tokens' content, as well as referencing rules.

A full example of a signed WSSE Header with various types of tokens and a SAML assertion can be viewed here using the latest versions of IE or Mozilla-based browsers (it is also a part of the source download). Figure 3 presents an annotated outline of a WSSE Header with several tokens and a signature in it.

Figure 3
Figure 3. Annotated WSSE Header

Security Tokens

Section 6 of the WSSE specification describes various types of security tokens that may be added to a WS-Security Header. These security tokens are generally carried in WSSE Headers to pass certain security information to the request's target (or, in some cases, intermediate) service. The specification, however, does not dictate what kind of information is passed around, leaving it up to the communicating applications to agree on semantics. What is enforced, however, are the insertion and processing rules for the supported types of tokens.

WSSE security tokens are represented by the classes in the wsse.Toolkit.Tokens.Wss package. Most of the supported token types are creatable from the existing XML; i.e., they can be extracted from one message and used in another one, which was one of the requirements for the Toolkit. All of the tokens, described below (with the exception of the header token itself), are optional. Their presence in the security header is dictated not by the WSSE specification, but by the application's logic.

Token validation against the standards takes place when XML is generated for a particular token, or when a token is created from the existing XML element. XML generation and its validation are delayed until the token is explicitly asked for its XML content. This allows for avoiding excessive XML parsing and for implementing only a single validation point, rather than scattering it over a number of places where XML elements may be constructed and updated. XML generation and insertion depends on the tokens' ordering, and, in certain cases, this ordering is important for proper processing of the generated header.

The Toolkit includes two different types of tokens--simple ones, which represent a single XML element, and composite tokens (represented by the WsCompositeToken class), which may optionally include other tokens. Rules for processing and handling the composite tokens are different from those of simple ones, since the composite tokens must propagate any operation (such as setting the owner document) down to the children, and to define a number of additional search/replace methods on the contained tokens.


<SOAP-ENV:Header>
  <wsse:Security SOAP-ENV:actor="myService" 
                 SOAP-ENV:mustUnderstand="1">
    ...
  </wsse:Security>
</SOAP-ENV:Header>

A WSSE Header itself, shown in the preceding code example, is represented by the WsseHeaderToken class. This class handles processing of all of the contained security tokens, enforces the header's conformance to the specification, and generates the header's XML. It supports the SOAP 1.1 actor attribute, as specified in the Section 5 of the WSSE specification. The SOAP mustUnderstand attribute can be set on the generated header, but enforcement of its processing rules should happen prior to constructing new headers and, therefore, falls outside of the Toolkit's scope.

Additionally, the WsseHeaderToken class includes a number of helper functions to perform common header operations, such as inserting a timestamp or signing and encrypting its tokens. It also exposes generic methods to insert additional types of token processors for extensibility, as demonstrated in Example 2. All added processors are called in turn, according to their insertion order--this helps to ensure that newly generated XML elements are made available for the processors later in the chain.


<wsse:Security ...>
  <wsse:UsernameToken wsu:Id="xdJKdk6hgg68h">
    <wsse:Username>Admin</wsse:Username>
    <wsse:Password Type="wsse:PasswordDigest">
      xcYjkd9Hjkksds...
    </wsse:Password>
    <wsse:Nonce>WKjd73j0...</wsse:Nonce>
  </wsse:UsernameToken>
</wsse:Security>

The wsse.Toolkit.Tokens.Wss.UsernameToken class handles the UsernameToken element, described in the Section 6.2 of the WSSE specification and in the Section 3 of Username token profile (PDF). The UsernameToken element is used as a standard means of passing the user's name and (optionally, as a confirmation mechanism) his or her password to the destination service. It also supports addressing by wsu:Id, which is used to reference it from the processing nodes, such as XML Signature. Auxiliary classes from the package wsse.Toolkit.Directory are used to retrieve user information from the specified User Store--it is represented in the code via wsse.Toolkit.Directory.IUserDirectory interface.


<wsse:Security ...>
  <wsse:BinarySecurityToken ValueType="wsse:X509v3"
                            EncodingType="wsse:Base64Binary"
                            wsu:Id="MyX509">
    MIIOlskew78Hjkds...
  </wsse:BinarySecurityToken>
</wsse:Security>

Binary tokens, from Section 6.3 of the WSSE specification and Section 3 of the X.509 token profile (PDF), which are used in WSSE to pass key or certificate data, are supported by the Toolkit's wsse.Toolkit.Tokens.Wss.BSTToken class. Since XML is a text-based format, the value of this element has to be Base64-encoded prior to inserting it into the security header. Although several profile specifications already exist (or are being developed), representing different types of binary tokens (Kerberos tickets, for instance), the Toolkit will only support X.509 tokens, as they are significantly more widespread.

Due to the extreme flexibility and great variety of possible XML security tokens, details of attaching them to the WSSE Header (see Section 6.4 of the specification) are completely deferred to the specific profile documents--for other supported token types, the main WSSE specification at least defines a general insertion framework. The Toolkit provides the class wsse.Toolkit.Tokens.Wss.SamlAssertionToken to implement the SAML token profile (PDF), which details the attachment of SAML Assertions to WSSE Headers. Assertion generation is delegated to configured providers, which are accessed via the wsse.Toolkit.Saml.IAssertionGenerator interface.

Token References

In general, providing a good reference framework for WSSE security tokens is a very non-trivial task, especially remembering that most elements may occur more than once in a particular header. Fortunately, the Toolkit does not need to provide a full generic implementation--when tokens are parsed from an existing header, it is reasonable to assume that the application has certain knowledge of the security tokens that it is intending to pass on.

References' implementations rely on certain specific characteristics of various types of tokens to retrieve or construct their instances. Usage of some tokens may be restricted (either by the standards, or by application policy) to only a single token per header (like timestamps, for instance, or by rejecting requests with more than one Username XML element); others may specify additional search context (such as the signer's public key name), sufficient for unambiguously retrieving the desired token from the processed WSSE header.


<wsse:SecurityTokenReference wsu:Id="X509STR">
  <wsse:Reference URI="#MyX509"/>
</wsse:SecurityTokenReference>

The WSSE specification defines a wsse:SecurityTokenReference element as a common way of referencing security tokens, be they X.509 certificates or SAML assertions. In the Toolkit, references to security tokens are handled via derivatives from the wsse.Toolkit.Tokens.Refs.WsTokenRef class. Generally, reference elements are going to be added to other security tokens (for instance, DSig) during their processing, so there is no need to explicitly insert instances of reference classes into WsCompositeToken objects. The only exception to this rule is the wsse.Toolkit.Tokens.Refs.Xml.SecureTokenRef class, which represents the wsse:SecurityTokenReference XML element in the Toolkit's framework.

Since the wsse:SecurityTokenReference element is supposed to handle references to almost any imaginable type of security token, its structure is by necessity very complicated. Section 7.1 of the WSSE specification lists several possible types of reference mechanisms that could be used with this token:

Several additional reference mechanisms are defined for the STR tokens, including Key Names and Embedded References in the main WSSE specification. However, due to lack of space, they are not going to be covered here in any detail.

Token Processing

Besides simply carrying security tokens, the WSSE specification utilizes XML Signature and XML Encryption recommendations to protect the integrity and confidentiality of SOAP messages. In the Toolkit, this functionality is represented by Token Processors. Their main common feature is that they all work on already-present nodes in the security headers, possibly altering or removing them, and inserting new ones into the WSSE header to reflect the processing results. This behavior is defined by the wsse.Toolkit.Processors.ITokenProcessor interface, and concrete implementations will provide processor-specific logic. At the moment, implementation of two processors for XML Signing and Encryption is planned and will be covered in future installments.

Conclusion

This article provided a high-level overview of major components of the WSSE Toolkit (tokens, references, and processors) and how they match various parts of the WS-Security standards. In the next installment we will have fun dissecting the verification requirements of a WSSE Header and describing implementation details of Username, Binary, and SAML tokens, as specified in the development plan for the Toolkit in the opening article of this series.

Resources

The project's source that accompanies this article is being developed in Java using Borland's JBuilder IDE, and includes its project files. There is also a crude Windows batch file to build the class hierarchy included with the archive, which may be downloaded here. As the project moves forward, the implementation files will be updated or added to the project, and made available with the new installments of this series. Please, direct all comments regarding the project's source code to my email address (see the author's link at the beginning), as I prefer to separate programming and standards discussions.

Denis Piliptchouk is a senior technical member of BEA's AquaLogic Enterprise Security group, participates in OASIS WSS and WS-I BSP standards committees, and regularly contributes to industry publications.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.