 | Level: Intermediate Hyen-Vui (Henry) Chung (hychung@us.ibm.com), Senior Software Engineer, IBM Simon Kapadia (simon.kapadia@uk.ibm.com), Security Lead, ISSW EMEA, IBM
26 Mar 2008 This two-part series describes how to generate custom tokens using
Web services security, authenticate them with WebSphere® Application Server,
and create credentials from them. Part 1 describes how to generate custom tokens
using a sample based on the JAX-RPC programming model for Web services.
Introduction
WebSphere Application Server V6.0, V6.0.2 and V6.1 support the Web Services
Security SOAP Message Security 1.0 (hereafter called WSS 1.0) standard
specification from OASIS. WSS 1.0 is one
of the key specifications to enable secure Web services commercially. The
specification defines enhancements to SOAP messages by providing message integrity
and confidentiality. It also defines a generic, flexible mechanism for associating
security tokens with SOAP messages. The specification is designed to be extensible
to support multiple token formats. It describes how to encode binary security
tokens and a framework for XML-based tokens. The specification does not include
any specific security tokens. Specific security tokens are defined in various
security token profiles.
WebSphere Application Server V6 and beyond (hereafter called Application Server)
provides out-of-the-box support for the following specifications and security
token profiles:
Besides the two security token profiles, Application Server V6 and beyond provide
a pluggable security token framework for applications to associate custom security
tokens with SOAP messages. The framework is designed to be flexible and extensible
to accommodate binary security tokens and XML-based security tokens.
This article provides step-by-step instructions on implementing the code and
performing the configuration required to enable the Web services run-time to
generate custom tokens. For the purposes of this article, we’ll use an XML-based
token, but you can use the same process for other token types.
This article is based on the JAX-RPC programming model for Web services, and is
applicable to WebSphere Application Server V6.0, V6.0.2 and V6.1. The programming
model is based on the pluggable token framework included in the Web services
security engine since WebSphere Application Server V6.0. We’ll use Rational
Application Developer V6.1 for the examples.
Overview of the pluggable
security token framework
Figure 1 shows the high-level architecture of the pluggable security token
framework included in Application Server for JAX-RPC Web services since V6.0.
Figure 1. Architecture of the
pluggable security token framework
The key components are as follows:
- Sender (Generator):
- The TokenGenerator interacts with the callback handler to acquire the
information for the security token, creates the XML representation of the
security token, and inserts it into WSS header.
- The callback handler acquires the information for the security token, for
example, prompting for username and password, talking to an external user
registry for user data, and so on.
- The security token WSSToken is a Java™ representation of the security
token.
- Receiver (Consumer):
- The TokenConsumer parses the XML representation of the security token
from the SOAP message. It uses the Java Authentication and Authorization
Service (JAAS) programming model to create a JAAS Login Module for
validating the security token, and passes the security token information to
the JAAS Login Module via a callback handler.
- The JAAS Login Module authenticates or validates the security token. It
may also perform identity mapping of the security token. It acquires the
security token information from the callback handler.
- The callback handler supplies the security information to the JAAS Login
Module.
- The security token WSSToken is a Java representation of the security
token.
This architecture is designed to be flexible and to handle custom tokens. It
separates the XML token format from the way in which the token information is
acquired or validated. In this architecture, on the sender side, you could use
different callback handler implementations to acquire security token information
from different input sources. For example, the out-of-the-box UsernameToken
implementation leverages the same framework: there is only one UsernameToken
Generator implementation but multiple callback handlers, one for reading the
username and password from the bindings file and another for standard command line
and GUI prompts.
Similarly, on the receiver side, the parsing of the XML security token from the
SOAP message is handled by the TokenConsumer, while the validation or
authentication of the security token is handled by the JAAS Login Module. In this
architecture, if there is a new requirement for token validation, the end users
only need to supply a new JAAS Login Module implementation, and reuse the same
TokenConsumer implementation for the XML parsing of the security token.
This article describes how the sender, or generator, generates an XML-based
custom token. Part 2 will describe how to consume the XML-based custom token.
Overview of the sample
The sample described in this article is a simple XML-based custom token called
MyToken that has only one field, an email address. The XML format of the token is
shown in Listing 1.
Listing 1. Sample XML token format
<soapenv:Envelope xmlns:soapenv="……" >
<soapenv:Header>
<wsse:Security xmlns:wsse="……" soapenv:mustUnderstand="1">
<a:MyToken xmlns:a="http://www.acme.com" >
<a:EMail>joe.smith@acme.com</a:EMail>
</a:MyToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
……
</soapenv:Body>
</soapenv:Envelope>
|
Figure 2 shows the components required to generate MyToken:
Figure 2. Sender components
The components (in the red box) that must be implemented to generate the custom
XML-based token MyToken are:
- MyTokenGenerator, which implements the Token Generator interface
- MyTokenCallbackHandler, which implements the JAAS Callback Handler interface
- MyToken, which extends the WSSToken abstract class
The rest of this article describes how to implement each of these components.
Part 2 will describe how to implement the required receiver components to validate
and authenticate the MyToken security token.
Configure the Web
services security run-time to generate a custom token
The following are the steps required to configure the WSS run-time to generate a
custom token:
- Develop the required code components described in the previous section and
deploy them to the application server, as described in
Develop the implementation classes.
- Create the custom token configuration in the application deployment
descriptor and bindings using a Web services editor, as described in
Configure the application. In this article, we use
Rational Application Developer V6.1.
- Deploy the Web services application to the application server. This step will
be covered in Part 2.
Step 1: Develop the
implementation classes
This section how to develop the implementation classes required to generate the
XML custom token.
Implement the TokenGenerator
MyTokenGenerator is responsible for creating the XML representation of MyToken.
In this example, the implementation shows that there is separation of the token
format and how the token data is obtained. A callback handler implementation,
called MyTokenCallbackHandler, is used to obtain the token data from the
application bindings, as described in
Implement the JAAS Callback Handler. You
could easily supply another implementation to obtain the email from another
source, such as from the SOAP message context.
MyTokenGenerator must implement the
com.ibm.wsspi.wssecurity.token.TokenGeneratorComponent
Java interface. Listing 2 shows the implementation of the
invoke and
createTokenElement methods of MyTokenGenerator:
Listing 2. MyTokenGenerator implementation
public void invoke(Document doc, Element parent, final Map context)
throws SoapSecurityException {
……
TokenGeneratorConfig config =(TokenGeneratorConfig)
context.remove(TokenGeneratorConfig.CONFIG_KEY);
SOAPMessageContext messageContext = (SOAPMessageContext)
context.get(com.ibm.wsspi.wssecurity.Constants.WSSECURITY_MESSAGE_CONTEXT);
CallbackHandlerConfig cbhconf = config.getCallbackHandler();
CallbackHandler handler = null;
String email = null;
if (cbhconf != null) {
String cbhname = cbhconf.getClassName();
if (cbhname != null && cbhname.length() != 0) {
Map properties = new HashMap();
handler = cbhconf.getInstance();
if (handler == null) {
Class cls = null;
try {
ClassLoader loader = AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
}
);
if (loader != null) {
cls = loader.loadClass(cbhname);
} else {
cls = Class.forName(cbhname);
}
} catch (Exception e) {
……
}
if (CallbackHandler.class.isAssignableFrom(cls)) {
// The configuration of a CallbackHandler.
properties.put(CallbackHandlerConfig.CONFIG_KEY, cbhconf);
try {
Constructor con = cls.getConstructor(new Class[] { Map.class });
handler = (CallbackHandler)con.newInstance(new Object[]{ properties });
} catch (Exception e) {
……
}
} else {
// Error conditions and throws exception
throw new ……
}
// Caches the instance of CallbackHandler.
cbhconf.setInstance(handler);
}
} else {
// Error conditions and throws exception
throw new ……
}
} else {
// Error conditions and throws exception
throw new ……
}
// getting the email id
Map properties = new HashMap();
// The MessageContext object.
if (messageContext != null) {
properties.put(com.ibm.wsspi.wssecurity.Constants.WSSECURITY_MESSAGE_CONTEXT,
messageContext);
}
Callback[] callbacks = new Callback[] { new MyEmailCallback(),
new PropertyCallback(properties) };
// Invokes a CallbackHandler.
try {
handler.handle(callbacks);
} catch (UnsupportedCallbackException e) {
// Error conditions and throws exception
throw new ……
}
email = ((MyEmailCallback) callbacks[0]).getEmail();
if (email == null || email.length() == 0)
throw new SoapSecurityException(CLS_NAME + ": no email found");
……
String idStr = ……; // wsu:Id of the token
Element elem = createTokenElement(doc, parent, email, idStr);
elem = (Element) parent.insertBefore(elem, parent.getFirstChild());
……
// insert token in Subject
final MyToken t = new MyToken(email, idStr, /* pass in principal name */ , elem);
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
Subject subject =
(Subject)context.get(com.ibm.wsspi.wssecurity.Constants.WSSECURITY_SUBJECT);
if (subject == null) {
System.out.println(CLS_NAME + " ERROR: Subject is null!");
} else {
subject.getPrivateCredentials().add(t);
}
return null;
}
});
}
private Element createTokenElement(Document doc, Element parent, String email,
String idStr) {
Element t = doc.createElementNS(MYTOKEN_NS, "a:" + MYTOKEN_LS);
t.setAttributeNS(XMLNS, "xmlns:a", MYTOKEN_NS);
t.setAttributeNS(XMLNS, "xmlns:u", ID_NS);
t.setAttributeNS(ID_NS, "u:" + ID_LS, idStr);
Element e = doc.createElementNS(MYTOKEN_NS, "a:EMail");
e.appendChild(doc.createTextNode(email));
e = (Element) t.appendChild(e);
return t;
}
}
|
The invoke method is the key method of the
MyTokenGenerator implementation. The bindings information of the TokenGenerator
and callback handler configuration is obtained from the context object, using the
appropriate keys. After obtaining the contextual information, the
invoke method instantiates the callback handler
implementation specified in the bindings, which is MyTokenCallbackHandler in this
example. It then calls MyTokenCallbackHandler to obtain the email information
using MyEmailCallback, constructs MyToken to hold the token information, and
stores it in the Subject obtained from the
Context.
The invoke method also creates the XML representation
of MyToken and inserts it into the WSS header, which is passed into the
invoke method as the parent
parameter.
Note that the standard exception class used in the code is
SoapSecurityException. You should use this exception
class for error conditions encountered in the processing flow.
Implement the JAAS Callback Handler
The callback handler must implement the JAAS
javax.security.auth.callback.CallbackHandler interface,
which is part of the JAAS programming model. Listing 3 shows the code for the
MyTokenCallbackHandler implementation for the sample.
Listing 3. JAAS Callback Handler implementation
public class MyTokenCallbackHandler implements CallbackHandler {
private final static String EMAIL = "com.acme.Constants.Email";
private CallbackHandlerConfig _config;
private String _email = null;
public MyTokenCallbackHandler(Map properties) {
if (properties == null) {
this._config = null;
} else {
this._config = (CallbackHandlerConfig) properties.get
(CallbackHandlerConfig.CONFIG_KEY);
Map p = this._config.getProperties();
if (p != null)
this._email = (String) p.get(EMAIL);
}
}
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
Callback c = callbacks[i];
if (c instanceof MyEmailCallback) {
((MyEmailCallback) c).setEmail(this._email);
} else if (c instanceof PropertyCallback) {
// Not used
} else {
System.out.println("WARNING: unsupported callback " + c.getClass().getName());
}
}
}
}
|
MyTokenCallbackHandler obtains the email data to be propagated and passes it to
MyTokenGenerator. This implementation is very simple; it obtains the email address
specified in the CallbackHandler property (using
com.acme.Constants.Email as a key) of the bindings.
(See Configure the application for more information.) It
uses MyEmailCallback to pass the information. You can
easily extend this implementation to obtain the email from a UI prompt, a
database, LDAP or other repositories.
Listing 4 shows the the MyEmailCallback implementation, which must implement the
JAAS javax.security.auth.callback.Callback interface.
Listing 4. MyEmailCallback implementation
public class MyEmailCallback implements Callback {
private String _email;
public String getEmail() {
return this._email;
}
public void setEmail(String email) {
this._email = email;
}
}
|
The implementation of MyEmailCallback is shared between the sender and receiver.
Implement the security token
The security token is the Java representation of the custom token. It must extend
the com.ibm.wsspi.wssecurity.auth.token.WSSToken
abstract class. Listing 5 shows the code for the MyToken security token
implementation for the sample:
Listing 5. Security token implementation
public final class MyToken extends WSSToken {
private final long _expiration = Long.MAX_VALUE; // never expired
private int _hashCode;
private String _username;
private String _uid;
private String _email;
public MyToken(String email, String id, String username, Element elem){
this._email = email;
this._username = username;
this._tokenId = id;
this._tokenelem = elem;
this.setType(Util.MYTOKEN_TYPE);
}
public long getExpiration() {
return this._expiration;
}
public String getPrincipal() {
if (isReadOnly()) {
return new String(this._username);
}
return this._username;
}
public String getUniqueID() {
if (this._uid == null) {
this._uid = String.valueOf(this.hashCode());
}
//return a copy if readOnly
if(isReadOnly()){
return new String(_uid);
}
return this._uid;
}
public int hashCode() {
……
return this._hashCode;
}
public String getEmail() {
if (isReadOnly()) {
return new String(this._email);
}
return this._email;
}
public boolean isValid() {
return (this._email != null && this._email.length() != 0);
}
public String toString() {
……
}
}
|
The MyToken object is a Java representation of the XML custom token. This example
uses the same object for both sender and receiver. It holds the email address and
the mapped principal. The key methods are as follows:
- The constructor sets the qualified name (QName) of the token type using the
setTypemethod. This is how the token is identified in
the run-time. In this sample, the token type is
{http://www.acme.com}MyToken. Note that this type is
used in the Configure the application section when
defining the custom token in the deployment descriptor and bindings.
-
getEmail returns the email address. This method is
specific to the XML email token used in this sample.
-
getExpiration returns the expiration time (in
milliseconds). In this sample, there is no expiration for MyToken, so it is set
to java.lang.Long.MAX_VALUE.
-
getPrincipal returns the principal name of the
security token. In this sample, it is the user name of the email address. This
is the identity used by the WSS run-time to create the WebSphere credential,
which is described in Part 2 of this series.
-
getUniqueID returns the unique ID that will be
included as a part of the cache key used in the authenticated
Subject. This is used by the receiver. In this
sample, it uses the hash code of MyToken. If you don’t want the token to affect
the cache key, the implementation should return null.
-
isValid returns the validity of the token. In this
example, the check is very simple: the email must be present and not empty.
Step 2: Configure the
application
Now that you’ve implemented all of the required classes, the next step is to
configure the application to use the TokenGenerator
class to propagate the email security token. The high-level steps to do this are:
- Create a security token of type
{http://www.acme.com}MyToken in the WS Extension on
the client.
- Create the TokenGenerator for the MyTokenGenerator implementation in the WS
Binding on the client.
These steps are described in the following sections.
Step 1: Create the security token
To create the security token, do the following:
- In this sample, the client is a J2EE application client. To open the client
in the editor, double-click application-client.xml, as shown in Figure 3.
Figure 3. Open the application
client
- Click the WS Extension tab, as shown in Figure 4.
Figure 4. Select the WS
Extension tab
- Expand Request Generator Configuration, then expand Security
Token, and click Add, as shown in Figure 5.
Figure 5. Add security token
- In the Security Token dialog shown in Figure 6, do the following:
- Specify
MyToken as the Name.
- Specify
{http://www.acme.com}MyToken as the
URI.
- Specify
MyToken as the Local name.
Figure 6. Specify security
token properties
Step 2: Create the Token Generator
To create the Token Generator, do the following:
- Click the WS Binding tab in the editor, as shown in Figure 7.
Figure 7. Select the WS
Binding tab
- Expand Security Request Generator Binding Configuration, then Token
Generator, and click Addto create a new Token Generator
configuration, as shown in Figure 8.
Figure 8. Add Token Generator
- In the Token Generator dialog shown in Figure 9, do the following:
- In the Token generator name, specify
MyToken.
- In the Token generator class field, specify the Token Generator
implementation class
com.acme.security.MyTokenGenerator.
- For Security token, select the name specified in the Security
Token dialog, which is
MyToken for this example.
- Check
Use value type.
- Specify
MyToken as the Local name.
- Specify
{http://www.acme.com}MyToken as the
URI.
- For Callback handler, specify
com.acme.security.MyTokenCallbackHandler. In the
Callback Handler Property, define the email address as
joe.smith@acme.com using with the name
com.acme.Constants.Email.
Figure 9. Specify Token
Generator properties
Step 3: Deploy the application
The next step is to deploy the application and run the client, which will be
covered in Part 2 of this series.
The following message is captured by a TCP/IP monitor when you run the sample:
Listing 6. Captured SOAP message from the
sample
<soapenv:Envelope
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/
wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<a:MyToken xmlns:a="http://www.acme.com"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-wssecurity-utility-1.0.xsd" u:Id="com.acme.security.MyTokenGenerator_1">
<a:EMail>joe.smith@acme.com</a:EMail>
</a:MyToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<p657:doEcho xmlns:p657="http://www.acme.org">
<s>Custom Token Test</s>
</p657:doEcho>
</soapenv:Body>
</soapenv:Envelope>
|
Summary
In this article, you learned how to implement a custom token using the System
Programming Interfaces from the pluggable security token framework, and how to
configure an application to enable use of a custom token. In Part 2, you’ll learn
how to consume a custom token. In Part 2, you’ll learn how to consume a custom
token.
Download | Description | Name | Size | Download method |
|---|
| Sample project and code | part1sample.zip | 30KB | HTTP |
|---|
Resources
About the authors  | 
|  |
Henry Chung is an architect and lead developer of Web services security on the WebSphere platform. Henry has been in middleware development for over 5 years and has developed many security features for the WebSphere platform. Henry&s current focus is leading the development of the latest WebSphere Web services security specifications. He also helps customers and other IBM teams apply Web services security solutions. His primary goal is to enable WebSphere Web services security support to meet real-world needs. You can contact Henry at hychung@us.ibm.com.
|
 | 
|  |
Simon Kapadia is the Security Lead for IBM Software Services for WebSphere (ISSW) in EMEA (Europe, Middle East, Africa). He works on designing and implementing large distributed computer systems for IBM customers. Simon holds a Bachelor’s degree in English and Drama, and a Master’s in Computer Science. He has owned a computer since six years of age, and has turned a lifelong hobby into a career. Prior to joining IBM, Simon developed software for digital exchanges at Bell Laboratories, managed networks and Web applications at a Polish ISP, and supported and consulted on DCE, DFS and Encina for Transarc Corporation.
You can reach Simon at simon.kapadia@uk.ibm.com or via his public Web site at http://www.kapadia.pl.
|
Rate this page
|  |