Tuesday, November 25, 2014

Salesforce Outbound Provisioning with WSO2 IS - Configuration that needs to be done in Salesforce

1. Go to https://developer.salesforce.com/signup and sign up as a new user giving your email address (use the email address as the Username)

2. You will get an email asking you to activate your account. Click on the given link and specify a password and other required information (Secret Question, etc). Once you do that, you will be logged into Salesforce

3. Next you need to create a domain for your account. To do this, traverse to Administer -> Domain Management -> My Domain and specify a new of your choice to be registered as your domain.

E.g.:- In my case, I have given the domain name as evanthika so once the domain is registered, the domain URL comes as https://evanthika-dev-ed.my.salesforce.com


Once the domain is registered successfully, you will see a message saying ‘Your domain name is available for testing. Click here to Login’. Click on the button and login using your Salesforce username (email which you specified at account sign-up time) and password (which you set after clicking on the activation link which you got through the email).



It will take you to a page asking you to specify your mobile number to send a security code in order to verify your details. You can either give a mobile number and get the code to do the verification or you can just click on ‘I'll try again later. Just log me in »’ link. Either way, you will be directed to the Salesforce home page from your domain name (If you check the URL, you should see something similar to - https://evanthika-dev-ed.my.salesforce.com/domainname/DomainName.apexp)

4. You need to create a new ‘Connected App’ first. For that, click on Build -> Create -> Apps from the left menu pane. Then specify details as follows and click on the ‘Save’ button.

Connected App Name - IdentityServer
API Name - IdentityServer
Contact Email - evanthika@wso2.com

Under ‘API (Enable OAuth Settings)’ tick the check-box ‘Enable OAuth Settings’.

Callback URL - https://login.salesforce.com/services/oauth2/token
Selected OAuth Scopes - Full access (full)


Then click on ‘Save’

Once saved, you will get a Client Key and a Secret key for the particular Application which you created.

5. Now lets assign the app we created to a user profile. In order to do this, go to Administer -> Profiles -> Select a user and click ‘Edit’ (E.g.:- Chatter Free User) -> Tick the check box under ‘Connected App Access’ to select the App (IdentityServer) which we created in the above step & click on the ‘Save’ button.


6. In order to capture the ProfileId for the above user profile, click on the profile name (Chatter Free User) and check the URL. Extract the value at the end of the URL and keep it safe. We need this to use as the ProfileId when we configure the IDP at WSO2 Identity Server side.
E.g.:- In my case, when I click on the user profile ‘Chatter Free User’ I would see the URL as https://evanthika-dev-ed.my.salesforce.com/00e90000001jZnj so my ProfileId would be 00e90000001jZnj.

7. You will need a security token to work with this scenario. In case you do not get a security token by email, you can request for a new one.

To reset your security token:

i) At the top of any Salesforce page, click the down arrow next to your name. From the menu under your name, select Setup or My Settings—whichever one appears.


ii) From the left pane, select one of the following:
        If you clicked Setup, select My Personal Information | Reset My Security Token.
        If you clicked My Settings, select Personal | Reset My Security Token.
iii) Click the Reset Security Token button. The new security token is sent via email to the email address on your Salesforce user record.


This covers the configuration that needs to be done in Salesforce. The rest of the configuration is explained clearly in the blog written by Ushani Balasooriya.

Monday, October 6, 2014

I want to read the request message from a file in JMeter. How can I do this?

Assume that you need to do a load test with a request which is in a file. Assume you need not to copy it to JMeter, you need to read the request body from a text file and do the POST operation.

All you have to do is, use the below command instead of the POST body.

${__FileToString(large_message.txt,,)}


Friday, September 19, 2014

How to solve the famous token regeneration issue in an API-M cluster

In a API Manager clustered environment (in my case, I have a publisher, a store, two gateway nodes and two key manager nodes fronted by a WSO2 ELB 2.1.1), while regenerating tokens, if you come across an error saying Error in getting new accessToken with an exception as below at Key Manager node, then this is due to a configuration issue.

TID: [0] [AM] [2014-09-19 05:41:28,321]  INFO {org.wso2.carbon.core.services.util.CarbonAuthenticationUtil} -  'Administrator@carbon.super [-1234]' logged in at [2014-09-19 05:41:28,321-0400] {org.wso2.carbon.core.services.util.CarbonAuthenticationUtil}
TID: [0] [AM] [2014-09-19 05:41:28,537] ERROR {org.wso2.carbon.apimgt.keymgt.service.APIKeyMgtSubscriberService} -  Error in getting new accessToken {org.wso2.carbon.apimgt.keymgt.service.APIKeyMgtSubscriberService}
TID: [0] [AM] [2014-09-19 05:41:28,538] ERROR {org.apache.axis2.rpc.receivers.RPCMessageReceiver} -  Error in getting new accessToken {org.apache.axis2.rpc.receivers.RPCMessageReceiver}
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:94)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
    at java.lang.reflect.Method.invoke(Method.java:619)
    at org.apache.axis2.rpc.receivers.RPCUtil.invokeServiceClass(RPCUtil.java:212)
    at org.apache.axis2.rpc.receivers.RPCMessageReceiver.invokeBusinessLogic(RPCMessageReceiver.java:117)
    at org.apache.axis2.receivers.AbstractInOutMessageReceiver.invokeBusinessLogic(AbstractInOutMessageReceiver.java:40)
    at org.apache.axis2.receivers.AbstractMessageReceiver.receive(AbstractMessageReceiver.java:110)
    at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:180)
    at org.apache.axis2.transport.http.HTTPTransportUtils.processHTTPPostRequest(HTTPTransportUtils.java:172)
    at org.apache.axis2.transport.http.AxisServlet.doPost(AxisServlet.java:146)
    at org.wso2.carbon.core.transports.CarbonServlet.doPost(CarbonServlet.java:231)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
    at org.eclipse.equinox.http.servlet.internal.ServletRegistration.service(ServletRegistration.java:61)
    at org.eclipse.equinox.http.servlet.internal.ProxyServlet.processAlias(ProxyServlet.java:128)
    at org.eclipse.equinox.http.servlet.internal.ProxyServlet.service(ProxyServlet.java:68)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
    at org.wso2.carbon.tomcat.ext.servlet.DelegationServlet.service(DelegationServlet.java:68)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.wso2.carbon.tomcat.ext.filter.CharacterSetFilter.doFilter(CharacterSetFilter.java:61)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.wso2.carbon.tomcat.ext.valves.CompositeValve.continueInvocation(CompositeValve.java:178)
    at org.wso2.carbon.tomcat.ext.valves.CarbonTomcatValve$1.invoke(CarbonTomcatValve.java:47)
    at org.wso2.carbon.webapp.mgt.TenantLazyLoaderValve.invoke(TenantLazyLoaderValve.java:56)
    at org.wso2.carbon.tomcat.ext.valves.TomcatValveContainer.invokeValves(TomcatValveContainer.java:47)
    at org.wso2.carbon.tomcat.ext.valves.CompositeValve.invoke(CompositeValve.java:141)
    at org.wso2.carbon.tomcat.ext.valves.CarbonStuckThreadDetectionValve.invoke(CarbonStuckThreadDetectionValve.java:156)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    at org.wso2.carbon.tomcat.ext.valves.CarbonContextCreatorValve.invoke(CarbonContextCreatorValve.java:52)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1653)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:853)
Caused by:
org.wso2.carbon.apimgt.keymgt.APIKeyMgtException: Error in getting new accessToken
    at org.wso2.carbon.apimgt.keymgt.service.APIKeyMgtSubscriberService.renewAccessToken(APIKeyMgtSubscriberService.java:281)
    ... 45 more
Caused by:
java.lang.RuntimeException: Token revoke failed : HTTP error code : 404
    at org.wso2.carbon.apimgt.keymgt.service.APIKeyMgtSubscriberService.renewAccessToken(APIKeyMgtSubscriberService.java:252)
    ... 45 more


This is what you have to do to solve this issue.

1. In you Gateway nodes, you need to change the host and the port values of the below APIs that resides under $APIM_HOME/repository/deployment/server/synapse-configs/default/api                                                      _TokenAPI_.xml                                                                                                                                         _AuthorizeAPI_.xml                                                                                                                                       _RevokeAPI_.xml                                                                                                                                        
If you get a HTTP 302 error at Key manager side while regenerating the token, make sure to check the RevokeURL of the api-manager.xml of the Key Manager node to see if it is pointing to the NIO port of the Gateway Node.

Friday, July 11, 2014

Resolving "ORA-12516, TNS:listener could not find available handler with matching protocol stack"


While testing a WSO2 G-Reg pack pointing to an Oracle database (with ojdbc6.jar), came up with the below exception

Caused by: oracle.net.ns.NetException: Listener refused the connection with the following error: ORA-12516, TNS:listener could not find available handler with matching protocol stack at oracle.net.ns.NSProtocol.connect(NSProtocol.java:399) at oracle.jdbc.driver.T4CConnection.connect(T4CConnection.java:1140) at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:340) ... 88 more 

In addition to that, noticed the below warning as well.
  TID: [0] [Greg] [2014-07-11 18:31:52,652] WARN {java.util.prefs.FileSystemPreferences} - Couldn't flush system prefs: java.util.prefs.BackingStoreException: Couldn't get file lock. {java.util.prefs.FileSystemPreferences}

So after doing some googling, I found out about the below parameter and added it to the server start-up script solves the issue. You can read more about this on http://allaboutbalance.com/articles/disableprefs/.

-Djava.util.prefs.syncInterval=2000000 \

Tuesday, April 29, 2014

Expiration times of L1 & L2 caches with Hazelcast for WSO2 products

Recently I had a doubt about how L1 & L2 caches work with WSO2 products. Then I went through the blog on WSO2 Multi-tenant Cache: JSR-107 (JCache) implementation based on Hazelcast by Azeez and got an idea about the caches and how they work.

Then I had a doubt about the expiration times of these two caches, whether they have separate cache expiration times or whether they share the same expiration time value. After having a discussion about this, found out that both caches are using the same expiration time value.

In Cabon 4.2.0 based products, which is using Hazelcast, the cache expiration time cannot be configured and right now, it is hard coded. The default timeout value used with our products is 15 minutes.

Friday, April 25, 2014

Retrieving availble HTTP methods from a back-end server through WSO2 ESB APIs

Assume you have a set of back-end servers that supports different combinations of HTTP methods and you have a need to retrieve which server supports what methods. This can be achieved using WSO2 ESB with the help of an API. Lets see how this can be done using a simple example.

Pre-requisites
  • Download the latest version of WSO2 ESB distribution (WSO2 ESB) to a preferred location on you file system.
  • Download an instance of WSO2 Application Server (WSO2 AS) distribution to host your web application. I will be using version 5.0.1 in this example.

Step 1 - Deploying the web application in WSO2 AS.

Extract the WSO2 AS distribution to a preferred location and start the server. Follow the instructions in the guide Deploying JAX-WS and JAX-RS Applications and deploy the sample web application.

Step 2 - Creating the API in WSO2 ESB

Now lets create an API pointing to the above web application to retrieve it's HTTP methods.

Follow the instructions given under Adding APIs in the Management Console and add an API with the below configuration.


<api xmlns="http://ws.apache.org/ns/synapse" name="CustomerAPI" context="/customer">
   <resource methods="POST GET DELETE PUT" url-mapping="/*">
      <inSequence>
         <send>
            <endpoint>
               <address uri="http://localhost:8280/proxyapi"></address>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send></send>
      </outSequence>
   </resource>
</api>


Step 3 - Invoking the API to retrieve the available methods of the back-end service.

$ curl -v -X OPTIONS http://localhost:8280/customer/customerservice


When you invoke the above curl command, it would return you all the available methods as below.


* About to connect() to localhost port 8281 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8281 (#0)
> OPTIONS /customer/customerservice HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8280
> Accept: */*
>
< HTTP/1.1 200 OK
< Max-Forwards: 1
< Host: 127.0.0.1:8280
< Allow: POST,GET,DELETE,PUT,OPTIONS,HEAD
< Accept: */*
< Date: Fri, 25 Apr 2014 13:09:10 GMT
< Server: WSO2-PassThrough-HTTP
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact


This is a simple scenario where there is only one layer between your back-end server and your client (which is the API). But assume that the message flows through another API/proxy service before reaching the back-end and still you need to get the methods allowed by the back-end server. To enable this, you can use the parameter Max-Forwards.

Assume a configuration as below.


<api xmlns="http://ws.apache.org/ns/synapse" name="CustomerAPI" context="/customer">
   <resource methods="DELETE POST PUT OPTIONS" url-mapping="/*">
      <inSequence>
         <send>
            <endpoint>
               <address uri="http://localhost:8280/proxyapi"></address>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send></send>
      </outSequence>
   </resource>
</api>

<api xmlns="http://ws.apache.org/ns/synapse" name="ProxyAPI" context="/proxyapi">
   <resource methods="PUT OPTIONS" url-mapping="/*">
      <inSequence>
         <send>
            <endpoint>
               <address uri="http://localhost:10763/jaxrs_basic_44/services/customers"></address>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send></send>
      </outSequence>
   </resource>
</api>



To retrieve the methods of the first API, you can specify the MAX-Forwards parameter as below.

$ curl -v -X OPTIONS http://localhost:8280/customer/customerservice -H "Max-Forwards: 0"

It would return the methods available of the first API which is CustomerAPI

* About to connect() to localhost port 8281 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8281 (#0)
> OPTIONS /customer/customerservice HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8280
> Accept: */*
> Max-Forwards: 0
>
< HTTP/1.1 200 OK
< Max-Forwards: 0
< Host: localhost:8280
< Allow: POST, DELETE, PUT
< Content-Type: application/x-www-form-urlencoded; charset=UTF-8
< Accept: */*
< Date: Fri, 25 Apr 2014 12:17:02 GMT
< Server: WSO2-PassThrough-HTTP
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact


If you need to retrieve the methods of the second API, which is ProxyAPI, you need to give the command with Max-Forwards parameter set to 1. Then, it would return the methods allowed by that API only.

$ curl -v -X OPTIONS http://localhost:8280/customer/customerservice -H "Max-Forwards: 1"

The result would be as follows. It returns the available methods of the second API.

* About to connect() to localhost port 8281 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8281 (#0)
< OPTIONS /customer/customerservice HTTP/1.1
< User-Agent: curl/7.29.0
< Host: localhost:8280
< Accept: */*
< Max-Forwards: 1
<
> HTTP/1.1 200 OK
> Max-Forwards: 0
> Host: localhost:8280
> Allow: PUT
> Content-Type: application/x-www-form-urlencoded; charset=UTF-8
> Accept: */*
> Date: Fri, 25 Apr 2014 18:20:16 GMT
> Server: WSO2-PassThrough-HTTP
> Transfer-Encoding: chunked
>
* Connection #0 to host localhost left intact



If you need the HTTP methods of the actual back-end server, you need to call the first API using a command as shown below

curl -v -X OPTIONS http://localhost:8280/customer/customerservice -H "Max-Forwards: 2"

It would give the result as below with all the HTTP methods allowed by the back-end.

* About to connect() to localhost port 8281 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8281 (#0)
< OPTIONS /customer/customerservice HTTP/1.1
< User-Agent: curl/7.29.0
< Host: localhost:8281
< Accept: */*
< Max-Forwards: 2
<
> HTTP/1.1 200 OK
> Allow: POST,GET,DELETE,PUT,OPTIONS,HEAD
> Content-Type: application/octet-stream
> Date: Fri, 25 Apr 2014 18:20:13 GMT
> Server: WSO2-PassThrough-HTTP
> Transfer-Encoding: chunked
>
* Connection #0 to host localhost left intact


Monday, April 21, 2014

Setting up a simple WSO2 AS cluster when custom keystores are used

When we setup cluster deployments, people are reluctant to use the default keystore that is shipped with WSO2 products due to security reasons. Therefore, we can use a different keystore within our products other than what is shipped by default (wso2carbon.jks).

Lets see how this is possible and what changes we need to do in addition to the normal cluster configuration we do.

NOTE: I'm demonstrating on how to setup a simple cluster with one WSO2 Application Server Manager node and one WSO2 ELB node.

1. Download a fresh WSO2 Application Server pack from the product page. 


2. Remove the default wso2carbon.jks that is shipped with the product. This file resides in $PRODUCT_HOME/repository/resources/security folder and drop the custom keystore that you created.

keytool -genkey -alias appserverks -keyalg RSA -keystore appserver.jks -storepass appserver


3. Change the following configuration files of the App server pack so that it refers the custom keystore file.
- $PRODUCT_HOME/repository/conf/carbon.xml
- $PRODUCT_HOME/repository/conf/identity.xml


4. Follow the instructions listed under the section Configuring the Manager Node and setup the Application server manager node.


5. Follow the instructions given in WSO2 Clustering & Deployment Guide and configure a WSO2 ELB as explained in Configuring the Load Balancer section.


6. Start the WSO2 ELB node


7. Start the WSO2 Application Server node & access the Management Console and it would throw an exception as below.



[2014-04-21 23:34:40,530] ERROR - TargetHandler I/O error: General SSLEngine problem javax.net.ssl.SSLHandshakeException: General SSLEngine problem at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1362) at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:513) at sun.security.ssl.SSLEngineImpl.writeAppRecord(SSLEngineImpl.java:1177) at sun.security.ssl.SSLEngineImpl.wrap(SSLEngineImpl.java:1149) at javax.net.ssl.SSLEngine.wrap(SSLEngine.java:469) at org.apache.http.nio.reactor.ssl.SSLIOSession.doWrap(SSLIOSession.java:220) at org.apache.http.nio.reactor.ssl.SSLIOSession.doHandshake(SSLIOSession.java:254) at org.apache.http.nio.reactor.ssl.SSLIOSession.isAppInputReady(SSLIOSession.java:380) at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:118) at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:160) at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:342) at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:320) at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:280) at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:106) at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:604) at java.lang.Thread.run(Thread.java:724)
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1683) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:278) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1341) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868) at sun.security.ssl.Handshaker$1.run(Handshaker.java:808) at sun.security.ssl.Handshaker$1.run(Handshaker.java:806) at java.security.AccessController.doPrivileged(Native Method) at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1299) at org.apache.http.nio.reactor.ssl.SSLIOSession.doRunTask(SSLIOSession.java:238) at org.apache.http.nio.reactor.ssl.SSLIOSession.doHandshake(SSLIOSession.java:270) ... 9 more Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:283) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:138) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1328) ... 17 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380) ... 23 more

To overcome this issue, you need to import the public certificate of the custom keystore that you used at the App server node, to the $ELB_HOME/repository/resources/security/client-truststore.jks of the WSO2 ELB instance.

Extracting the public certificate
 
keytool -export -alias appserverks -keystore appserver.jks -file appserverks.cert


Importing the public certificate to the client-truststore.jks of the WSO2 ELB instance


keytool -import -file appserverks.cert -keystore client-truststore.jks -storepass wso2carbon -alias appserverks



Restart the WSO2 ELB instance and then the WSO2 Appserver node and then you will be able to access the Management Console successfully

Monday, February 3, 2014

Solving "org.apache.ws.security.WSSecurityException: An unsupported signature or encryption algorithm was used"

While trying out the scenario which I have explained in my previous post Accessing a non secured backend from a secured client with the help of WSO2 ESB, with security scenario 3 onward, you might have come across an issue as below on client side.

org.apache.axis2.AxisFault: Error in encryption
    at org.apache.rampart.handler.RampartSender.invoke(RampartSender.java:76)
    at org.apache.axis2.engine.Phase.invokeHandler(Phase.java:340)
    at org.apache.axis2.engine.Phase.invoke(Phase.java:313)
    at org.apache.axis2.engine.AxisEngine.invoke(AxisEngine.java:261)
    at org.apache.axis2.engine.AxisEngine.send(AxisEngine.java:426)
    at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:430)
    at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:225)
    at org.apache.axis2.client.OperationClient.execute(OperationClient.java:149)
    at org.apache.axis2.client.ServiceClient.sendReceive(ServiceClient.java:554)
    at org.apache.axis2.client.ServiceClient.sendReceive(ServiceClient.java:530)
    at SecurityClient.runSecurityClient(SecurityClient.java:99)
    at SecurityClient.main(SecurityClient.java:34)
Caused by: org.apache.rampart.RampartException: Error in encryption
    at org.apache.rampart.builder.AsymmetricBindingBuilder.doSignBeforeEncrypt(AsymmetricBindingBuilder.java:612)
    at org.apache.rampart.builder.AsymmetricBindingBuilder.build(AsymmetricBindingBuilder.java:97)
    at org.apache.rampart.MessageBuilder.build(MessageBuilder.java:147)
    at org.apache.rampart.handler.RampartSender.invoke(RampartSender.java:65)
    ... 11 more
Caused by: org.apache.ws.security.WSSecurityException: An unsupported signature or encryption algorithm was used (unsupported key transport encryption algorithm: No such algorithm: http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p); nested exception is:
    java.security.NoSuchAlgorithmException: Cannot find any provider supporting RSA/ECB/OAEPPadding
    at org.apache.ws.security.util.WSSecurityUtil.getCipherInstance(WSSecurityUtil.java:785)
    at org.apache.ws.security.message.WSSecEncryptedKey.prepareInternal(WSSecEncryptedKey.java:205)
    at org.apache.ws.security.message.WSSecEncrypt.prepare(WSSecEncrypt.java:259)
    at org.apache.rampart.builder.AsymmetricBindingBuilder.doSignBeforeEncrypt(AsymmetricBindingBuilder.java:578)
    ... 14 more
Caused by: java.security.NoSuchAlgorithmException: Cannot find any provider supporting RSA/ECB/OAEPPadding
    at javax.crypto.Cipher.getInstance(Cipher.java:524)
    at org.apache.ws.security.util.WSSecurityUtil.getCipherInstance(WSSecurityUtil.java:777)
    ... 17 more
Exception in thread "main" java.lang.NullPointerException
    at SecurityClient.main(SecurityClient.java:38)

The reason for this is that the Bouncycastle jar required to run this scenario, is not found in the class path at the client side.

To overcome this issue, you need to place the relevant bouncycastle jar downloaded from the www.bouncycastle.org.

E.g.:- If you are running your client on JDK1.7, then the jar you need to download is bcprov-jdk15on-150.jar.
A point to note : I tried this scenario pointing the $ESB_HOME/repository/plugins folder to the Eclipse project and pointed to a bouncycastle jar which was at a different location. For some reason, it did not load the jar until I dropped it inside the $ESB_HOME/repository/plugins folder.

NOTE: Sometimes, you will have to clear the Eclipse/IntelliJ Idea cache in order for the classes to pick up the jars properly.

Thursday, January 16, 2014

Accessing a non secured backend from a secured client with the help of WSO2 ESB

Assume that you have a backend that is not secured and you need to invoke this with a secured client. The below configuration is of a Proxy service which talks to a non secured backend. The client used to invoke this proxy service is secured. In this particular scenario, the Proxy service is secured.

Pre-requisites

Download WSO2 ESB 4.8.0 from here. Extract it to a folder of your choice and start the server.
Download the StockPurchasingService.aar from here and deploy it in a application server of your choice.

Testing out the scenario

When the message sent from the client is secured and the backend service is not, you need to ensure that the security headers are removed from the message before it is being sent from ESB to the backend. By using the header mediator as below, we can remove the security headers.




Step 1

Deploy the following proxy service in WSO2 ESB. <?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="secClientNonSecService"
       transports="https,http"
       statistics="disable"
       trace="disable"
       startOnLoad="true">
   <target>
      <inSequence>
         <header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
                 name="wsse:Security"
                 action="remove"/>
      </inSequence>
      <outSequence>
         <send/>
      </outSequence>
      <endpoint>
         <address uri="http://localhost:9773/services/PuchasingService"/>
      </endpoint>
   </target>
   <publishWSDL uri="http://localhost:9773/services/PuchasingService?wsdl"/>
</proxy>

We will test our scenario with security scenario 2 (Non-repudiation). Once the above proxy service is deployed, apply security scenario 2 from the wizard.  View the ?wsdl of the proxy service and verify whether the relevant policy is attached to the proxy service.
 
One the security policy is applied, the complete proxy service configuration will look like what is available here.

Step 2

Create a Java project from your favourite IDE and place the SecurityClient.java & client.properties files in the below structure.

.
└── src
    ├── client.properties
    └── SecurityClient.java
 
Note: Download the resources.zip folder and extract the content to the src level. Then set the paths of following fields of the client.properties file accordingly.
  • clientRepo
  • clientKey
  • securityPolicyLocation
  • trustStore

Step 3

Invoke the SecurityClient.java code and you will get the expected response message as below.



Result :1000
Scenario No :2

Result : >ns:purchaseresponse xmlns:ns="http://service.purchasing.wso2.com"<>ns:return<1000>/ns:return<>/ns:purchaseresponse<

Friday, January 3, 2014

How to solve "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target" issue of WSO2 Products


Ever come across the error message mentioned in the subject while trying out WSO2 products? Well, if you have, the reason is that cetifacte of the backend that you is not trusted and the certificate of that backend server should be added to the WSO2 product servers client-truststore.jks. Lets try this with a simple example.

Assume you have a simple API with the below configuration pointing to twitter search in WSO2 ESB (You can try this with a latest version of ESB). The configuration will be as follows.

      <api name="TwitterAPI" context="/twitter">
      <resource methods="GET" url-mapping="/*">
         <inSequence>
            <send>
               <endpoint>
                  <address uri="https://twitter.com"/>
               </endpoint>
            </send>
         </inSequence>
         <outSequence>
            <send/>
         </outSequence>
      </resource>
   </api>

Invoke the above API using the below command.

curl -v -X GET -H "Content-type: application/json" http://localhost:8281/twitter/search?q=wso2

It will throw the below exception at the WSO2 ESB server console.


Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:283)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:138)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1328)
    ... 17 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
    ... 23 more



Inorder to get rid of this exception and the request to passthrough successfully to the backend, you need to import the public certificate of Twitter to the client-truststore.jks of the WSO2 ESB server instance. Below are the steps you need to follow.

1. Go to https://twitter.com/, click on the lock icon at the address bar, click on the 'Connection' tab, then click on the link 'Certificate Information'. From the 'Certificate Viewer', select the tab 'Details' and click on the 'Export' button and download the certificate (twitter.com) to a preferred location.

2. Once downloaded, issue the below command to import the public certificate of Twitter to the client-truststore.jks.

$ keytool -importcert -file $somepath/twitter.com -keystore $ESB_HOME/repository/resources/security/client-truststore.jks -alias "Twitter"

3. Restart the WSO2 ESB server and invoke the API again and you will get the expected result.