Trouble with securing a Tomcat/Axis2 web service via SSL - java

I have successfully built a working web service, client, and .jsp-driven UI. I did this with Eclipse, Axis2, Tomcat 7, and Java 7.
The basic flow is that the user visits the .jsp and submits a form with input data. The JSP forwards the "request" object to the Java client. The Java client consumes the web service and submits the user input. The service connects to a SQL Server database via JDBC to retrieve information, which is displayed back to the user.
This all works perfectly over HTTP, but now I want to secure the process, and this is where I'm running into problems. I'm able to create a cert and get Tomcat to use it. I can connect to the web UI via HTTPS and submit the form and get data back just fine. The problem is that this is only securing the front-end. The web service client code is still connecting to the service via HTTP in the background.
According to this page, all I really need to do to enable my service for connections via SSL is to update the axis2.xml file and include a new "transportReceiver" node for HTTPS. I did that and regenerated my client code to use the secure endpoint. It doesn't work.
I have configured Tomcat to listen on ports 8081 for http and 8443 for https. But after changing axis2.xml to match, and starting up Tomcat, I get the following:
[INFO] Listening on port 8443 [ERROR] Terminating connection
listener
org.apache.axis2.transport.http.server.DefaultConnectionListener#16d60567
after 10retries in 0 seconds. java.net.BindException: Address already
in use: JVM_Bind at java.net.DualStackPlainSocketImpl.bind0(Native
Method) at java.net.DualStackPlainSocketImpl.socketBind(Unknown
Source) at java.net.AbstractPlainSocketImpl.bind(Unknown Source) at
java.net.PlainSocketImpl.bind(Unknown Source) at
java.net.ServerSocket.bind(Unknown Source) at
java.net.ServerSocket.(Unknown Source) at
java.net.ServerSocket.(Unknown Source) at
org.apache.axis2.transport.http.server.DefaultConnectionListener.run(DefaultConnectionListener.java:80)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
I have tried changing the port number in axis2.xml (for example, to 8445), and that sort of works. The server is able to start cleanly, but eventually, the same errors start showing up. For example, when I retrieve the WSDL, I see the error via the console (though the WSDL does show up). Also, if I try to actually use the service when on port 8445, I get the following error:
org.apache.axis2.AxisFault: Connection has been shutdown:
javax.net.ssl.SSLException: Unrecognized SSL message, plaintext
connection?
I can only assume this is because Tomcat is configured to handle HTTPS on 8443, not 8445, but I honestly don't know.
If I leave the port as 8443 and ignore the errors at startup, I get the following message when I connect to the service:
org.apache.axis2.AxisFault: Connection has been shutdown:
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
I followed these steps to try to get it to recognize my certificate, but when importing it into my JRE7 keystore, I get the following:
keytool error: java.lang.Exception: Certificate reply and certificate
in keystore are identical
Basically, that cert is already there. Which makes sense, because it's the one that Tomcat is already using successfully.
So, I'm pretty clueless at this point. I'm really not sure what I'm supposed to be doing. Any general guidance, or a link to a step-by-step how-to would be really helpful.
But for a specific question... What, exactly, am I doing when I set the transportReceiver nodes in axis2.xml? Am I telling it what ports Tomcat is running on and that it should use, or does Axis2 have its own servers that will start on those ports? It seems to be the latter, but that doesn't make a whole lot of sense to me.

The correct way to configure the servlet transport is described in the Axis2 documentation. The symptoms you are describing suggest that you have a transportReceiver that refers to org.apache.axis2.transport.http.SimpleHTTPServer. Please also make sure that you use a recent version of Axis2 (1.5.6 or 1.6.1).

See Andreas's response for the port issue. As for the certificate issue, it was a misunderstanding on my part over the distinction between a keystore and truststore. The JVM defaults to using JAVA_HOME\lib\security\cacerts as its trust store, rather than the USER_HOME\.keystore file. Once I imported my certificate there, the errors went away.
I was also able to resolve the certificate problems by explicity setting the trust store to be the key store file, via code. I did this before importing the certificate into the cacerts store. It goes in the client code, right before invoking the service:
System.setProperty("javax.net.ssl.trustStore","C:\\path\\to\\.keystore");
System.setProperty("javax.net.ssl.trustStorePassword","password");

One of the way to connect to HTTPS is using jax-ws
E:\WSDL>wsimport -keep -p com.mypack.webservice https://domain:port/ws/MyService?wsdl
This will generate package structure under the wsdl folder. Use it.
Now all you have to do is put trust store in run.bat or use System class to set property.
It will work fine.

Related

How do I fix this SSL error when making a SQL request from a jetty server

I'm in the process of upgrading a java application. Originally, the application was built with jdk 8 and the server was jetty 9. Since upgrading to jetty 10 and jdk 11, I'm running into an issue when trying to make requests to our sql datasource. When the application attempts to query the database, it fails with the error:
java.sql.SQLException: Cannot create PoolableConnectionFactory
(The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption. Error: "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target". ClientConnectionId:redacted)
at org.apache.commons.dbcp2.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:653)
at org.apache.commons.dbcp2.BasicDataSource.createDataSource(BasicDataSource.java:531)
at org.apache.commons.dbcp2.BasicDataSource.getConnection(BasicDataSource.java:731)
at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:181)
I understand that either the jetty server or the sql server are missing a security certificate, but I'm not sure what to do about it. I read through the documentation here:
https://www.eclipse.org/jetty/documentation/jetty-10/operations-guide/index.html#og-keystore but the sql server is a remote server that I don't have admin access to, so I'm not sure I can even do what they describe. Do I need to get the certificate and key from the sql server somehow? What am I missing here?
Your program is failing because it is trying to connect to an "unsecure" location, you have to connect to the destination manually, get the certificate/or certificates, install them into your local keystore, and restart your program.
Some instructions on how to do that can be gound here.
https://www.thesslstore.com/knowledgebase/ssl-install/jetty-java-http-servlet-webserver-ssl-installation/
If you connect to multiple locations, yes, you need to have an entry for each, unless that they have a wildcard certificate (a certificate that applies for all of them).
Alternatively! (not sure but handy) you can start the connection without enforcing certificate validation, in this case the connection will happen no matter is the destination is actually false, this is a security issue, but there are cases when this is needed, in fact, this happens a lot when you run balancers where they validate the certs for you and you connect in http to them, automatically the validation is getting dropped since most of those balancers won't fail to connect even if the validation fails, but this is a different topic!
Cheers.

HTTPS (SSL) connections issue in Codename One

I'm now stuck with a HTTPS/SSL issue. I'm developing on Windows 10 / Java 8 v121 on Codename One.
When I tried to call a HTTP (without SSL) connection, I get rejected with a reference to [https://www.codenameone.com/blog/ios-http-urls.html]. I don't think the call even hit the server. I tried to include the "build hint" in my codenameone_settings.properties file but to no avail.
Next, I tried to use self-signed certificate and it generated a "Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target" exception. So, I thought the error was caused because my certificate was self-signed, but it wasn't...
Then I got a free certificate from [https://www.sslforfree.com/] and it is still causing the SAME exception message. However, this cerficate works fine with my Firefox browser. I'm also using Simple DNS Plus (for Windows) for the signed certificate domain name tested to work correctly in my Firefox browser (ie. correctly hit the server with no Insecure Connection message).
I'd like to understand how to:-
Get the "build hint" to work for iOS (in the Codename One simulator) so it calls http (without ssl) connections.
How to resolve the Java exception.
Thanks!
Just use http URL during the development stage and when you are ready to publish, buy a genuine SSL certificate and change the http to https. It's for your own good, as unencrypted Webservice call will make your app vulnerable to a man-in-middle attack.
To make your http work on iOS during dev stage, add this build hint:
ios.plistInject=<key>NSAppTransportSecurity</key><dict><key>NSAllowsArbitraryLoads</key><true/></dict><key>CFBundleURLTypes</key><array><dict><key>CFBundleURLName</key><string>com.mycompany.myapp</string></dict><dict><key>CFBundleURLSchemes</key><array><string>MyApp</string></array></dict></array>
There was a similar question previously which I answered here.

java web service client trying to access web service through SSL - TrustManagerFactoryImpl is not initialized

I'm a web service client and I'm connecting to the web service through SSL.
It's a 2-way SSL and the producer has shared the certificate. I did run the InstallCert.java, got the alias and created a Keystore.
I'm using weblogic application server and I have placed my Keystore in it.
Now when I run it, I'm getting an error,
Caused by: java.lang.RuntimeException: java.lang.IllegalStateException:
TrustManagerFactoryImpl is not initialized
Before this I could see that it is trying to load the identity certificate and the private key. But as per standards the producer isn't willing to share the private key with us.
Any suggestion on this would be of great help to me. Thanks.
I resolved it.
I added my .cer file to cacerts which is referred by the weblogic server (Using keytool import). In the keystores section, I kept the default option (Demo identity and Demo trust). In the SSL section, I went to advanced, click on the checkbox ("Use JSSE SSL").
It worked.
I had this issue connecting to a MySQL database that requires SSL to connect.
It turns out, for me, the driver version needed to be updated in order to properly-handle the trust store configuration in the JDBC URL.

Java settings for Fiddler examination of outgoing SOAP requests?

I am trying to debug my SOAP requests going to a third party web service. My client side code (the code running on my local machine) was generated using wsimport. I am trying to configure Fiddler to intercept my outgoing SOAP requests but have had no luck. Here is what I have done so far:
WinINET LAN settings pointed to localhost (127.0.0.1:8888) with Fiddler running and capturing traffic. I have HTTPS requests being decrypted by Fiddler since my web service is using HTTPS. At this point my requests to the 3rd party URL do not appear at all. I receive a stack trace error telling me the web service cannot process request due to an internal error (this is why I am debugging my request - since it is third party I do not have access to the web service logs).
I then added the following code to my method that calls the web service:
System.setProperty("http.proxyHost", "127.0.0.1");
System.setProperty("https.proxyHost", "127.0.0.1");
System.setProperty("http.proxyPort", "8888");
System.setProperty("https.proxyPort", "8888");
When I execute my code with this included, I receive the following errors:
org.apache.jasper.JasperException: javax.xml.ws.WebServiceException: Failed to access the WSDL at: <wsdl_url>. It failed with:
Got sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target while opening stream from <wsdl_url>.
BUT in Fiddler I get an entry for the request that seems to not complete (sorry - new to Fiddler). The host is listed as "Tunnel to" and it is greyed out. In the Fiddler log I get this error:
!SecureClientPipeDirect failed: System.Security.Authentication.AuthenticationException A call to SSPI failed, see inner exception. < An unknown error occurred while processing the certificate on pipe to (CN=<wsdl_base_url>, O=DO_NOT_TRUST, OU=Created by http://www.fiddler2.com).
To me, it seems that I have the configuration correct for Fiddler and my proxy, but I am missing something in Java. I can't help but feel that I am missing a critical step here, so any advice would be greatly appreciated. Thanks!
EDIT: Do I need to generate new Java code using wsimport with the -httpproxy option set?
You need to add Fiddler's root certificate to the Java Key Store. Java does not use the system's certificate store, so the fact that Fiddler puts itself there doesn't solve the problem for you.
Get Fiddler's certificate by clicking Export Root Certificate from inside Tools > Fiddler Options > HTTPS. Then import the cert: http://azure.microsoft.com/en-us/documentation/articles/java-add-certificate-ca-store/#to-add-a-certificate-to-the-cacerts-store

JSE 1.8, Sandbox Java Applet loaded over HTTPS, but crossdomain.xml retrieved using HTTP

Hi all Java/Applet gurus,
I've stumbled upon an interesting problem with the latest JDK build (1.8.0_b26).
When running Sandbox Java Applet with the latest JDK, from within Java code we try to connect back to the server with a different protocol - instead of original HTTPS we use WSS (secured Websockets connection, we use third party Websockets Client Java library). As the result, JVM tries to retrieve crossdomain.xml file from the server. The problem is, that the file is retrieved using HTTP (and not HTTPS) protocol.
For example, in our case the server IP is 192.168.1.1, the applet is loaded over HTTPS default port (443). Using trace level 5 in Java console we see that the crossdomain.xml is retrieved from http://192.168.1.1:443. And of course it doesn't work because the server listens only for HTTPS connections on port 443 (and not HTTP).
On the other hand, when we use HTTP protocol and open new WS (unsecured Websockets connection) to the server, the problem doesn't appear, because crossdomain.xml is retrieved from http://192.168.1.1:80 and it is completely correct.
As the problem was further investigated, we've made few more observations:
It is possible to provide alternative location of crossdomain.xml file using jnlp.altCrossDomainXMLFiles Java VM parameter. We've never succeed to make this parameter work for us though (tried both in java_arguments list and as lone applet parameter). The possible reason might be that the parameter should be used only with Webstart application (although it is not written specifically in specs).
While establishing Websockets connection, the connection stack trace is as follows:
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:790) at
sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647) at
sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:787) at
sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647) at
sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1534)
at
sun.net.www.protocol.http.HttpURLConnection.access$200(HttpURLConnection.java:90)
at
sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1431)
at
sun.net.www.protocol.http.HttpURLConnection$9.run(HttpURLConnection.java:1429)
at java.security.AccessController.doPrivileged(Native Method) at
java.security.AccessController.doPrivileged(AccessController.java:713)
at
sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1428)
at com.sun.deploy.net.CrossDomainXML.check(Unknown Source) at
com.sun.deploy.net.CrossDomainXML.check(Unknown Source) at
sun.plugin2.applet.SecurityManagerHelper.checkConnectHelper(Unknown
Source) at
sun.plugin2.applet.AWTAppletSecurityManager.checkConnect(Unknown
Source) at
sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:624)
So we looked at the latest publicly available source code of CrossDomainXML.java class (back from 2010 though). And from the code it is evident, that http connection is always used while retrieving crossdomain.xml file from server, regardless what is the original browser connection.
So the questions are:
Might it be a JDK bug or the strict usage of HTTP for crossdomain.xml is by design?
Is jnlp.altCrossDomainXMLFiles JVM parameter supported inside Sandbox applet?
Is there a way access the latest version of com.sun.deploy.net.CrossDomainXML.java source code to get a full picture of what is going on?
Thank you very much in advance.
Best regards,
Mark
in order to get rid of the http://myhost/crossdomain.xml request, there is nothing you can do except adding something like this into your java.policy file:
permission java.net.SocketPermission "myhost:1024-", "connect, resolve";
You can restrict that to a specific certificate signer in order to enforce this policy, see https://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html#SocketPermission
We use it like this in an applet early in the init-process (applet constructor) and it works:
try
{
System.setProperty("jnlp.altCrossDomainXMLFiles", //
"http://www.some-domain.de/crossdomain.xml" //
+ ",https://www.secure-domain.de:8443/crossdomain.xml" //
);
}
catch (Exception e)
{
e.printStackTrace();
}

Categories

Resources