Tuesday 18 August 2009

One-Way SSL with JAX-WS using JDeveloper 11gR1 and WLS 10.3.1

A while back Gerard Davison blogged some simple examples of using WS-Security Policies. Gerard's specific example dealt with the WLS policy Wssp1.2-2007-Wss1.1-UsernameToken-Plain-X509-Basic256.xml. As Gerard notes the said policy (further documented in the WLS 10.3.1 doco here) implements user name tokens, encryption of the tokens and signing of the whole SOAP payload.

The following post strips back Gerard's example to instead to consider the steps in setting up and testing One-Way SSL for a JAX-WS web service generated via JDeveloper 11gR1 and installed in WLS 10.3.1, using the WLS policy Wssp1.2-2007-Https.xml.

Assumptions

This article assumes the reader has the following basic knowledge:

* HTTPS/SSL
* Digital certificates and trusted/certificate authorities (CAs)
* Oracle's WebLogic Server, WLS managed servers and the WLS console

One-Way SSL vs Two-Way SSL

For those not familiar with either, Oracle's WLS documentation has a good explanation of the implementation of and differences between One-Way SSL and Two-Way SSL in the Understanding Security for Oracle WebLogic Server manual.

Steps

To implement a One-Way SSL example we'll run through the following steps:

1) Create a basic JAX-WS web service with JDeveloper 11gR1
2) Generate the digital certificates required for the WLS server
3) Modify the web service to use the Wssp1.2-2007-Https.xml WLS policy
4) Deploy the running web service to WLS
5) Test the running web service via JDeveloper's HTTP Analyzer
6) Test the running web service via SoapUI
7) Test the running web service via a JAX-WS client
8) Inspect the web service packets on the wire to verify the traffic is indeed encrypted

1) Create a basic JAX-WS web service with JDeveloper 11gR1

This step is documented in a previous blog post Creating JAX-WS web services via a WSDL in JDev 11g. There are also a number of viewlet demonstrations available from Oracle's OTN which show how to construct the WSDL in a drag'n'drop fashion.

The resulting web service we'll demonstrate here is a very simple one. It is comprised of the following solutions:

OneWaySSLExample.xsd

targetNamespace="http://www.sagecomputing.com.au" elementFormDefault="qualified">


The inputElement and the outputElement will constitute the incoming and outgoing payloads of a simple HelloWorld web service.

OneWaySSLExample.wsdl

xmlns:tns="urn:OneWaySSLExample.wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsca="http://www.sagecomputing.com.au">



































The overall web service comprises of a single operation accepting the inputElement and outputElement strings as specified in the XSD.

OneWaySSLPortTypeImpl.java
package au.com.sagecomputing.ws;

import javax.jws.WebService;

import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;

@WebService(serviceName = "OneWaySSLService",
targetNamespace = "urn:OneWaySSLExample.wsdl",
portName = "OneWaySSLPortTypePort",
endpointInterface = "au.com.sagecomputing.ws.OneWaySSLPortType",
wsdlLocation = "/WEB-INF/wsdl/OneWaySSLExample.wsdl")
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public class OneWaySSLPortTypeImpl {

public String oneWaySSLOperation(String part) {
return "Hello " + part;
}
}
A very basic JAX-WS web service accepting the inputElement String and returning the outputElement String prefixed with "Hello ".

Example request SOAP payload



Chris



Example response SOAP payload



Hello Chris

The overall application/project structure will look as follows in JDeveloper's Application Navigator:


2) Generate the digital certificates required for the WLS server

In order for a client to undertake a SSL connection with our web service on the WLS server, the WLS server must be configured with a valid digital certificate.

Again note from the Oracle documentation how One-Way SSL works at runtime:

With one-way SSL authentication, the target (the server) is required to present a digital certificate to the initiator (the client) to prove its identity. The client performs two checks to validate the digital certificate:

1. The client verifies that the certificate is trusted (meaning, it was issued by the client's trusted CA), is valid (not expired), and satisfies the other certificate constraints.
2. The client checks that the certificate Subject's common name (CN) field value matches the host name of the server to which the client is trying to connect

If both of the above checks return true, the SSL connection is established.


In this section we consider the digital certificates required for the WLS server.

WLS is an interesting application server in that it keeps two separate Java keystores, 1 for storing the digital certificates for such actions as SSL, and another which is typically used for storing CA digital certificates. The former is referred to as the identity keystore, the later the trust keystore.

The WLS manual Securing Oracle WebLogic Server section 11 Configuring Identity and Trust has a detailed explanation of this setup.

By default WLS comes with demonstration identity and trust keystores containing demonstration digital certificates. As the WLS documentation takes great pains to explain these are for development purposes only and should never be used in a production environment. For the purposes of this blog post if you're testing One-Way SSL in a development environment you can in fact skip this entire step as the demonstration WLS keystores will suffice.

To check that the demonstration keystores are currently installed login to your WLS console, select your server, and under the Configurations -> Keystores tab you'll see the following entries:


Your entries for the file locations of the keystore will be different from my example here dependent on where you installed WLS.

However using the demonstration keystores avoids the whole learning exercise of configuring your own custom digital certificates in WLS which is an important lesson. The following describes those steps in detail, as based off Gerard's original post.

To install our own digital certificate we followed these general steps:

a) Open a command prompt and set the WLS environment
b) Generate our own trusted certificate authority digital certificate
c) Store the private key and digital certificate and import into the identity keystore
d) Store the same digital certificate into the trust keystore.
e) Configure the new keystores in WLS's identity and trust keystore

The following describes those steps in detail. In order to do this we've used WLS utilities to do as much of the work as possible.

a) Open a command prompt and set the WLS environment

Under Windows open a command prompt on the same machine as where WLS is installed, create a temporary directory in your favourite place and cd to that directory, and run your WLS server's setDomainEnv.cmd command. Something like:

"C:\\setDomainEnv.cmd"

Once run ensure you're still in your new directory.

b) Generate our own trusted certificate authority digital certificate
java utils.CertGen -certfile ServerCACert -keyfile ServerCAKey -keyfilepass ServerCAKey -selfsigned -e somebody@xxxx.com.au -ou FOR-DEVELOPMENT-ONLY -o XXXX -l PERTH -s WA -c AU


This generates 4 files: ServerCACert.der, ServerCACert.pem, ServerCAKey.der, ServerCAKey.pem

The utils.CertGen utility is useful for development purposes, but as per the WLS documentation, should not be used for production purposes. Alternatively OpenSSL could be used instead.

Note the use of selfsigned flag. This implies this digital certificate will be used both as the CA in the trust keystore and the served digital certificate in the identity keystore. This is not what we'd do for a production environment using commercial Certificate Authorities, but is sufficient for demonstration purposes in this post.

More information on:

* the WLS CertGen utility can be found here.
* .der vs .pem files can be found here and here.
* WLS provides two utilities der2pem and pem2der can be used to convert between the two file types.

Under Windows you can double click on the ServerCACert.der file to show its contents:


If you have access to the openSSL command line tool you can use it to query the certificate we just created:
openssl x509 -text -inform der -in ServerCACert.der

Certificate:
Data:
Version: 3 (0x2)
Serial Number:
0d:a9:d1:4a:0f:0b:b2:61:13:90:89:f5:40:4d:4f:e2
Signature Algorithm: md5WithRSAEncryption
Issuer: C=AU, ST=WA, L=PERTH, O=SAGECOMPUTING, OU=FOR-DEVELOPMENT-ONLY, CN=/emailAddress=somebody@sagecomputing.com.au
Validity
Not Before: Jul 9 07:06:49 2009 GMT
Not After : Jul 10 07:06:49 2029 GMT
Subject: C=AU, ST=WA, L=PERTH, O=SAGECOMPUTING, OU=FOR-DEVELOPMENT-ONLY, CN=/emailAddress=somebody@sagecomputing.com.au
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:df:cb:6c:ed:86:75:4c:5b:66:cd:aa:3d:34:8f:

73:f6:9c:b5:ed:82:9c:c3:15
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Certificate Sign
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:1
Signature Algorithm: md5WithRSAEncryption
b7:fa:1b:8f:c4:ee:af:6b:1d:f0:dc:f4:cf:35:20:f1:df:eb:

0c:fe
-----BEGIN CERTIFICATE-----
MIIC8zCCAlygAwIBAgIQDanRSg8LsmETkIn1QE1P4jANBgkqhkiG9w0BAQQFADCB

i7Pd63d03mWkI85tvsr5Q+40yitOL5JnLsbyHSrM+1aK8kkY7Qz+
-----END CERTIFICATE-----


This identifies information that maybe useful later if we make a mistake, such as the encryption algorithm used (RSA), the size of the keys (1024bit), the serial number of the certificate (a hex number).

c) Store the private key and the digital certificate in the identity keystore

java utils.ImportPrivateKey -certfile ServerCACert.der -keyfile ServerCAKey.der -keyfilepass ServerCAKey -keystore ServerIdentity.jks -storepass ServerCAKey -alias identity -keypass ServerCAKey


d) Store the same digital certificate into the trust keystore
Import the certificate generated in step b into a trust keystore.

keytool -import -v -trustcacerts -alias identity -file ServerCACert.der -keystore ServerTrust.jks -storepass ServerTrustStorePass


e) Configure the new keystores in WLS's identity and trust keystore

To configure the keystores in WLS enter the WLS console, select the managed server you're interested in, then make the following changes under the following tabs:

Configuration tab -> General subtab

SSL Listed Port Enabled = checkbox
SSL Listen Port = 7102 (and different from the Listen Port)

Configuration tab -> Keystores subtab

Keystores = Custom Identity and Custom Trust
Custom Identity Keystore = \ServerIdentity.jks, such as c:\temp\ServerIdentity.jks
Custom Identity Keystore Type = jks
Custom Identity Keystore Passphrase = ServerCAKey
Confirm Custom Identity Keystore Passphrase = ServerCAKey

Custom Trust Keystore = \ServerTrust.jks, such as c:\temp\ServerTrust.jks
Custom Trust Keystore Type = jks
Custom Trust Keystore Passphrase = ServerTrustStorePass
Confirm Custom Trust Keystore Passphrase = ServerTrustStorePass

Configuration tab -> SSL subtab

Identify and Trust Locations = Keystores
Private key alias = identity
Private Key Passphrase = ServerCAKey
Confirm Private Key Passphrase = ServerCAKey

Then save.

After this restart your WLS server and you should see similar messages to the following in the WLS logs:
    


Alternatively is you see the following messages you have made a mistake in your configuration:
10/07/2009 4:08:30 PM WST>     
<10/07/2009 4:08:30 PM WST>
<10/07/2009 4:08:30 PM WST>


3) Modify the web service to use the Wssp1.2-2007-Https.xml WLS policy

This can be done in a number of ways in JDeveloper, the easiest of which for this blog post at least is just to insert the @Policy annotation into the JAX-WS endpoint as follows:

(Note if you're using earlier versions of JDeveloper or Eclipse, this mechanism wont work, you must manually add the policies to the WSDL).
package au.com.sagecomputing.ws;

import javax.jws.WebService;

import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;

import weblogic.jws.Policy;

@WebService(serviceName = "OneWaySSLService",
targetNamespace = "urn:OneWaySSLExample.wsdl",
portName = "OneWaySSLPortTypePort",
endpointInterface = "au.com.sagecomputing.ws.OneWaySSLPortType",
wsdlLocation = "/WEB-INF/wsdl/OneWaySSLExample.wsdl")
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
@Policy(uri = "policy:Wssp1.2-2007-Https.xml")
public class OneWaySSLPortTypeImpl {

public String oneWaySSLOperation(String part) {
return "Hello " + part;
}
}
4) Deploy the running web service to WLS

Within JDeveloper to deploy and run from the integrated WLS, it's simply a case of right clicking on the JAX-WS file and selecting Run.

If you click on the hyperlink provided in the log window, this will open the HTTP Analyzer. From the HTTP Analyzer you can open the WSDL at the top of the window:



































Note on deployment to WLS you can see that the Wssp1.2-2007-Https.xml policy has been added to the binding to enforce One-Way SSL, and in addition the service address now runs from HTTPS, not HTTP, on the now enabled SSL port.

5) Test the running web service via JDeveloper's HTTP Analyzer

JDeveloper out of the box includes HTTP Analyzer for testing your web services. It's particularly useful as you don't have to leave the confines of your IDE to test your web services.

In order to run the HTTP Analyzer with SSL'ed web service traffic, you need to make some changes to the configuration of JDeveloper. Selecting the Tools->Preferences menu option, followed by Https and Truststore Settings, you can configure the Client and Server keystores HTTP Analyzer needs to run with SSL.

If you followed my exact instructions on setting up a selfsigned CA into the WLS identity and trust keystores, you need to enter the following options in the Preferences Https and Trusting Settings page:

Client Trusted Certificate Keystore: c:\temp\ServerTrust.jks
Client Trusted Keystore Password: ServerTrustStorePass

Server Keystore: c:\temp\ServerIdentity.jks
Server Keystore Password: ServerCAKey
Server Private Key Password: ServerCAKey

When you run your web service you can access the HTTP Analyzer by clicking on the URL of your served web service in the JDev IDE log window, among other methods.


This presents the following HTTP Analyzer screens:


In the top of the screen you'll see the HTTP Analyzer has formed a dummy request for you to send out based on the web service's WSDL. In my example picture I've filled out the part field and pressed Send Request, of which you can see the reply from the web service on the right hand side.

At the bottom of the screen you can the individual request/responses that were generated in order to service the request.

6) Test the running web service via SoapUI

SoapUI is a popular web service testing tool. I wanted to show how to configure it here to show similar results to the HTTP Analyzer. The following steps were built with SoapUI v3.0.

a) Create a new Project via File -> New soapUI Project
b) In the New SoapUI Project dialog, enter a custom project name, then your WSDL, leave the rest of the fields as default.


c) In the Project list expand your new project to the last Request 1 node, and double click it.
d) This will open the Request 1 window, showing on the left handside the outgoing request payload, where you can modify the inputElement XML element with your name.
e) Pressing the green arrow executes the request against the webservice, you'll now hopefully see the SOAP response on the right handside of the window.
f) Note at the bottom right of the right handside of the window you have the text SSL Info. Clicking on this shows another sub-window with the SSL certificate information that was swapped with the WLS server to undertake the SSL communications.


7) Test the running web service via a JAX-WS client

Assuming under JDeveloper you know how to create a Java Proxy for the deployed web service, you'll end up with the following code:
import clientexamples.SSLUtilities;

import javax.xml.ws.WebServiceRef;

public class OneWaySSLPortTypePortClient
{
@WebServiceRef
private static OneWaySSLService oneWaySSLService;

public static void main(String [] args)
{
oneWaySSLService = new OneWaySSLService();
OneWaySSLPortType oneWaySSLPortType = oneWaySSLService.getOneWaySSLPortTypePort();

SSLUtilities.trustAllHttpsCertificates();

System.out.println(oneWaySSLPortType.oneWaySSLOperation("Chris"));
}
}
Note SSLUtilities is a handy class written by Srgjan Srepfler that includes a number of methods for handling and modifying the default SSL behaviour. In our case in writing a simple test client we're not overly concerned about trusting the server's CA, so we can use SSUtilities.trustAllHttpsCertificates to stop the required checking.

8) Inspect the web service packets on the wire to verify the traffic is indeed encrypted

What neither JDeveloper's HTTP Analyzer nor SoapUI can do is actually confirm for you that the traffic on the network was actually encrypted. To check this we can use a wire sniffing tool called WireShark.

Warning: at some sites using wire sniffing tools like WireShark can be a dismissible offence because you can see private data on the network. Be careful to check your organisation policies before doing this.

Note if you're running the JAX-WS web services via the integrated WLS on the same localhost as SoapUI, you're most likely running through the localhost address. For various technical reasons WireShark cannot sniff packets through localhost or the MS loopback adapter in Windows. Instead we must separate our WLS and SoapUI installations, and place them on different hosts. Let's call them Box1 and Box2, with WLS and SoapUI installed respectively

Once you have both up and running, determine the IP address of Box2. Let's say that IP address was: 101.102.103.104

a) Start WireShark. In the filter box top left enter: ip.addr == 101.102.103.104
b) Select the filter Apply button.
c) Select the Capture -> Interfaces
d) Select the Start button for your ethernet card
e) WireShark is now sitting listening for traffic from the other ip.address of Box2.

f) Now in SoapUI execute the request.
g)In WireShark you should see the incoming requests:


As WireShark works at the network level it sees the individual packets, several of which will comprise the request/response between SoapUI and WLS, effectively an incredible amount of detail. You can select each packet and look at the data contained within in the bottom window of the display. This window shows the data in both hex and raw text, so you'll need to carefully look to see the data contained within. Obviously if the traffic is encrypted you wont see much meaning at all which is what we want! To see the unencrypted traffic, remove the policy from your web service, redeploy it and run the same scenario again.

Thanks

I must aim my very strong thanks to Gerard Davison from Oracle UK with assistance with this article, Gerard's help has been invaluable. Any mistakes in this post are of course mine however, of which I'm sure there will be a few in such a long post.

4 comments:

Gerard Davison said...

Came to be after your post, another way of seeing the HTTPS traffic is using the "-Djavax.net.debug=ssl" system property.

This dumps out all of the certs and all of the traffic to the console, and can be useful when debugging ssl issues.

Gerard

Pas Apicella said...

Thanks chris works well. I did find that I needed to add the following java options to my client to avoid this run time error.

Error
-----

Exception in thread "main" javax.xml.ws.WebServiceException: weblogic.wsee.wsdl.WsdlException: Failed to read wsdl file from url due to -- javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
at weblogic.wsee.jaxws.spi.WLSProvider.readWSDL(WLSProvider.java:322)


Solution
--------

-Djavax.net.ssl.trustStore=D:\jdev11gr1\jdeveloper\jdev\mywork\OneWaySSLWebService\SecureProxyClient\keys\ServerTrust.jks -Djavax.net.ssl.trustStoreType=jks -Djavax.net.ssl.trustStorePassword=ServerTrustStorePass

Chris Muir said...

For reference as passed on by Pas, the following Metalink notes exist:

1. I have created a note for gerards demo.

Note.882585.1 Securing a JAX-WS Web Service Within JDeveloper 11g R1 Using X509 Token Policy

2. Now I have created a note on your one which won't be visible for a few more hours.

Note.884650.1 Securing a JAX-WS Web Service Within JDeveloper 11g R1 Using One way SSL

schrepfler said...

Thanks for the reference! :)