3

I have a Javafx application that sends GET and POST requests to a secure web service via HTTPS. The SSL setup on the server hosting the webservices is one-way ssl, i.e. the Javafx application validates the server's identity but the server does not validate the thick client's identity.

The application server is behind an F5 that has the certificates(signed by an external authority).

For a browser this would not have been a problem as the browser itself handles validating the server's identity and displays the relevant warning to the user. But for a thick client, I am not sure how to validate the server's identity before sending the request. Please let me know how to handle this in a Javafx application.

I did ask a question relating to this earlier here and here, but those did not help. So, please pardon my limited knowledge on this topic.

Any help would be appreciated.

Community
  • 1
  • 1
Aspirant
  • 1,934
  • 4
  • 25
  • 44
  • if server certificate issued by known (to your java version) CA and _valid_ (it includes various aspects: PKIX chain, dates not before-not after, CN, extensions, etc) everything should work without any additional code. Plain HttpsUrlConnection will do the job. – user1516873 Oct 31 '13 at 15:39
  • And HttpsUrl connection will check everything, so you cannot connect by https protocol to site with invalid certificate. In fact, if you have old java version with old root CA certificates, you cannot even connect to site with valid certificate, but issued with new CA, because java hasn't it in its internal keystore. – user1516873 Oct 31 '13 at 15:46
  • If you want to read more about ssl/https and how it works, i recommend this article http://www.zytrax.com/tech/survival/ssl.html – user1516873 Oct 31 '13 at 15:52
  • Thank you very much for the response [user1516873](http://stackoverflow.com/users/1516873/user1516873). I was hoping to have this work for a server with a valid certificate but somehow I receive exceptions relating to PKIX. So, I ended up ignoring all certificates as described [here](http://www.mkyong.com/webservices/jax-ws/java-security-cert-certificateexception-no-name-matching-localhost-found/) and [here](http://www.nakov.com/blog/2009/07/16/disable-certificate-validation-in-java-ssl-connections/). :) – Aspirant Oct 31 '13 at 17:07
  • Sure you can disable validation at all, but i strongly advice you not to do so. PKIX related problem usually occurs when server uses self-signed or invalid certificate. You said server certificate issued by external authority, can you hit server with browser? – user1516873 Oct 31 '13 at 17:22
  • I cannot hit it with the IP of the F5. But I am able to hit the server directly from the browser. – Aspirant Oct 31 '13 at 17:24
  • Do you have any security exception for this site? And can you add certificate chain information to the question? Issuer and CN fields would be enough. You can view it in browser. – user1516873 Oct 31 '13 at 19:41
  • I tried working with the DNS of the F5 and it did work on chrome and IE, but not on firefox and a simple java client. So, I have asked a new [question](http://stackoverflow.com/questions/19714470/pkix-path-validation-failed-java-security-cert-certpathvalidatorexception-path) in this regard. I will not be able to share the Issuer and CN fields here as these are proprietary to the organization I work for. But if the other question can help dig a little deep into the problem. For now I can tell you that the issur CN is: "ca", if that helps. Please let me know. – Aspirant Oct 31 '13 at 20:19

1 Answers1

5

If your certificate don't work in Firefox/java, most likely it issuer is unknown by Firefox/java.

How to make it work:

  1. Get full certificate chain of your server. You can do it with Firefox. View certificate -> details-> export to .pem file. In your case chain will contain at least 2 certificate (cerver cert and CA cert, CA possible self-signed or maybe not) Export CA certificate in .pem file.

  2. Now you can force java to trust that CA, it can be done in various ways, for example, you can add CA certificate in jre cacerts or create custom SSLContext for HttpsURLConnection.

  3. If you do DNS or etc.hosts modification, rollback it. Connection address should match with certificate CN, include wildcards.

  4. Use that code to connect to your server:

    public void test() throws Exception {
        URL u = new URL(
                "https://my-server.com/my-webservices/data");
        HttpsURLConnection http = (HttpsURLConnection) u.openConnection();
        http.setSSLSocketFactory(createSSLContext().getSocketFactory());
        http.setAllowUserInteraction(true);
        http.setRequestMethod("GET");
        http.connect();
    
        InputStream is = http.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder stringBuilder = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null)
        {
            stringBuilder.append(line
                    + "\n");
        }
        System.out.println(stringBuilder.toString());
    
    }
    
    private SSLContext createSSLContext() throws Exception {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        FileInputStream in = new FileInputStream("path_to_ca_file.pem");
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(null);
        try {
            X509Certificate cacert = (X509Certificate) cf.generateCertificate(in);
            trustStore.setCertificateEntry("ca", cacert);
        } finally {
            IOUtils.closeQuietly(in);
        }
    
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(trustStore);
    
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
        return sslContext;
    }
    
user1516873
  • 5,060
  • 2
  • 37
  • 56
  • 1
    Thank you for your response [user1516873](http://stackoverflow.com/users/1516873/user1516873). I am trying to avoid packaging the certificate to the local because when the certificate expires, then this would need an update. I think if the application always works with the certificate on the server, that woud be the best solution. Is there a way to achieve this? – Aspirant Nov 02 '13 at 22:05
  • 2
    Yes, application can work with any certificate it gets from server, but it eliminates only one kind of attack - sniffing. MITM http://en.wikipedia.org/wiki/Man-in-the-middle_attack and etc/hosts rewrites still can be applied to that kind of https. – user1516873 Nov 05 '13 at 06:49
  • So can this be achieved by just not setting the SSLSocketFactory as you did in the code above? Thanks. `http.setSSLSocketFactory(createSSLContext().getSocketFactory());` – Aspirant Nov 05 '13 at 14:03
  • 2
    It depends of what certificate you set up to server. In common, this can be achieved by setting all-trust SSL context. You already know how to do this, [for example](http://www.nakov.com/blog/2009/07/16/disable-certificate-validation-in-java-ssl-connections/) But obtaining certificate from well known authority (VeriSign, GeoTrust, etc) is better. As disadvantage, it will cost you some money on regular basis. As advantage, you will not bother with updating CA (java maintains actual CA list) – user1516873 Nov 05 '13 at 16:06
  • 1
    Thanks [user1516873](http://stackoverflow.com/users/1516873/user1516873). You have been of great help in clearing my confusion. I hope this thread can be of value to other users as well. – Aspirant Nov 05 '13 at 16:10