Archive

Posts Tagged ‘message_protection’

Run JAX-WS Proxy for message_protection enabled web service with simple JSE Client

October 5, 2012 Leave a comment

One of the most widely used pattern to invoke an ADF BC Web service or a SOA Composite Service from java is JAX-WS Proxy. The case that I am discussing here is invoking a Web service secured with a message_protection policy using a JAX-WS JSE client by using the client policy –  oracle/wss11_saml_or_username_token_with_message_protection_service_policy.

Now to invoke the service secured with above OWSM policy from J2EE client, we can generate a JAX-WS proxy as given on documentation pointed above and use connection.xml as an option to inject end point dynamically by creating an ADF Web Service Connection.

 Context ctx = ADFContext.getCurrent().getConnectionsContext();
 WebServiceConnection wsc = (WebServiceConnection) ctx.lookup("MyAppModuleService");
 MyAppModuleService proxy = wsc.getJaxWSPort(MyAppModuleService.class);
 

This approach of using connections.xml to manage end point would work only when your JAX-WS proxy code is going to executed in a J2EE Application context where the app knows about connections.xml. But if you execute the same API with a simple JSE client, it would fail since a simple java program has no idea of connections.xml. There are two cases that comes to my mind immediately where I want to execute my JAX-WS proxy code in a simple JSE client.

1) During development of my web proxy, I don’t want to run my UI everytime to test the web proxy. Instead I want a simple approach to test my web proxy code.
2) I am exposing a service from my product secured with oracle/wss11_saml_or_username_token_with_message_protection_service_policy which wouldn’t allow me to test with default end point tester. Hence if I want to write some automated test mechanism for my service, then a simple JSE client comes very handy to test message_protection enabled services.

Now this can be done in two ways. But in each, the complication lies in how we will be applying the client policy and other request parameters for encryption and signature requirement for message_protection_client policy.


Prerequisite: This requires some security related files to be pre-configured to inject the client policy in a JSE client. Hence you need to configure jps-config.xml and default-keystore.jks. jps-config.xml to inject the keystore and default-keystore.jks that would have the encryption keys.

Copy the default-keystore.jks: Download the default-keystore.jks file from this location to a location where the JSE client can access.

Setting up jps-config.xml: Store the contents of below file in a location as jps-config.xml. Make sure to replace the “keystore” serviceInstance tag location value with the location where the default-keystore.jks file has been saved.

<?xml version = '1.0' encoding = 'UTF-8'?>
 <jpsConfig xmlns="http://xmlns.oracle.com/oracleas/schema/11/jps-config-11_1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/oracleas/schema/11/jps-config-11_1.xsd jps-config-11_1.xsd">
    <serviceProviders>
       <serviceProvider type="CREDENTIAL_STORE" name="credstore.provider" class="oracle.security.jps.internal.credstore.ssp.SspCredentialStoreProvider">
          <description>Credential Store Service Provider</description>
       </serviceProvider>
       <serviceProvider class="oracle.security.jps.internal.keystore.KeyStoreProvider" name="keystore.provider" type="KEY_STORE">
         <description>PKI Based Keystore Provider</description>
         <property value="owsm" name="provider.property.name"/>
       </serviceProvider>
    </serviceProviders>
    <serviceInstances>
       <serviceInstance name="credstore" provider="credstore.provider">
          <property name="location" value="./"/>
       </serviceInstance>
       <serviceInstance location="D:\default-keystore.jks" provider="keystore.provider" name="keystore">
         <description>Default JPS Keystore Service</description>
         <property value="JKS" name="keystore.type"/>
         <property value="oracle.wsm.security" name="keystore.csf.map"/>
         <property value="keystore-csf-key" name="keystore.pass.csf.key"/>
         <property value="sign-csf-key" name="keystore.sig.csf.key"/>
         <property value="enc-csf-key" name="keystore.enc.csf.key"/>
      </serviceInstance>
    </serviceInstances>
    <jpsContexts default="Application1">
       <jpsContext name="Application1">
          <serviceInstanceRef ref="credstore"/>
          <serviceInstanceRef ref="keystore"/>
       </jpsContext>
    </jpsContexts>
 </jpsConfig>

Find the keystore password: The password for the default-keystore.jks give above is welcome1. But in case if you want to find it yourself, you can get it using below command.
This should be executed after setting java path.

 set PATH=C:\Program Files\Java\jre6\bin
 keytool -list -v -keystore default-keystore.jks -storepass welcome1



Option 1: Run the main method on <Port>Client.java file that is generated by JAX-WS Proxy..

While generating JAX-WS proxy, it will generate the <Port>Client.java file which will have a main method. <Port> is the Port defined on your Web service that you can find it on the WSDL <service> tag.

In this main method, just add the WS call, run the main method and see what happens. Eg,

 @WebServiceRef
 private static EmpDeptBCService_Service empDeptBCService_Service;

 private static final AddressingVersion WS_ADDR_VER = AddressingVersion.W3C;

 public static void main(String[] args) {
     empDeptBCService_Service = new EmpDeptBCService_Service();

     BindingProvider wsbp = (BindingProvider)empDeptBCService;

     Map<String, Object> requestContext = wsbp.getRequestContext();
     requestContext.put(BindingProvider.USERNAME_PROPERTY, "weblogic");
     requestContext.put(BindingProvider.PASSWORD_PROPERTY, "weblogic1");

     try {
         EmployeesVOSDO emp = empDeptBCService.getEmployeesVO1(201);
         System.out.println(emp.getFirstName().getValue());
     } catch (ServiceException e) {
         e.printStackTrace();
     }
 }

Above code snippet will throw Invalid security error. Because this JSE client does not have any idea about FMW configuration files like connections.xml, jps-config.xml or default-keystore.jks which are responsible for injecting message encryption, OWSM client policies etc at runtime in a J2EE client.

To make this JSE client on <Port>Client.java work, in this main method, we can attach the client policy, inject the jps-config.xml and default-keystore.jks and set the required keystore and encryption keys required for web service call encryption. Finally invoking this main method will invoke the service. Hence I modify the code as below.

 @WebServiceRef
 private static EmpDeptBCService_Service empDeptBCService_Service;

 private static final AddressingVersion WS_ADDR_VER = AddressingVersion.W3C;

 public static void main(String[] args) {
     SecurityPolicyFeature[] securityFeature =
         new SecurityPolicyFeature[] { new SecurityPolicyFeature("policy:oracle/wss11_username_token_with_message_protection_client_policy") };

     EmpDeptBCService empDeptBCService =
         empDeptBCService_Service.getEmpDeptBCServiceSoapHttpPort(securityFeature);

     BindingProvider wsbp = (BindingProvider)empDeptBCService;

     Map<String, Object> requestContext = wsbp.getRequestContext();
     requestContext.put(BindingProvider.USERNAME_PROPERTY, "weblogic");
     requestContext.put(BindingProvider.PASSWORD_PROPERTY, "weblogic1");

     System.setProperty("oracle.security.jps.config","D:\\jps-config.xml");

     requestContext.put(ClientConstants.WSS_KEYSTORE_TYPE, "JKS");
     requestContext.put(ClientConstants.WSS_KEYSTORE_LOCATION, "D:\\default-keystore.jks");
     requestContext.put(ClientConstants.WSS_KEYSTORE_PASSWORD, "welcome1");

     try {
         EmployeesVOSDO emp = empDeptBCService.getEmployeesVO1(201);
         System.out.println(emp.getFirstName().getValue());
     } catch (ServiceException e) {
         e.printStackTrace();
     }
 }

Option 2: Invoke using ServiceDelegate

ServiceDelegate is an alternate option to obtain port to the service created through JAX-WS proxy and invoke the service end point. Below is the sample JSE model of invocation of the service using the JAX-WS Proxy with ServiceDelegate as JSE client.

public class InvokeUsingServiceDelegate {
 public InvokeUsingServiceDelegate() {
 super();
 }

 private static final String serviceNamespace = "http://go2kavinkumar.com/adf/sample/model/applicationModule/common/";
 private static final String serviceName = "EmpDeptBCService";
 private static final String servicePort = serviceName + "SoapHttpPort";

 public static void main(String... args) {
     InvokeUsingServiceDelegate invokeUsingServiceDelegate = new InvokeUsingServiceDelegate();

     String endPoint ="http://localhost:7101/empDeptService/EmpDeptBCService?WSDL";
     URL wsdlUrl=null;

     try {
         wsdlUrl = new URL(endPoint);
     } catch (MalformedURLException e) {
         e.printStackTrace();
     }
     ServiceDelegateImpl serviceDelegate = new ServiceDelegateImpl(wsdlUrl, new QName(serviceNamespace,serviceName),oracle.webservices.OracleService.class);
     EmpDeptBCService port = serviceDelegate.getPort(new QName(serviceNamespace, servicePort), EmpDeptBCService.class);

     BindingProvider wsbp = (BindingProvider)port;

     Map<String, Object> requestContext = wsbp.getRequestContext();
     requestContext.put(BindingProvider.USERNAME_PROPERTY, "weblogic");
     requestContext.put(BindingProvider.PASSWORD_PROPERTY, "weblogic1");

     System.setProperty("oracle.security.jps.config", "D:\\jps-config.xml");

     try {
         InputStream clientPolicy = EmpDeptBCService.class.getResourceAsStream("webservices-client.xml");
         requestContext.put(oracle.webservices.ClientConstants.CLIENT_CONFIG, fileToElement(clientPolicy));
     } catch (IOException e) {
         e.printStackTrace();
     } catch (Exception e) {
         e.printStackTrace();
     }

     requestContext.put(ClientConstants.WSS_KEYSTORE_TYPE, "JKS");
     requestContext.put(ClientConstants.WSS_KEYSTORE_LOCATION, "D:\\default-keystore.jks");
     requestContext.put(ClientConstants.WSS_KEYSTORE_PASSWORD, "welcome1");

     try {
         EmployeesVOSDO emp = port.getEmployeesVO1(201);
         System.out.println(emp.getFirstName().getValue());
     } catch (ServiceException e) {
         e.printStackTrace();
     }
 }

 private static Element fileToElement(InputStream f) throws IOException, Exception{
     DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
     builderFactory.setValidating(false);
     builderFactory.setNamespaceAware(true);
     builderFactory.setIgnoringElementContentWhitespace(true);
     builderFactory.setIgnoringComments(true);
     return builderFactory.newDocumentBuilder().parse(f).getDocumentElement();
 }
 }

While using the ServiceDelegate approach, the client policy has to be has to be physically attached with it using CLIENT_CONFIG properly in the request context. Hence it is obtained from below file in above code.

webservices-client.xml content:

 <oracle-webservice-clients>
 <webservice-client>
 <port-info>
 <policy-references>
 <policy-reference uri="oracle/wss11_username_token_with_message_protection_client_policy" category="security"/>
 </policy-references>
 </port-info>
 </webservice-client>
 </oracle-webservice-clients>

As you can see in above cases, we just injected the jps-config.xml and the default-keystore.jks with the keystore password. That has taken care of the message encryption/signature for message_protection.

In case if the service is secured with non-message_protection policy like “wss_saml_or_username_token_service_policy”, then just using respective client policy as SecurityPolicyFeature in option 1 or in webservices-client.xml in option 2 would have worked. We don’t need manual injection of jps-config.xml or default-keystore.jks as those are required only for message encryption or signing when there is message_protection involved.


Thanks,
Kavin.