Invoking Secure RESTful Web Service over HTTPS When client is in bluemix - ssl

My application is running in BlueMix and it has to make restful call to another application over SSL. I am wondering where and how to add these information
> trustStoreType, trustStore and trustStorePassword
So that application running in bluemix can use that ? When I am testing from my local I modified server class-path, can I do some thing similar in bluemix liberty server, where the client app is running ? Or is there any easier better way ?

You should be able to edit the server.xml in eclipse and setup something like
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>websocket-1.0</feature>
<feature>localConnector-1.0</feature>
<feature>jndi-1.0</feature>
<feature>jsp-2.2</feature>
<feature>jdbc-4.0</feature>
<feature>ejbLite-3.1</feature>
<feature>ssl-1.0</feature>
<feature>jaxb-2.2</feature>
</featureManager>
<ssl clientAuthenticationSupported="true" id="defaultSSLConfig" keyStoreRef="defaultKeyStore" trustStoreRef="defaultTrustStore"/>
<keyStore id="defaultKeyStore"location="${server.config.dir}/resources/security/keystore.jks" password="passw0rd" type="JKS"/>
<keyStore id="defaultTrustStore" location="${server.config.dir}/resources/security/trustStore.jks" password="passw0rd" type="JKS"/>
<ssl clientAuthenticationSupported="true" id="defaultSSLConfig" keyStoreRef="serverKeyStore" trustStoreRef="serverTrustStore"/>
<keyStore id="serverKeyStore" location="${server.config.dir}/resources/security/serverKey.jks" password="passw0rd" type="JKS"/>
<keyStore id="serverTrustStore" location="${server.config.dir}/resources/security/serverTrust.jks"> password="passw0rd" type="JKS"/>
<!-- customize SSL configuration -->
<ssl id="customizeSSLConfig" keyStoreRef="clientKeyStore" trustStoreRef="clientTrustStore"/>
<keyStore id="clientKeyStore" location="${server.config.dir}/resources/security/clientKey.jks" password="passw0rd" type="JKS"/>
<keyStore id="clientTrustStore" location="${server.config.dir}/resources/security/clientTrust.jks" password="passw0rd" type="JKS"/>
<!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint httpPort="8080" httpsPort="9443" id="defaultHttpEndpoint"/>
<applicationMonitor updateTrigger="mbean"/>
</server>
Easiest way is with the Bluemix plugin for eclipse and using Websphere Libery Profile Server

If you are depending on the Liberty server, you can customize it offline and push it to Bluemix.
.
https://www.ibm.com/developerworks/community/blogs/msardana/entry/developing_with_bluemix_customizing_the_liberty_build_pack_to_add_your_configurations?lang=en

Where is your webservice running ? If it is on-premise, then you have to use cloud-integration agent available in Bluemix to make a secure tunneling and to get a proxy IP to your onpremise web service. Details of the same is available in the following link:
https://www.ibm.com/developerworks/community/blogs/96960515-2ea1-4391-8170-b0515d08e4da/entry/cloud_to_on_premise_web_services_bluemix_cloud_integrators?lang=en

Though all those I believe are valid option, but I ended up doing little differently. This is what finally worked for me
public static HttpClient getCustomClient() throws GeneralSecurityException, IOException {
KeyStore trustStore = KeyStore.getInstance("jks");
// Load the truststore from the classpath using the password
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("/clienttruststore");
trustStore.load(resourceAsStream, "password".toCharArray());
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
resourceAsStream.close();
return httpclient;
}
//get custom httpclient
Unirest.setHttpClient(getCustomClient());
//send request...
HttpResponse<String> response =
Unirest.get("https://xyz.abc.com/").asString();
Basically packaged custom trust store with war and let application use that. I will be trying other option too, but with the previous option my custom server was crashing not sure if that was because of resources.

Related

Why is hostname verification done even though verifyHostname is false?

In trying to get secure rest services to work on Open Liberty in a container, I get the following error:
CWPKI0824E: SSL HANDSHAKE FAILURE: Host name verification error while connecting to host [hostname]. The host name used to access the server does not match the server certificate's SubjectDN or Subject Alternative Name information. The extended error message from the SSL handshake exception is: [No name matching hostname found].
Relevant portion of the server.xml:
<featureManager>
<feature>appSecurity-3.0</feature>
<feature>jca-1.7</feature>
<feature>jdbc-4.1</feature>
<feature>jndi-1.0</feature>
<feature>localConnector-1.0</feature>
<feature>mpConfig-1.3</feature>
<feature>passwordUtilities-1.0</feature>
<feature>ssl-1.0</feature>
<feature>transportSecurity-1.0</feature>
</featureManager>
<sslDefault sslRef="DefaultSSLConfig" httpHostNameVerification="false"/>
<ssl id="DefaultSSLConfig" keyStoreRef="DefaultKeyStore" trustStoreRef="DefaultTrustStore" trustDefaultCerts="true" verifyHostname="false"/>
<keyStore id="DefaultKeyStore" location="liberty-default-key.p12" type="PKCS12" password="password"/>
<keyStore id="DefaultTrustStore" location="liberty-default-trust.p12" type="PKCS12" password="password"/>
<ldapRegistry id="ldapRegistry" realm="Standalone LDAP Registry" ldapType="IBM Tivoli Directory Server"
host="server" port="123"
baseDN="baseDN" bindDN="bindDN" bindPassword="password"
recursiveSearch="true"
sslEnabled="true" sslRef="DefaultSSLConfig">
<idsFilters>
...
</idsFilters>
</ldapRegistry>
As you can see verifyHostname has the value 'false', but the check is done anyway.
What am I missing?
The JDK has handles LDAP separately and hostname verification is enabled by default by the JDK. To disable LDAP hostname verification you need to set the system property com.sun.jndi.ldap.object.disableEndpointIdentification to true. So in the jvm.options in your server directory add -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true to disable hostname verification on an LDAP connention.

SSL config for outbound connections doesn't work in websphere-liberty 17.0.0.2

I'm trying to configure websphere-liberty server to use default keystore and trustore for all outbound connections (actually REST calls) and for inbound use a custom key and trust stores. But it fails with SSLHandshakeException when try to make a call to external REST service. In logs I can see that it uses my custom truststore instead of default one.
Below is my server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server description="Default server">
<featureManager>
<feature>appSecurity-2.0</feature>
<feature>transportSecurity-1.0</feature>
<feature>jaxrs-2.0</feature>
<feature>json-1.0</feature>
<feature>javaMail-1.5</feature>
<!--<feature>ssl-1.0</feature>-->
</featureManager>
<sslDefault sslRef="saasSSLConfig" outboundSSLRef="outboundSSLConfig" />
<ssl id="saasSSLConfig" keyStoreRef="saasKeyStore" trustStoreRef="saasTrustStore" clientAuthentication="true" sslProtocol="TLSv1" />
<keyStore id="saasKeyStore" location="/opt/ibm/wlp/output/defaultServer/resources/security/sbs_endpoint_keystore.jks" password="pwd" />
<keyStore id="saasTrustStore" location="/opt/ibm/wlp/output/defaultServer/resources/security/serverTruststore.jks" password="pwd" />
<ssl id="outboundSSLConfig" keyStoreRef="defaultKeyStore" trustStoreRef="defaultTrustStore" />
<basicRegistry id="basic" realm="BasicRealm">
<!-- <user name="yourUserName" password="" /> -->
</basicRegistry>
<httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" httpsPort="9443" />
<applicationManager autoExpand="true"/>
</server>
BTW if change saasSSLConfig to use defaultTrustStore instead of saasTrustStore then everything works fine.
Server version:
WebSphere Application Server 17.0.0.2 (1.0.17.cl170220170523-1818) on IBM J9 VM, version pxa6480sr4fp7-20170627_02 (SR4 FP7) (en_US)
Error:
[ERROR] CWPKI0022E: SSL HANDSHAKE FAILURE: A signer with SubjectDN CN=*.api.ibm.com, O=International Business Machines, L=Armonk, ST=New York, C=US was sent from the target host. The signer might need to be added to local trust store /opt/ibm/wlp/output/defaultServer/resources/security/serverTruststore.jks, located in SSL configuration alias saasSSLConfig. The extended error message from the SSL handshake exception is: PKIX path building failed: java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl could not build a valid CertPath.;
SSLHandshakeException invoking https://dev.api.ibm.com/scx/test/sbs/customer/222222222: java.security.cert.CertificateException: PKIXCertPathBuilderImpl could not build a valid CertPath.
Liberty does not load cacerts automatically. You can create a keyStore element to point to it if desired. So in your case above you can create a configuration like this.
<ssl id="outboundSSLConfig" keyStoreRef="cacertKeyStore" />
<keyStore id="cacertKeyStore" location=<fill in path to your jdk cacerts file> password="changeit" />
I am assuming you do not need a key for this configuration so I simplified to just a keyStoreRef on outboundSSLConfig. It will use what is pointed to by keyStoreRef for both key and trust.
In your configuration I do not see keyStore elements for defaultKeyStore and defaultTrustStore. If they are missing that will cause outboundSSLConfig to be an invalid SSL configuration. Can you please add them and see if things work.

IBM Worklight 6.0 - Mixed port numbers after enabling console login authentication?

Wishing to have a Worklight Console Authentication I followed the infocenter instructions from this link at infocenter.It seems to be working at the first glance as entering the console it prompts the login form.
Going to the console URL at http://192.168.168.154:9080/finance/console/#catalog it brings the login page right away. So far so good.
After entering the right username/password (both defined at worklight.properties) the login page redirects to the Worklight console at port 10080 and fails to load the page
Unable to connect
Firefox can't establish a connection to the server at 192.168.168.154:10080.
As you can read it tries to connect to http://192.168.168.154:10080/finance/console. It should not happen I guess, as port 10080 was not configured.
I have no idea why it is redirecting to this port. I searched inside the config files for 10080 and could not find any reference to it. I think somehow WL is getting this 10080 as a default as it is the port used for development.
Here is the files snippets I am using to enable Login Console:
worklight.properties:
#publicWorkLightHostname=localhost
# http or https
#publicWorkLightProtocol=http
# For default port leave empty
#publicWorkLightPort=10080 <-- it is commented out
console.username=finance
console.password=finance
authenticationConfig.xml
<staticResources>
<resource id="worklightConsole" securityTest="WorklightConsole">
<urlPatterns>/console*</urlPatterns>
</resource>
....
</staticResources>
<securityTests>
<customSecurityTest name="WorklightConsole">
<test realm="WorklightConsole" isInternalUserID="true"/>
</customSecurityTest>
...
</securityTests>
server.xml
<httpEndpoint id="defaultHttpEndpoint"
host="*"
httpPort="9080"
httpsPort="9443" >
<tcpOptions soReuseAddr="true"/>
</httpEndpoint>
<!-- Declare the JNDI properties for the IBM Worklight Console. -->
<jndiEntry jndiName="worklight/publicWorkLightProtocol" value='"http"'/>
<jndiEntry jndiName="worklight/publicWorkLightPort" value='"9080"'/>
<jndiEntry jndiName="worklight/serverSessionTimeout" value='"10"'/>
Any idea on how to solve this port mismatch issue ? Env is WL 6.0 on Liberty
P.S: Later on we plan to user LDAP instead. For now, having the username/passaword in the properties would suffice.
In worklight.properties, uncomment the #publicWorkLightPort=10080 property and change it to 9080.
worklight.properties is part of the Worklight project's .war file that is deployed into the application server; the application server may have its own port number set in server.xml, but the .war (project) has some specific properties of its own.
During my testing I encountered the same issue, but once uncommenting and changing the port value in worklight.properties, the re-direct then worked properly after logging-in.

Active MQ JMX SSL

I'm trying to use SSL with the JMX connector that Active MQ creates, but with no success. I'm able to get SSL working with the JVM platform JMX connector, but that requires storing keystore and truststore passwords plaintext, which is a no-go for our project.
Using the instructions here, I set up managementContext in activemq.xml as follows:
<managementContext>
<managementContext createConnector="true">
<property xmlns="http://www.springframework.org/schema/beans" name="environment">
<map xmlns="http://www.springframework.org/schema/beans">
<entry xmlns="http://www.springframework.org/schema/beans"
key="javax.net.ssl.keyStore"
value="${activemq.base}/conf/keystore.jks"/>
<entry xmlns="http://www.springframework.org/schema/beans"
key="javax.net.ssl.keyStorePassword"
value="${keystore.password}"/>
<entry xmlns="http://www.springframework.org/schema/beans"
key="javax.net.ssl.trustStore"
value="${activemq.base}/conf/truststore.jks"/>
<entry xmlns="http://www.springframework.org/schema/beans"
key="javax.net.ssl.trustStorePassword"
value="${truststore.password}"/>
</map>
</property>
</managementContext>
</managementContext>
This section seems to be completely ignored when the connector starts up. I can connect without credentials. I also tried using username and password authentication instead of ssl for JMX, as seen here, and that worked fine.
Has anyone seen this before? Any ideas? Thanks!
Have you enabled jmx ssl in the activemq launch scripts? On windows in the activemq-admin or activemq batch files, uncomment and modify the SUNJMX settings.
JMX authentiation is independent of whether ssl is used. It is controlled by the authenticate attribute. By default it will use the jmx access files in your jre, so re-point them with the system properties shown below. You may get an error message stating that the files themselves must be access controlled, so set them with chmod on unix or cacls on windows. I would suggest even turning off the ssl and getting the authentication to work first. You can test with jconsole with a remote connection to confirm that it wants credentials. Then follow-up with the ssl stuff.
set SUNJMX=-Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=1199 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=true -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.password.file=%ACTIVEMQ_BASE%/conf/access/jmx.password -Dcom.sun.management.jmxremote.access.file=%ACTIVEMQ_BASE%/conf/access/jmx.access
I had the same issue regarding the ActiveMQ SSL configuration (keystore & password) in the XML not working.
My requirement was to enable remote JMX monitoring of ActiveMQ with SSL and authentication through a firewall.
I resolved it using a custom JMX connector (via a Java Agent), rather than using the JMX connector that Active MQ creates.
see: JMX connectivity through a firewall for an example (JMXAgent.java)
The important entries for configuring SSL in the JMXAgent.java are:
Map<String, Object> env = new HashMap<String, Object>();
SslRMIClientSocketFactory csf = new SslRMIClientSocketFactory();
SslRMIServerSocketFactory ssf = new SslRMIServerSocketFactory();
env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf);
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);
You can also specify your authentication files in the env Map:
env.put("jmx.remote.x.password.file", System.getProperty("password.file","<default_path>"));
env.put("jmx.remote.x.access.file", System.getProperty("access.file","<default_path>"));
The Java Agent needs to be compiled and put into a jar with a valid manifest file as described here
Add the following to the activemq launch configuration (depending on activemq version/ environment and run ActiveMQ:
-javaagent:<full_path_to_agent_jar_file> \
-Dpassword.file=<full_path_to_jmx.password_file> \
-Daccess.file=<full_path_to_jmx.access_file> \
-Djavax.net.ssl.keyStore=<full_path_to_keystore_file> \
-Djavax.net.ssl.keyStorePassword=<password>
You should then be able to connect through jconsole (with correct security parameters)
The remote JMX connection URL will be something like:
service:jmx:rmi://<host>:<rmi_server_port>/jndi/rmi://<host>:<port>/jmxrmi
Note - ports can be configured in the Java Agent.

HTTPS and RESTEasy

Is there a way within the RESTEasy configuration (using 2.*) or jax-rs to not allow http access to any REST based web services? I want to only serve the web service end points under https.
In tomcat its done in on a per port basis. There looks to be 3 steps to setting this up.
1) Creating the KeyStore file. I used java to gen this command is as follows
Keytool –genkey –alias presto –keypass prestoAdmin –keystore presto.bin –storepass prestoAdmin
Copy the presto.bin file into the webapps dir of tomcat
2) Setting up server.xml for tomcat
<Connector port=”PORT_TO_BE_SCURED” maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile../webapps/presto.bin " keystorePass="prestoAdmin"
clientAuth="false" sslProtocol="TLS"/>
3) Configuring the web service to use the secured connection. Add the following to web.xml
<security-constraint>
<web-resource-collection>
<web-resource-name>securedapp</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
I pulled this from http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html
Here's how I did this:
HttpServletRequest httpServletRequest =
ResteasyProviderFactory.getContextData(HttpServletRequest.class);
HttpServletResponse httpServletResponse =
ResteasyProviderFactory.getContextData(HttpServletResponse.class);
if (!httpServletRequest.isSecure())
{
try
{
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Use HTTPS");
}
catch (IOException e)
{
throw new WebApplicationException(e);
}
}
This is pure-RESTEasy solution and you can place this code anywhere before handling request.
I used tapestry-resteasy integration and implemented this using tapestry service advisors.
I believe that this configuration should not be at RESTEasy side, but rather at servlet container or web server.
For example if you run Tomcat, in server.xml remove connector from 8080 port and define one for 8443 port. So Tomcat won't accept the http traffic anymore.