Using soapUI for secure, asynchronous web service invocations in Fusion Applications   

Using secure, asynchronous web services

Fusion Applications exposes across all of its product families numerous web services that allows for querying, creating and updating of business objects. In this blog we will show how to leverage these services in a secure, asynchronous fashion from a web service client tool such as soapUI.

Asynchronous Web Service Invocation

While invoking services in a synchronous way over SSL is relatively straight forward since the client takes care of establishing the secure communication over SSL and then waiting for the response, the situation is very different for asynchronous communication. In the asychronous case, the initial communication terminates with Fusion Applictions persisting the incoming message in a queue. The response back to the client is done over a new channel, this time initiated by Fusion Applications once the response has been created and is ready for delivery.

Why asynchronous invocations at all?

One could argue why would we go the asynchronous route at all given that the same service operations are exposed as synchronous operations as well. Well, there are many reasons why asynchronous communication would be preferred including

  • long running service invocations would block the client unnecessarily long. In many cases we would prefer to proceed on the client side not having to wait for the service invocation to complete.
  • better scalability both on the client and on the server side since message processing happens when resources become available, not when the request is being initiated.
  • overall higher throughput due to better load distribution over time

The implementation of asynchronous web services in Fusion Applications is discussed in this follow-up blog.

The synchronous case

Usually it makes sense to first make sure the service invocation works in a synchronous case so that we can rule out any issues with the actual payload later on.

For this example we want to query customers from the CRM module. Here is a sample record that we entered in Fusion Applicatons CRM that we want to retrieve later through the web service:

FA Customer Page

In order to find the actual web service the best way is to use the public web site hosting all Fusion Applications web services (and many other things): https://fusionappsoer.oracle.com. This leads us to the ‘Sales Party’ ADF based web service and its corresponding wsdl URL:

Fusion APIs documented in OER

After setting the proper hostname in the template URL we get access to the actual wsdl at https://crm.fa-integration.com/crmCommonSalesParties/SalesPartyService?WSDL.

Creating a soapUI project based on a provided wsdl is very straight-forward:

Screenshot from 2014-02-07 08:08:42

After the import of the wsdl and the referenced schemas we can expand the SalesPartySoapHttp binding, locate the findSalesParty (not findSalesPartyAsync – that’s coming later), right-click and select ‘New Request’. After specifying some name for the request soapUI defaults the payload. We have tweaked the payload accordingly to search for the Customer with name ‘Some Corp’.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <typ:findSalesParty xmlns:typ="http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/types/">
         <typ:findCriteria xmlns:typ1="http://xmlns.oracle.com/adf/svc/types/">
            <typ1:fetchStart>0</typ1:fetchStart>
            <typ1:fetchSize>-1</typ1:fetchSize>
            <typ1:filter>
               <typ1:conjunction>And</typ1:conjunction>
               <typ1:group>
                  <typ1:conjunction>And</typ1:conjunction>
                  <typ1:upperCaseCompare>false</typ1:upperCaseCompare>
                  <typ1:item>
                     <typ1:conjunction>And</typ1:conjunction>
                     <typ1:upperCaseCompare>false</typ1:upperCaseCompare>
                     <typ1:attribute>PartyName</typ1:attribute>
                     <typ1:operator>=</typ1:operator>
                     <typ1:value>Some Corp</typ1:value>
                  </typ1:item>
               </typ1:group>
            </typ1:filter>
         </typ:findCriteria>
      </typ:findSalesParty>
   </soapenv:Body>
</soapenv:Envelope>

Note that soapUI defaults the service endpoint as https://crm.fa-integration.com/crmCommonSalesParties/SalesPartyService according to the wsdl.

Executing this request will not work yet as we have not provided credentials to authenticate the service client. For this we first configure an Outgoing WS-Security Configuration in the soapUI project view. Create a new configuration, provide some name (username_timestamp in our case). Then add in the details panel a new entry of type ‘Timestamp’ and set it the Time To Live to a value such as ‘300’. Add one more entry of type ‘Username’ and populate the Username and Password entry and finally set the Password Type to ‘PasswordText’.

This should look as follows:

Screenshot from 2014-02-07 08:33:17

We can now refer to this outbound configuration in the synchronous request we created earlier. Just refer to this configuration by clicking on ‘Aut’ in the lower left corner of the request window and specify the entry under ‘Outgoing WSS’. If we run the invocation again now we see a proper response in the right panel:

Screenshot from 2014-02-07 12:26:12

By toggling to the ‘Raw’ view of the request we see that soapUI introduces a SOAP header into the payload on the fly including the timestamp and credentials expected by Fusion Applications:

<soapenv:Header>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
         <wsse:UsernameToken wsu:Id="UsernameToken-26" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsse:Username>john.self@fa-integration.com</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">MHOkAlntId98tmBFYRUA5neIGFQ=</wsse:Password>
            <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">LLSfAitLIP9c5JN0DQfsvw==</wsse:Nonce>
            <wsu:Created>2014-02-07T14:08:22.345Z</wsu:Created>
         </wsse:UsernameToken>
         <wsu:Timestamp wsu:Id="Timestamp-25" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsu:Created>2014-02-07T14:08:22.345Z</wsu:Created>
            <wsu:Expires>2014-02-07T14:13:22.345Z</wsu:Expires>
         </wsu:Timestamp>
      </wsse:Security>
   </soapenv:Header>

So now that we know how to successfully execute a sychronous SOAP call over SSL we can now proceed preparing for the asynchronous case.

Decrypting the asynchronous response (non SSL)

Asynchronous Fusion Application web services are generally configured with the Oracle Web Service Manager policy ‘oracle/wss11_saml_token_with_message_protection_client_policy’ attached to the callback. This can be seen in the Callback Client section of the web service configuration in Enterprise Manager:

Screenshot from 2014-02-07 15:45:39

This implies that we not only have to tell Fusion Applications the address for the callback (i.e. where a soapUI mock service is going to wait for a response), but also provide the public key that Fusion apps will be using to encrypt and sign the message before sending it back. For this purpose we need a key pair where we include the public key in the request message and the soapUI mock service will use the private/secret key for decrypting the payload.

There are several tools to create key stores and key pairs like ‘keytool’ shipping with Java distributions, but we are using Portecle here for convenience to create a new keystore (type JKS) with a fresh new, self-signed, certificate (RSA-1024):

Creating a keystore with a self-signed certificate

We save this keystore in a jks file and also copy/paste the PEM encoding of the public key since we are going to include it in our asychronous request.

Now we create a new request similar to the synchronous case except specifying operation findSalesPartyAsync (as opposed to findSalesParty in the sychronous case). Also refer to the Outgoing WSS Configuration again and include the following SOAP header:

   <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
      <wsa:ReplyTo>
         <wsa:Address>http://10.175.49.14:8080/SalesPartyAsyncResponse</wsa:Address>
         <wsid:Identity xmlns:wsid="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
            <dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
               <dsig:X509Data>
                  <dsig:X509Certificate>MIIB8DCCAV..[snip]...kCBFL0kUswDg12s=</dsig:X509Certificate>
               </dsig:X509Data>
            </dsig:KeyInfo>
         </wsid:Identity>
      </wsa:ReplyTo>
   </soapenv:Header>

There are two important parts here. First of all, we specify the ReplyTo address, which is going to be the endpoint where we are going to configure the soapUI mock service to receive the response from Fusion Applications, i.e. the IP address points to the machine hosting soapUI. Note that we do not go via SSL yet here, we specify http as the protocol and the regular listen port of the soapUI mock.

The second part is the inclusion of the PEM encoded certificate we created before. Fusion apps will use this key to encrypt the response and we will need the private key later on to decrypt the response.

Before we can actually create the mock service for receiving the response we need to configure an Incoming WS-Security configuration. As the first step we register the keystore we created earlier under WS-Security Confgurations → Keystores/Certificates in the project view:

Screenshot from 2014-02-07 16:10:14

This keystore is referenced in the next when we create an Incoming WS-Security Configuration:

Screenshot from 2014-02-07 16:12:28

With that in place the last thing we need to do is to create a mock service for the findSalesPartyAsyncResponse operation. Right-click on this operation in the SalesPartyServiceResponseBinding binding and select ‘Add to Mock Service’.

We don’t need to specify any payload in the response, but it’s key to set the path (/SalesPartyAsyncResponse in our case) and the port to the values that we specified in the ReplyTo address in the request. Also we need to set the Incoming WSS’ accordingly so that the incoming payload can be decrypted:

Incoming WSS settings on the mock service

Now after starting our new mock service we are ready to trigger the asynchronous request and receive the response in the mock service:

Screenshot from 2014-02-07 16:35:14

While the response for the request invocation is empty and returns rather fast, we can see an response coming in in the mock a few seconds later. Double-clicking on the Log of the mock service shows the actual message:

Receiving the asynchronous response

It is also worth taking a closer ook at the Raw tab to see how the encrypted message looks like:

Encrypted message sample

With that we have a setup for asynchronous invocations of Fusion Apps, but although the response is encrypted, we are not yet leveraging SSL for the transports level encryption.

Enabling SSL for the asynchronous response

In order to receive the response message over SSL, a few additional setup steps are required. In particular, this includes:

  1. 1. Configure SSL support for mock services in soapUI
  2. 2. Enable SSL for the asynchronous response in Fusion Applications
  3. 3. Register the self-signed certificate in the Fusion Applications trust store

Note that step 3 is only required since we are using a self-signed certificate.

Configuring soapUI for SSL

Similar to the message encryption before, we need to have a certificate that soapUI can use for SSL communication. In this case we are simply using the same certificate again and refer to the same keystore in the SSL preferences in soapUI:

Screenshot from 2014-02-07 16:43:15

This is sufficent for soapUI to start listening on the specified port 8081.

Registering the self-signed certificate on the Fusion Applications environment

In order to make Fusion Applications trust the SSL endpoint in soapUI we have to include our self-signed certificate in the truststore of the Fusion Applications installation. Note that this step is only required since we are using a self-signed certificate.

The location can be determined e.g. in the WebLogic console under the SSL configuration tab of the respective Weblogic managed server:

Screenshot from 2014-02-07 17:09:37

In our case the location of the keystore is /u01/app/fusionapps/config/keystores/fusion_trust.jks. We can use Portecle again to add our self-signed certificate in the truststore and then replace the file on the server.

This time we are using the usual keytool from the JDK to import the certificate into the trust keystore:

[oras8@crm keystores]$ /u01/app/fusionapps/fusionapps/jdk6/bin/keytool -keystore fusion_trust.jks -import -file soapui.crt -alias soapui 
Enter keystore password: 
Owner: CN=SOAPUI, OU=ATEAM, O=ORACLE, C=US
Issuer: CN=SOAPUI, OU=ATEAM, O=ORACLE, C=US
Serial number: 52F4914B
Valid from: Mon Feb 03 10:07:33 EST 2014 until: Sun May 04 11:07:33 EDT 2014 
Certificate fingerprints: 
         MD5:  6C:96:6F:A2:8B:BA:25:24:FA:CE:72:C5:55:80:6F:8F 
         SHA1: 31:E7:74:D6:DA:F3:ED:35:92:AF:D3:E9:9F:EE:BE:3B:C1:B9:E6:DC 
         Signature algorithm name: SHA256withRSA 
         Version: 3 

Extensions: 

#1: ObjectId: 2.5.29.14 Criticality=false 
SubjectKeyIdentifier [ 
KeyIdentifier [ 
0000: 30 5E E9 15 1F E4 77 0B   C2 41 D8 16 32 AE C5 E0  0^....w..A..2... 
0010: 71 FE 97 D1                                        q... 
] 
] 

Trust this certificate? [no]:  yes 
Certificate was added to keystore 
[oras8@crm keystores]$

 

It requires a restart of the managed server to take effect.

Configuring SSL for Asynchronous Web Services in Fusion Applications

Before we can run the first asynchronous test we need to configure SSL for Asynchronous web services in the Fusion Applications as documented in Oracle Fusion Middleware Developer’s Guide for Oracle Infrastructure Web Services. The result looks as follows:

Screenshot from 2014-02-10 09:00:27

Now we are ready to have the callback to the soapUI mock done over SSL and all we need to do is to change the ReplyTo address in the request to refer to https and the corresponding port:

 

<wsa:Address>https://10.175.49.14:8081/SalesPartyAsyncResponse</wsa:Address>

 

From the soapUI perspective there is nothing different compared to the non-SSL execution before since the SSL transport is completely transparent.

Summary

This article covers the following parts in order to secure asynchronous web service invocations with Fusion Applications:

Direction Message Encryption SSL
Request No Yes
Response Yes Yes

 

We are working on adding support for outbound message encryption and will provide this in a later post.

 

Troubleshooting

In the following section provides solutions to common issues when testing asynchronous web services from soapUI.

Current release of soapUI (wss4j stack)

Current releases of soapUI leverage a more recent wss4j implementation (e.g. 1.6.2) leading to the following issue when trying to decrypt a response in the mock service:

2014-02-10 10:48:59,348 ERROR [errorlog] org.apache.ws.security.WSSecurityException: An invalid security token was provided (Bad TokenType "")
org.apache.ws.security.WSSecurityException: An invalid security token was provided (Bad TokenType "")
	at org.apache.ws.security.str.BSPEnforcer.checkEncryptedKeyBSPCompliance(BSPEnforcer.java:115)
	at org.apache.ws.security.str.SignatureSTRParser.processPreviousResult(SignatureSTRParser.java:432)
	at org.apache.ws.security.str.SignatureSTRParser.parseSecurityTokenReference(SignatureSTRParser.java:122)
	at org.apache.ws.security.processor.SignatureProcessor.handleToken(SignatureProcessor.java:140)
	at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:396)
	at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:304)
	at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:249)
	at com.eviware.soapui.impl.wsdl.support.wss.IncomingWss.processIncoming(IncomingWss.java:121)
	at com.eviware.soapui.impl.wsdl.mock.WsdlMockRequest.initPostRequest(WsdlMockRequest.java:134)
	at com.eviware.soapui.impl.wsdl.mock.WsdlMockRequest.<init>(WsdlMockRequest.java:107)
	at com.eviware.soapui.impl.wsdl.mock.WsdlMockRunner.dispatchRequest(WsdlMockRunner.java:368)
	at com.eviware.soapui.monitor.JettyMockEngine$ServerHandler.handle(JettyMockEngine.java:715)
	at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
	at org.mortbay.jetty.Server.handle(Server.java:326)
	at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
	at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:945)
	at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
	at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
	at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
	at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

The workaround is to use an older version of soapUI (e.g. 4.0.1) with the corresponding version of wss4j (e.g. 1.5.8).

Asynchronous SSL not configured in Fusion Applications

The following server side error message is seen if the ssl configuration has not been done of the Fusion Apps instance, the server log (CRMCommonServer_1.out in our case) will contain:

<Feb 3, 2014 10:15:59 AM EST> <Error> <oracle.j2ee.ws.common.jaxws.JAXWSMessages> <BEA-000000> <[MessageID: uuid:0b178f57-2f02-4af2-a19c-6fc2ee22dd56] Could not create SSLSocketFactory.
java.lang.NullPointerException
        at oracle.j2ee.ws.saaj.util.SSLUtil.loadKeyStore(SSLUtil.java:72)
        at oracle.j2ee.ws.saaj.util.SSLUtil.getKeyManagerFactory(SSLUtil.java:87)
        at oracle.j2ee.ws.saaj.util.SSLUtil.getKeyManagers(SSLUtil.java:96)
        at oracle.j2ee.ws.saaj.util.SSLUtil.createSSLSocketFactory(SSLUtil.java:49)
        at oracle.j2ee.ws.server.jaxws.NonAnonymousResponseHandler.getSSLSocketFactory(NonAnonymousResponseHandler.java:538)
        at oracle.j2ee.ws.server.jaxws.NonAnonymousResponseHandler.createDispatch(NonAnonymousResponseHandler.java:385)
        at oracle.j2ee.ws.server.jaxws.NonAnonymousResponseHandler.sendResponse(NonAnonymousResponseHandler.java:93)
        at oracle.j2ee.ws.server.WebServiceProcessor.handleNonAnonymousEPR(WebServiceProcessor.java:589)
        at oracle.j2ee.ws.server.WebServiceProcessor.processRequest(WebServiceProcessor.java:301)
        at oracle.j2ee.ws.server.WebServiceProcessor.doService(WebServiceProcessor.java:195)
        at oracle.j2ee.ws.server.WebServiceServlet.doPost(WebServiceServlet.java:485)

Missing registration of self-signed certificate

If the self-signed certificate was not added to the trust store in Fusion Applications, the following exception can be seen in the server log file:

<Feb 3, 2014 10:16:00 AM EST> <Error> <oracle.wsm.resources.enforcement> <WSM-07503> <Failure in Oracle WSM Agent processFault, category= security, function=agent.function.client, application=CrmCommonApp, composite=null, modelObj=oracle-apps-crmCommon-salesParties-salesPartiesService-applicationModule-server-SalesPartyServiceImpl-_oracleAsyncResponseClient, policy=oracle/wss11_saml_token_with_message_protection_client_policy, policyVersion=6, assertionName={http://schemas.oracle.com/ws/2006/01/securitypolicy}wss11-saml-with-certificates.
oracle.wsm.common.sdk.WSMException: WSM-00048 : The signature confirmation value is invalid. Expected : <kV+01QFClBh1rclI0KKZ5eXIlG0=, U7ZnMYv3zOQp4k+ji+imDGtRFXubYNzc6oD+lfM0kJqM7es9HUwpbrTfD+dSiSCZWXPzTteTH+099BWkmV9LD+VKOXj80O/RCabA03H/G2XsWqDjfMGM4FDoxP8K/2g9QVRX8Knqzoaal9mzdqRhN3v7l0J/mt+CvZOAXR9JhMM=, >, Actual : <>.
        at oracle.wsm.security.policy.scenario.executor.Wss11SamlWithCertsScenarioExecutor.receiveFault(Wss11SamlWithCertsScenarioExecutor.java:316)
        at oracle.wsm.security.policy.scenario.executor.SecurityScenarioExecutor.execute(SecurityScenarioExecutor.java:864)
        at oracle.wsm.policyengine.impl.runtime.AssertionExecutor.execute(AssertionExecutor.java:41)
        at oracle.wsm.policyengine.impl.runtime.WSPolicyRuntimeExecutor.executeSimpleAssertion(WSPolicyRuntimeExecutor.java:425)
        at oracle.wsm.policyengine.impl.runtime.WSPolicyRuntimeExecutor.executeAndAssertion(WSPolicyRuntimeExecutor.java:344)
        at oracle.wsm.policyengine.impl.runtime.WSPolicyRuntimeExecutor.execute(WSPolicyRuntimeExecutor.java:291)
        at oracle.wsm.policyengine.impl.PolicyExecutionEngine.execute(PolicyExecutionEngine.java:102)

Add Your Comment