use tlsv1.2 in mobilefirst javascript http adapter - ibm-mobilefirst

I asked a similar question before and was able to use tlsv1.2 in java adapter, now I need to be able to do the same thing but in a javascript http adapter. Anyone know how to force the adapter to use tlsv1.2? This is Mobilefirst 7.0 on a mac.
Here is a code snippet of how to do it in java adapter:
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(null, null, null);
HttpClientBuilder clientBuilder = HttpClientBuilder.create().setSslcontext(context);
CloseableHttpClient httpClient = clientBuilder.build();
HttpGet request = new HttpGet(new URI(baseURL));
request.addHeader("Authorization", authHeader);
CloseableHttpResponse httpResponse = httpClient.execute(request);
json = EntityUtils.toString(httpResponse.getEntity());
Don't see a way to do this in the http adapter:
function getProbes(appName) {
var input = {
method : 'get',
returnedContentType : 'json',
path : "greenspot-web/rest/category/category/" + appName,
body : {
contentType : 'application/json',
content : ''
}
};
input.headers = headers;
var res = WL.Server.invokeHttp(input);

Last fall, work was done to add TLS V1.2 support when running with WebSphere, so installing the latest fix pack for 7.0 and up should enable you to do this
To write your JavaScript adapter to use TLS V1.2, the key issue is to use the WebSphere JSSEHelper API instead of the Apache HTTP client. The Apache client cannot handle the WebSphere SSL context that is required for force the version switch. Using JSSEHelper allows the adapter to correctly handle the WebSphere trust store and set the protocol.
Here are some links:
WebSphere JSSEHelper documentation
Programmatically specifying an outbound SSL configuration using JSSEHelper API
Does this help?

There is no way to do this with the JavaScript HTTP adapter. Maybe by invoking Java code from the JavaScript adapter? Could be. Perhaps this will help guide you to the right place: https://developer.ibm.com/mobilefirstplatform/documentation/getting-started-7-1/foundation/server-side-development-category/javascript-adapters/using-java-adapters/

Related

Helidon Webclient does not seem to work with a proxy

I am facing some trouble with the WebClient when using proxy, i.e. the code below does not work
WebClient webClient = WebClient.builder().baseUri("BASEURL").proxy(getProxy()).build();
Single<WebClientResponse> res = webClient.get().path("/MY/SUB/PATH").addHeader("Authorization", "Bearer " + MY_TOKEN).request();
WebClientResponse webClientRes = res.get();
String resContent = webClientRes.content().as(String.class).get();
public Proxy getProxy(){
return Proxy.builder().type(Proxy.ProxyType.HTTP).host(host).port(port).password("SECRET_PASSWORD".toCharArray()).username(username).build();
}
However the if we use Apache HttpClient the code works (working code below)
HttpHost proxy = new HttpHost(host, port);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(username, "SECRET_PASSWORD"));
HttpGet request = new HttpGet("BASEURL" + "/MY/SUB/PATH");
request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + MY_TOKEN);
CloseableHttpClient httpClient = HttpClients.custom().setProxy(proxy).setDefaultCredentialsProvider(credentialsProvider).build();
String resContent = EntityUtils.toString(httpClient.execute(request).getEntity());
could anyone let us know if we are overlooking something basic?
We are using helidon MP 2.5.2
When you set a proxy in WebClient, it will use absolute URI in the request because of changes made in https://github.com/helidon-io/helidon/issues/2302 and https://github.com/helidon-io/helidon/issues/3438. The use of absolute URI was implemented because of section 5.1.2 Request-URI in https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html which states:
The absoluteURI form is REQUIRED when the request is being made to a
proxy.
The problem is that some hosts have issue processing a request with absolute URI as they expect relative URI instead. I have encountered while working on issue https://github.com/helidon-io/helidon/issues/4644 where my testcase has a client that is connecting to KeyCloak as an OIDC server, and KeyCloak will return a 404 because it cannot handle the absoluteURI.
There is a special Webclient config property called relative-uris that you can use to force the request URI to use the relative form rather than absolute. So you can try adding config() in your WebClient.builder() and set that property like this:
.config(Config.create(ConfigSources.create(Map.of("relative-uris", "true")))
where Config needs to be imported as io.helidon.config.Config and ConfigSources as io.helidon.config.ConfigSources. As an alternative, you can also add something like this in your application.yaml:
force-relative-uris:
relative-uris: true
and add config() in the WebClient.builder() like this:
.config(config.get("force-relative-uris"))
where config is instantiated prior to WebClient.builder() like this:
Config config = Config.create();
In the upcoming Helidon v2.5.5 (and v3.0.3), there will be a new relativeUris(boolean relativeUris) in WebClient.builder() so that you don’t have to use config() as in my examples above, which is slightly cumbersome.

Jersey Client : Using ConnectionKeepAliveStrategy

Need help in applying ConnectionKeepAliveStrategy for ApacheConnector in jersey client.
For a standalone Apache Client, we can do it this way -
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
//your strategy here..
}
};
CloseableHttpClient client = HttpClients.custom()
.setKeepAliveStrategy(myStrategy)
.build();
But I couldn't find any way to apply strategy while using Jersey Client.
Tried registering the above feature class to WebClient but that did not help.
Any leads ?
Jersey 2.29.1 added support for Apache HTTP Client ConnectionKeepAliveStrategy and ConnectionReuseStrategy. Should work like this with ApacheConnector:
clientConfig.property(ApacheClientProperties.KEEPALIVE_STRATEGY, myStrategy);

How to connect a worklight app to a datapower backed on the start

We are developing a DataPower(DP) + Worklight(WL) POC
Having this objective in mind, we are following this article: http://www.ibm.com/developerworks/websphere/techjournal/1301_efremenko/1301_efremenko.html
We are clear and on sync with about the DP role on this approach, but we have one question related to the WL code implementation.
At the WL application client code we are using WL HTTP Adapters for all the http requests (REST+JSON) to the backend, like this:
WL.Client.invokeProcedure(invocationData, options);
These adapters are pointing to the DP MPGW endpoint, but based on our understanding, the HTTP Adapter code runs on WL Server.
If it is correct, our assumption for the execution sequence is:
WL Client App -> WL Server -> DP MPGW -> WL Server
When we are looking the same sequence mentioned in the DW article:
WL Client App ->DP MPGW -> WL Server
Could anyone please clarify our understanding about how the WL HTTP Adapter works in this case?
When you're using Worklight Adapter to call DP MPGW, the sequence as below
Request:
WL Client App --> WL Server (Adapter) --> DP MPGW
Response:
DP MPGW --> WL Server (Adapter) --> WL Client App
NOTE: The session id before/after WL server (Adapter) is not the same. If you want to do SSO, you need to pass your LTPA token in adapter to the backend DP. Here's the sample code for you.
Step1. Get LTPA token method (in you ChallengeHandler.js file)
sampleAppRealmChallengeHandler.isCustomResponse = function(response) {
if (!response || response.responseText === null) {
return false;
}
var indicatorIdx = response.responseText.search('j_security_check');
if (indicatorIdx >= 0){
return true;
}else if(response && (response.responseJSON) && (response.responseJSON['WL-Authentication-Success']) && (response.responseJSON['WL-Authentication-Success']['WASLTPARealm'])){
// set ltpaToken when login success
var realm = response.responseJSON['WL-Authentication-Success']['WASLTPARealm'];
ltpaToken = realm.attributes.LtpaToken;
console.log('Get ltpa token success: '+ltpaToken);
}
return false;
};
Step2. Call procedure method (in client App js file)
// define global LTPA token variable
var ltpaToken = null;
function getAccountInfo(){
// check ltpa token is not null, or get the ltap token from login user in WASLTPARealm
if(!ltpaToken){
if(WL.Client.isUserAuthenticated('WASLTPARealm')){
var attrs = WL.Client.getUserInfo('WASLTPARealm', 'attributes');
if(attrs){
ltpaToken = attrs.LtpaToken;
console.log('Set ltpaToken again: '+ltpaToken);
}
}
}
// Pass LTPA token from client App to WL server(adapter)
var token = {'LtpaToken2' : ltpaToken};
var invocationData = {
adapter: "DummyAdapter",
procedure: "getAccountInfo",
parameters: [token]
};
WL.Client.invokeProcedure(invocationData, {
onSuccess: getSecretData_Callback,
onFailure: getSecretData_Callback
});
}
Step3. Pass LTPA token to backend DP in adapter
function getServices( token) {
path = getPath("path/to/services");
var input = {
method : 'post',
returnedContentType : 'json',
path : path,
cookies: token
};
return WL.Server.invokeHttp(input);
}
Developer Works [DW] article correctly says the call sequence. It should be [assuming your mobile application is on customers mobile phone and he/she is operating on internet]
Worklight Mobile Client -> Data Power -> Worklight Server
The reason for this is, datapower acts as a ESB layer providing gateway for all the enterprise services. In a typical environment your Worklight server will be inside intranet and datapower will be on DMZ zone. Now Datapower needs to provide a gateway to worklight service [in your case what you call as adapter]. So the client code on mobile handset is even not aware about any worklight server. It calls the datapower proxy service which in turn scrutinize the request and if valid pass it to backend worklight server. When a response comes back it is also examined and forwarded to client application.
Exactly hosting this service on datapower is relatively easy but making it working requires a lot of effort. LTPA token plays a key role over here in validating client.
Hope this answers your question.
- Ajitabh

How can I configure Apache HttpClient 4.x to use a specific Websphere SSL alias?

We have an issue in our environment when using Websphere to attempt to connect to an external system with HttpClient 4.x (current version is 4.2.1). Connecting to the external system is fine with their certificate being installed in Websphere with no additional configuration of HttpClient. However, when they enabled mutual authentication, it no longer works and we get a SSLPeerUnverifiedException exception:
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated,
at com.ibm.jsse2.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:105),
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128),
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572),
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180),
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294),
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640),
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479),
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906),
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:1066),
I was provided the following code sample, and I was wondering if there's any way to configure HttpClient to use an explicit alias like this code sample does. I've tried to find good documentation on using SSL mutual authentication with HttpClient 4 and haven't been able to find much.
Here's the code sample:
private HttpURLConnection getConnection(String server, String machine,
String port) throws Exception {
URL u = new URL(server);
HttpsURLConnection connection = (HttpsURLConnection) u.openConnection();
String alias = "CellDefaultSSLSettings";
final HashMap connectionInfo = new HashMap();
connectionInfo.put(JSSEHelper.CONNECTION_INFO_DIRECTION,
JSSEHelper.DIRECTION_OUTBOUND);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_HOST, machine);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_PORT, port);
javax.net.ssl.SSLSocketFactory sslFact = JSSEHelper.getInstance()
.getSSLSocketFactory(alias, connectionInfo, null);
connection.setSSLSocketFactory(sslFact);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
return connection;
}
Basically, how do I make HttpClient use "CellDefaultSSLSettings"?
Fundamentally this problem has nothing to do with HttpClient. HttpClient can be configured to establish HTTPS connections using any custom SSLContext or SSLSocketFactory instance. This is basically about how to use JSSE APIs to configure SSLContext in the right way. In your particular case JSSEHelper does all the hard work for you.
// JSSE socket factory
javax.net.ssl.SSLSocketFactory jssesf = JSSEHelper.getInstance().getSSLSocketFactory(alias, connectionInfo, null);
// HC socket factory
SSLSocketFactory hcsf = new SSLSocketFactory(jssesf, SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
This will give a connection socket factory that can be registered with the connection manager.
HttpClient 4.3 also comes with SSLContextBuilder class which can be used to assemble custom SSL configurations using fluid builder API.
https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/conn/ssl/SSLContextBuilder.java
oleg's answer helped me out.
What I did was extend the DefaultHttpClient, and each constructor takes a String argument for the destination URL and calls a method setupScheme:
private void setupScheme(final String url) throws Exception {
Scheme scheme = new Scheme("https", 443, retrieveWebsphereSSLConnectionFactory(url));
getConnectionManager().getSchemeRegistry().register(scheme);
}
The method retrieveWebsphereSSLConnectionFactory essentially combines the code from the sample with the code oleg provided:
private SchemeSocketFactory retrieveWebsphereSSLConnectionFactory(final String url)
throws SSLException, URISyntaxException {
final String alias = "CellDefaultSSLSettings";
final HashMap<String, String> connectionInfo = new HashMap<String, String>();
connectionInfo.put(JSSEHelper.CONNECTION_INFO_DIRECTION, JSSEHelper.DIRECTION_OUTBOUND);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_HOST,
URIUtils.extractHost(new URI(url)).getHostName());
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_PORT, "443");
return new SSLSocketFactory(JSSEHelper.getInstance().getSSLSocketFactory(alias, connectionInfo, null),
SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
}

Adding authentication to security header in WCF to consume Metro WSIT service

I use this simple way to attach username and password to the SOAP request header. This works fine inside Java boundaries, but I want to be able to call it with my WCF client. How do I do this?
I've tried the following code, but it does not include the credentials in the header:
wsClient.ClientCredentials.UserName.UserName = "Hello";
wsClient.ClientCredentials.UserName.Password = "World";
Thanks in advance!
That is quite awful non-standardized way. It uses custom HTTP Headers so you cannot expect that built in WCF mechanism will magically support such approach. How should WCF know that you want to add custom non-standard HTTP header to HTTP request (not SOAP header)?
Use this:
var proxy = new YourServiceClient();
using (var scope = new OperationContextScope(proxy.InnerChannel))
{
var prop = new HttpRequestMessageProperty();
prop.Headers.Add("UserName", "Hello");
prop.Headers.Add("Password", "World");
OperationContext context = OperationContext.Current;
context.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = prop;
proxy.CallYourOperation();
}