Saturday, June 26, 2010

Using Spring AOP to Trace Invocation

Security is a major requirement of many types of enterprise services. It’s also a risky area to try to implement on your own, because even a minor and obscure oversight can lead to serious vulnerabilities. These characteristics make standardization of security handling appealing, allowing many experts to contribute to a standard and avoid any individual’s oversights. SOAP-based Web services can use widely supported WS-Security and related standards for their security needs, allowing security to be configured for each service as appropriate.

Apache Axis2 supports these security standards by means of the Rampart module (see Resources). In this article, you’ll see how to install, configure, and use Rampart with Axis2 for the basic security function of sending a username and password on a service request. In subsequent columns in this series, you’ll learn how to use Rampart for more sophisticated forms of security.

About this series

Web services are a crucial part of Java™ technology’s role in enterprise
computing. In this series of articles, XML and Web services consultant Dennis Sosnoski
covers the major frameworks and technologies that are important to Java developers
using Web services. Follow the series to stay informed of the latest developments in the field and be aware of how you can use them to aid your programming projects.

WS-Security

WS-Security is a standard for adding security to SOAP Web service message exchanges
(see Resources). It uses a SOAP message-header element to attach the security information to messages, in the form of tokens conveying different types of claims (which can include names, identities, keys, groups, privileges, capabilities, and so on) along with encryption and digital-signature information. WS-Security supports multiple formats for tokens, multiple trust domains, multiple signature formats, and multiple encryption technologies, so in most cases the header information needs to include specific format and algorithm identification for each component. The added information can result in a complex structure for the header information, as shown in the (heavily edited) Listing 1 — a sample message with signing and encryption:

Listing 1. Sample message with signing and encryption

              2006-07-11T21:59:32Z     2006-07-12T06:19:32Z        MIIEC56MQswCQY...                           LlYsHyhNnOVA9Aj7...                      g+A2WJhsoGBKUydZ9Za...                                                                                        lKjc5nyLQDZAIu/hZb4B6mLquow=            ...          TiLmWvlz3mswinLVQn58BgYS0368...                                                        mirmi0KuFEEI56eu2U3cICz...          

In this article, you’ll see some simple examples of WS-Security headers, with only a single token. The next article in the series will go further in discussing the type of complex structure shown in Listing 1.

WS-Security applies to actual SOAP message exchanges. The service implementation can verify that WS-Security has been properly applied to incoming messages, but clients need to know in advance what they must implement in order to use the service. With the complexity of WS-Security and the number of options supported, it can be difficult just to use a text description for this purpose, and manual configuration of WS-Security handling can cause errors. WS-Policy is a general-purpose structure for specifying extension requirements for Web services, and WS-SecurityPolicy is an extension of WS-Policy specifically for WS-Security support. Together these two standards support describing WS-Security requirements in machine-readable form. The WS-Policy and WS-SecurityPolicy information can be used alone or embedded directly inside Web Services Description Language (WSDL) documents, so that Web service frameworks can configure themselves automatically to a service’s requirements.

Introducing Rampart

Rampart is the Axis2 security module, supporting WS-Security, WS-SecurityPolicy,
WS-SecureConversation, and WS-Trust. In this article, you’ll only see Rampart’s WS-Security and WS-SecurityPolicy functions; later articles will give you a look at the other features.

Because Rampart is implemented as a module (actually a pair of modules — rampart.mar and rahas.mar), it plugs into the Axis2 processing framework and does its job by intercepting messages at particular points in the inbound and outbound processing, checking or making changes to the messages as appropriate.

Installing Rampart

Rampart comes with several .jar files (in the distribution’s lib directory), along with
a pair of .mar module files (in the dist directory). You must add the .jar files to
your classpath in order to use Rampart with Axis2, and you must add the .mar files to either your classpath or an Axis2 repository structure.

The easiest way of handling the Rampart .jar and .mar files is to add them into your Axis2 installation. You can just directly copy the .jar files from the Rampart lib directory into your Axis2 lib directory, and the .mar files from the Rampart dist directory into your Axis2 repository/modules directory. (You can also use the Ant build.xml in the Rampart samples directory to copy the files across to your Axis2 installation. Just set theAXIS2_HOME environmental variable to your Axis2 installation directory and run ant from a console open to the Rampart sample directory.)

For many WS-Security features, you also need to add the Bouncy Castle security provider to your JVM security configuration and the Bouncy Castle .jar to your Axis2 installation. This step — not needed for the UsernameToken you’ll learn about in this article — is required for other security features to be covered later in the series. Because of patent issues with some of the security algorithms, the Bouncy Castle .jar is a separate download from Rampart (see Resources). Download the appropriate version of the .jar for your Java runtime, and add the .jar to the Axis2 lib directory. You then need to modify your Java installation’s security policies to use the Bouncy Castle code by adding a line to the java.security file found in your Java runtime’s lib/security directory. Look for the section of the file with several different security.provider lines, and add the following line:

security.provider.99=org.bouncycastle.jce.provider.BouncyCastleProvider

The ordering of the security.provider lines in the file doesn’t matter, but it’s a good idea to add this after the other lines for predefined security provider implementations.

To use the Rampart code in an Axis2 server installation, you need to create a new axis2.war file, one that includes the added Rampart .jar and .mar files. You can use the Ant build.xml provided in the webapp directory to create axis2.war, provided you make one change: delete the line near the end of the file. Then open a console to the Axis2 webapp directory and run ant. After the build.xml runs, you can find the created axis2.war Web application in the Axis2 installation dist directory.

A sample application

The application provided in the example code (see Downloads) is
based on one I used in “Axis2 Data Binding” to demonstrate data-binding alternatives for Axis2. For this article and the following ones on Axis2 WS-Security support, I’ve trimmed it down to just three operations: getBook, addBook, and getBooksByType. To keep things simple, only the Axis Data Binding (ADB) version of the code is provided, but this is not a requirement of working with WS-Security in Axis2 — Rampart implements WS-Security at a level that’s independent of the data-binding technique your code uses, so it works with all forms of data binding supported by Axis2.

The root directory of the example code is jws04code. Inside this directory, you’ll find Ant build.xml and build.properties files, the library.wsdl file giving the service definition for the example application, a log4j.properties file used to configure client-side logging, and several property-definition XML files (all named XXX-policy-client.xml or XXX-policy-server.xml). The build.properties file configures the operation of the example application. Listing 2 shows the supplied version of this properties file:

Listing 2. Supplied build.properties file

# set axis-home to your Axis2 installation directory axis-home=PATH_TO_AXIS2_INSTALLATION # set the connection protocol to be used to access services (http or https) protocol=http # set the name of the service host host-name=localhost # set the port for accessing the services (change this for monitoring) host-port=8080 # set the base path for accessing all services on the host base-path=/axis2/services/ # set the name of the policy file to be used by the client client-policy=plain-policy-client.xml # set the name of the policy file to be used by the server server-policy=plain-policy-server.xml

Before trying out the examples, you need to edit the build.properties file and set the actual path to your Axis2 installation (with Rampart added, as discussed in the preceding section). If you’re using a different host or port number for your server, you also need to modify the host-name and host-port values. I’ll discuss the remaining values later in this article.

Giving WS-Security a try

WS-Security defines several types of security tokens (including tokens that are part of the core specification, and those defined by profiles as plug-in extensions to the specification), with many options for how the tokens are constructed and used. The point of this article is the configuration and use of Rampart with Axis2, so I’ll just use the simplest useful token as an example: the UsernameToken, defined by theUsernameToken profile.

UsernameToken WS-SecurityPolicy

The purpose of a UsernameToken is just to convey username and password information as part of the WS-Security headers. The most basic form of UsernameToken sends both the username and password as plain text. This isn’t optimal from a security standpoint (though there’s nothing wrong with using this approach over secure connections), but it’s easy to see what’s being sent, making it a useful starting point.

The WS-SecurityPolicy configuration for a UsernameToken sent as text can be as simple as shown in Listing 3. This policy (shown here with one line split in two to fit the page width — not valid for actual use) consists of a standard WS-Policy wrapper (the elements using the wsp prefix) around a WS-SecurityPolicy UsernameToken assertion.

Listing 3. WS-SecurityPolicy for plain-text UsernameToken

                                                            

The UsernameToken in Listing 3 uses an IncludeToken attribute to specify the type of message flow that is to include the token — in this case, all message flows that go from the initiator of a request (that is, the client) to the recipient of a request (the server). You could use other defined values for the IncludeToken attribute to specify other uses of the token, but for a UsernameToken this is generally the only value that makes sense.

Applying the policy

WS-Policy and WS-SecurityPolicy are designed to support embedding within WSDL service definitions. References are used to associate a policy to one or more, /, or definitions. Axis2 1.4.X implements preliminary handling for policies embedded in WSDL, but as of Axis2 1.4.1 the implementation is not yet robust. This article instead attaches the policies directly to the client and server in order to be compatible with the 1.4.1 code.

Server-side policy handling

For the server side, you apply a policy by adding it into the services.xml configuration file included in each Axis2 .aar service archive. The policy can be added directly as the child of a element to apply to all operations defined by that service. You also need to add a element to services.xml in order to tell Axis2 that the Rampart module must be included in the configuration for the service. Listing 4 is an edited version of the services.xml used by the example application, with the added module reference and policy information shown in bold:

Listing 4. services.xml with embedded policy

                         com.sosnoski.ws.library.adb.LibraryUsernameImpl     true     true            urn:getBook       http://.../getBookResponse          ...                                                                                                                                                                                       ...PWCBHandler                                     

Regenerating services.xml the easy way

When you use the Axis2 Wsdl2Java tool to generate server-side deployment files it creates
a services.xml file as part of the generated artifacts (in the resources
directory, under your generation target directory). Any time your WSDL service definition changes, you must regenerate this file, so the need to embed module reference and policy information in the file can be painful. The example code includes a tool to automate this modification to the generated file: thecom.sosnoski.ws.MergeServerPolicy class. (Source and binary are in the mergetool directory.) The build.xml file’s generate-server target runs this tool to insert the module reference and appropriate policy information into the generated services.xml file each time the server code is generated. You can use the tool for your own projects if you want. It takes the path to the services.xml file as the first command-line parameter, the path to the policy file as the second, and the names of any modules to be added as the remaining command-line parameters.

If you compare the embedded policy in Listing 4 with the basic policy in Listing 3, you’ll see one addition — a element. This element provides Rampart-specific extensions to the policy information, in this case giving the name of a class to be used for handling password callbacks. The callback is how your server code can verify the username-and-password combination supplied by the client on a request.

Listing 5 shows the actual implementation of the callback
class, as used for the plain-text password. In this case, both the username and password are supplied to the callback, and all the callback needs to do is verify the combination. If the username and password match the expected values, this just returns; otherwise, it throws an exception to indicate the error.

Listing 5. Password callback code

import org.apache.ws.security.WSPasswordCallback;  public class PWCBHandler implements CallbackHandler {     public void handle(Callback[] callbacks)         throws IOException, UnsupportedCallbackException {         for (int i = 0; i < pwcb =" (WSPasswordCallback)callbacks[i];" id =" pwcb.getIdentifer();">

For a real application, you’d naturally want to use some other
mechanism (such as a database or an external security mechanism) to
verify the username and password combination. The callback technique lets you use any verification technique you want as an extension of the Rampart security handling.

Client-side configuration

To use Rampart for your client code, you first need to have the module available for use with Axis2. You can do this by configuring an Axis2 repository structure for the client, but it’s generally easier just to include the rampart.mar module file (and any other modules you need to use) in your classpath. The supplied example uses the classpath approach.

You then need to configure the security policy and any related parameters for the client. The easiest way to handle this configuration is to set values directly on the service stub. Listing 6 shows how the configuration is done in the example code:

Listing 6. Client configuration

    /**      * Load policy file from classpath.      */     private static Policy loadPolicy(String name) throws XMLStreamException {         ClassLoader loader = WebServiceClient.class.getClassLoader();         InputStream resource = loader.getResourceAsStream(name);         StAXOMBuilder builder = new StAXOMBuilder(resource);         return PolicyEngine.getPolicy(builder.getDocumentElement());     }      public static void main(String[] args) throws IOException, XMLStreamException {          // check for required command line parameters         if (args.length < target =" args[0]" stub =" new" client =" stub._getServiceClient();" options =" client.getOptions();">

The configuration portion is the final code block in Listing 6. This gets theorg.apache.axis2.client.ServiceClient instance from the created stub and sets the policy information (loaded from the classpath) and username/password in the client options. It then engages the Rampart module in the Axis2 configuration used by the client. Once this is done, you can use the stub to access the service just as you would without WS-Security, and Rampart adds the UsernameToken automatically to each request.

Confirming the results

With Ant installed, you can just run ant from a console open
to the example code directory to build both client and server code. You can then
deploy the created library-username.aar file to your Axis2 server installation (one
that includes the Rampart .jars and .mars, of course), and try out the client by
entering ant run at the console. If everything is set up
correctly, you should see the output shown in
Figure 1:

Figure 1. Console output when running application
Console output when running application

Just running the client with the server doesn’t show you what’s happening, of course.
You can use a tool such as TCPMon to act as an intermediary between the client and
server and capture the message exchange to see the WS-Security UsernameToken in action (see Resources). To
do this, you’d first need to get TCPMon set up and accepting connections from the
client on one port, which it then forwards to the server running on a different port (or a different host). You can then edit the build.properties file and change the host-portvalue to the listening port for TCPMon. If you again enter ant run at the console, you should then see the messages being exchanged. Listing 7 shows a sample client-message capture:

Listing 7. Client message with UsernameToken

                         libuser         books                              scifi         

Securing UsernameToken

A basic plain-text UsernameToken doesn’t provide much
security directly, because both the username and the corresponding password are
visible to anyone able to monitor a message. If you use an encrypted communication channel, this isn’t a real problem — as long as the channel encryption is solid, no outside party can monitor a message. WS-SecurityPolicy conveniently defines a way to require the use of an encrypted channel, as shown in Listing 8 (again with a line split to fit page width — see the example code package’s secure-policy-server.xml file for the real policy):

Listing 8. Policy requiring HTTPS connection

                                                                                                  

The portion of Listing 8 shown in bold is the added part, consisting of a element and nested element. The element says that a secure HTTPS connection must be used in communicating with the service. If you try building the service .aar with this policy (by changing the server-policy value in build.properties to secure-policy-server.xml, and then running ant build-server) and deploying it, you’ll see that Rampart enforces this policy requirement, rejecting any normal HTTP connections.

If you want to try out an HTTPS connection to the service you can do so, but you first
need to configure your Web server to support HTTPS. (Tomcat has good instructions for this, at /tomcat-docs/ssl-howto.html.) You also need to change the protocol value in build.properties to https, and if you’re using a self-signed certificate for the Web server, you need to pass a trust store to the client when running the Ant test target. The supplied build.xml has a commented-out line to do this, so you can just uncomment the line and set the location of the appropriate trust store file on your system.

Another way of making UsernameToken more secure works even over unencrypted links. This method uses a digest value computed over a string made up of two other text values combined with the password. One of the text values, the nonce, is a random value generated by the sender for each request. The other, the created timestamp, is just the time at which the sender created the UsernameToken. Both these values are included in the UsernameToken as plain text. When properly used by both client and server, the combination of these values with the password in the digest makes it possible for the server to verify that the correct password was used when generating the digest, while making it difficult for any outside party to fake a valid password. Listing 9 gives a policy example for using the digest password, followed by an actual capture from a message using the digest password (both reformatted to fit page width — see the hash-policy-client.xml file for the real policy). The differences from the original policy are again shown in bold.

Listing 9. Policy using password digest, and sample message

                                                                                                                                           libuser         /Wt/2yDdZwa8a5qd7U70hrp29/w=         4ZQz5ytME/RXfChuKJ03iA==         2009-03-17T11:20:57.467Z                              scifi         

Wrapping up

In this article, you’ve seen how to use Axis2 and Rampart for a basic form of
policy-based WS-Security handling. In the next Java Web services installment, you’ll learn about two powerful features of WS-Security: XML encryption and signatures. Using XML encryption lets you keep your message content secret when operating over any type of connection, even when untrusted intermediaries are involved in the processing. Using XML signatures gives you guarantees that messages are really from the claimed originator and that the message content has not been tampered with in transit. Encryption and signing are the basis for
most enterprise security implementations, so check back to see how you
can apply these features in your own web services.

Download

DescriptionNameSizeDownload method
Source code for this articlej-jws4.zip10KB

HTTP

Information about download methods

Resources

Learn

  • Java
    Web services
    (Dennis Sosnoski, developerWorks): Read the complete series.

  • “Java Web Services: Axis2 Data Binding” (Dennis Sosnoski, developerWorks, July 2007) discusses the main data-binding options for Axis2. Its sample code is the basis of the sample code for this column.

  • Apache Axis2/Java: Visit the project home for the Axis2 Web services engine.

  • Rampart: Learn more about the Rampart WS-Security module for Axis2.

  • “Web services and Axis2 architecture” (Eran Chinthaka, developerWorks, November 2006): Learn how modules such as Rampart extend Axis2.

  • Understanding Web Services specifications: This series of tutorials introduces many of the important Web services standards, including:

    • “Understanding Web Services specifications: Web Services Description Language (WSDL)” (Nicholas Chase, developerWorks, July 2006).
    • “Understanding Web Services specifications: WS-Security” (Nicholas Chase, developerWorks, August 2006).
    • “Understanding Web Services specifications: WS-Policy” (Tyler Anderson, developerWorks, February 2007).

  • OASIS Web
    Services Security (WSS) TC: This is the organization responsible for the WS-Security specification and token profiles. You can find links here to all versions of these standards.

  • The W3C Web Services Policy Working Group: This group defines the WS-Policy specification.

  • OASIS Web Services Secure Exchange (WS-SX) TC: This organization is responsible for WS-SecurityPolicy and related specifications.

  • Browse the
    technology bookstore for books on these and other technical topics.

  • developerWorks Java technology zone: Find hundreds of articles about every aspect of Java programming.

Get products and technologies

  • Apache Axis2: Download the latest Axis2 release.

  • Rampart: Download the Rampart module for Axis2.

  • The Legion of the Bouncy Castle: Download the correct Bouncy Castle .jar for your runtime.

  • TCPMon: Download this open source utility for monitoring TCP connections.

No comments:

Post a Comment