Skip to main content

skip to main content

developerWorks  >  WebSphere | SOA and Web services  >

Security for JAX-RPC Web services, Part 1: Generating custom tokens

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Rate this page

Help us improve this content


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.



Back to top


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
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.



Back to top


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
Figure 2. Sender components

The components (in the red box) that must be implemented to generate the custom XML-based token MyToken are:

  1. MyTokenGenerator, which implements the Token Generator interface
  2. MyTokenCallbackHandler, which implements the JAAS Callback Handler interface
  3. 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.



Back to top


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:

  1. Develop the required code components described in the previous section and deploy them to the application server, as described in Develop the implementation classes.
  2. 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.
  3. 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:

  1. 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.
  2. getEmail returns the email address. This method is specific to the XML email token used in this sample.
  3. 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.
  4. 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.
  5. 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.
  6. 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:

  1. Create a security token of type {http://www.acme.com}MyToken in the WS Extension on the client.
  2. 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:

  1. 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
    Figure 3. Open the application client

  2. Click the WS Extension tab, as shown in Figure 4.

    Figure 4. Select the WS Extension tab
    Figure 4. WS Extension tab

  3. Expand Request Generator Configuration, then expand Security Token, and click Add, as shown in Figure 5.

    Figure 5. Add security token
    Figure 5. Add security token

  4. 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
    Figure 6. Specify security token properties

Step 2: Create the Token Generator

To create the Token Generator, do the following:

  1. Click the WS Binding tab in the editor, as shown in Figure 7.

    Figure 7. Select the WS Binding tab
    Figure 7. WS Binding tab

  2. 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
    Figure 8. Add Token Generator

  3. 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
    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>



Back to top


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.




Back to top


Download

DescriptionNameSizeDownload method
Sample project and codepart1sample.zip30KBHTTP
Information about download methods


Resources



About the authors

Henry Chung photo

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

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


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!