Friday, October 13, 2006

Using HTTPS with Web Services

Prerequisites:
In this article you have

  • already a Web Service deployed in OC4J that is running on the default HTTP port. The WSDL and Endpoint are available. In my sample the non secure Web Service endpoint is: http://127.0.0.1:8888/math-service/MathServiceSoapHttpPort

Add HTTPS to OC4J

Creating of the Keystore

The first thing to do to secure OC4J would be to create a new keystore that will contain the different certificates. The easiest way to do that for a Java developer is to use SUN's keytool:
keytool -genkey -alias oracle-server -dname "CN=Tug Grall, OU=Blog O=Grall And Co L=Redwood Shores, S=CA, C=US" -keyalg RSA -keypass welcome -storepass welcome -keystore server.keystore
You can copy the server.keystore into the $ORACLE_HOME/j2ee/home/config to simplify the next steps.


Configuring OC4J

OC4J stand alone is using the notion of Web-Site to expose HTTP resources (Web Applications). The default-web-site is define is he $ORACLE_HOME/j2ee/home/config/default-web-site.xml. To secure an OC4J you can follow the steps describe in the OC4J Security guide that I have summarized in the following section.

What we want to achieve for the purpose of the demonstration is to have OC4J using HTTP and HTTPS, on port 8888 and 4443 for example.

1. Copy default-web-site.xml to  secure-web-site.xml

2. Edit the secure-web-site.xml:
    2.1. Change the web-site tag by changing the port to 4443 and adding the element secure="true"
    2.1. Add the ssl-config element and point this to the new created keystore.
    The file looks like:

<web-site   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="http://xmlns.oracle.com/oracleas/schema/web-site-10_0.xsd"
            port="4443"
            secure="true"
            display-name="OC4J 10g (10.1.3) Default Web Site"
            schema-major-version="10"
            schema-minor-version="0" >
        ...
        <ssl-config keystore="server.keystore" keystore-password="welcome" />
        ...
</web-site>

3. Import the new Web site in your OC4J instance by editing the $ORACLE_HOME/j2ee/home/server.xml file. You need to add or replace the web-site tag. In my case I want to add the secure web site to my instance so the configuration looks like:

    ...   
    <web-site default="true" path="./default-web-site.xml" />
    <web-site path="./secure-web-site.xml" />
    ...

Since we have copied the file from the default-web-site, all applications are available using HTTP and HTTPS

Start OC4J and test the HTTPS port

Start OC4J using the standard Java command or shell script, I am adding the Java Network debug flag that would help you to see what is happening at the SSL level.

    java -Djavax.net.debug=ssl -jar oc4j.jar

You should be able to access the service WSDL using the HTTPS port for example in my case:
  •     https://127.0.0.1:4443/math-service/MathServiceSoapHttpPort?WSDL


Consuming the Service using HTTPS

Generate and configure a client Keystore

Event if this is possible to use the same keystore for the server and the client, I will guide you in the steps to create a client certificate and import the certificate from the existing -server- one. Here the command to create a new keystore:
keytool -genkey -alias oracle-client -dname "CN=John Doe, OU=Blog O=MyDummyClient, S=CA, C=US" -keyalg RSA -keypass welcomeClient -storepass welcomeClient -keystore client.keystore

The next step is to export the certificate from the server keystore to be able to import it in the client:
keytool -keystore server.keystore -export -alias oracle-server -file server.cer

You can now import the cerificate in the client keystore:
keytool -keystore client.keystore -import -file server.cer

Generate the proxy

You have now the client certificate so you can use the Oracle Web Service Assembler to generate the proxy. The only specific thing you have to do is to specify which key store to use when running the tool. The command to use when generating the proxy is:

java -Djavax.net.ssl.trustStore=/Users/tgrall/ssl/client.keystore
     -Djavax.net.ssl.keyStore=/Users/tgrall/ssl/client.keystore
     -Djavax.net.ssl.trustStorePassword=welcomeClient
     -Djavax.net.ssl.keyStorePassword=welcomeClient
     -jar $ORACLE_HOME/webservices/lib/wsa.jar
     -genProxy
     -wsdl https://127.0.0.1:4443/math-service/MathServiceSoapHttpPort?WSDL


Calling the Service using secure endpoint

Configure the Java Environment to use the client store is made using the following System properties:
  • javax.net.ssl.trustStore
  • javax.net.ssl.keyStore
  • javax.net.ssl.trustStorePassword
  • javax.net.ssl.keyStorePassword
       
This could be done using different approach, property file, -D command line parameter or programmatically. To simply the example I am using the programmatic approach, the following code is part of the main method of the Client class:
  ...
   System.setProperty("javax.net.ssl.trustStore", "/Users/tgrall/ssl/client.keystore");
   System.setProperty("javax.net.ssl.keyStore", "/Users/tgrall/ssl/client.keystore");
   System.setProperty("javax.net.ssl.trustStorePassword", "welcomeClient");
   System.setProperty("javax.net.ssl.keyStorePassword", "welcomeClient");
   ...
   // Adding Debug information
   
System.setProperty("javax.net.debug", "ssl");
   ...


It is possible to change the Endpoint dynamically in the Proxy using the setEndpoint method.
  ...
  democlient.proxy.MathServiceSoapHttpPortClient myPort = new democlient.proxy.MathServiceSoapHttpPortClient();
  ...
  String ep = "https://127.0.0.1:4443/math-service/MathServiceSoapHttpPort";
  myPort.setEndpoint(ep);
  System.out.println("Result of the operation is "+ myPort.add(2,2));
  ...


You should now be able to run the client and call the service using HTTPS. This would look like:

10 comments:

Yogesh Chaudhari said...

Hi, I trying to secure Web Service.
I created the Keystore and configure OC4J (10.1.3.2) but I am getting following error message when I tried to access from SSL port (443)

https://nyhdev50n.adr.org/calc/MathSoapHttpPort

404 Not Found
Resource /calc/MathSoapHttpPort not found on this server

Other applications are available from SSL port.

Also Web service is accessible from non-SSL port.

Could you please suggests any idea?

Thanks,
Yogesh Chaudhari

thirtha said...

Hi, i am facing the same problem.
I created secure-web-site.xml, modified server.xml as per the above procedure explained. Now when i am trying to access my webservice through https, i am getting "404 Not Found" but the same webservice can be accessible through non SSL mode(http). but i could open the homepage i.e(https://localhost:4443/em/console) of Jdeveloper successfully.
where might i have gone wrong, Please help

D.Schroff said...

Hi Tug,

i tried to configure HTTPS for a webservice with the help of this blog entry.
But it does not work....
Is this only for standalone oc4j?
I changed the configuration of the application server but if i try to invoke the webservice i get an Oracle test certificate and not the one created with the keytool command.

Thanks...

Lavnish said...

Hi any idea how to do the same in oc4j 11g .. as the tech preview 3 is available now

Lavnish said...

any pointers how to do same on oc4j 11g tech preview 3 ??

Tug said...

Lavnish

I have to say that since I have left Oracle I did not spend enough time on the different developer previews. But it should be more or less similar.

If you want to do it in a simpler way, just put Apache and mod_proxy in front :) This could be a good shortcut for the various Developer Previews

Tug

Anonymous said...

Great tutorial! very useful! Thank you so much!

Yogesh Gandhi said...

Thanks for this tutorial, This might help me out.

But in real scenario, I have to make a call to a webservice on https and my client is WAS 7.0

I have few questions, I'll be obliged, if anyone of you can answer my questions

1) Exact steps needed to install certificate on client
2) Is it really required that WSDL URL should be accessible from client's machine? I mean is it possible that the server guys have protected their WSDL and not anyone can access it?
3) Can I make the path to WSDL configurable? As I see, the path to WSDL gets hard-coded in annotations. Is there a way to make it configurable. My client demands it.

Any information on the above points might prove very useful to me, if you can help.

Yogesh Gandhi said...

System.setProperty("javax.net.ssl.trustStore", "/Users/tgrall/ssl/client.keystore");
System.setProperty("javax.net.ssl.keyStore", "/Users/tgrall/ssl/client.keystore");

you had set trustStore and keyStore to the client.keystore

Do they always point to the same file always and the file name is usually client.keystore? because the file I got from my client is also client.keystore.

Yogesh Gandhi said...

Well setting the javax.net.ssl.trustStore property might work for stand-alone java program. but it didn't work for me when the program invocation was done by websphere.

I had tried setting the properties in System class but of no use. Finally I had to add the certificate in Websphere UI.

Here goes the details of the problem and the solution :

http://www.coderanch.com/t/573425/Web-Services/java/javax-net-ssl-SSLException-SSLSocketFactory