2

I have a Java application that will need to connect to a SSL server. That server provides a proprietary certificate that is not automatically recognized by the Java cacert.

so I have 2 solutions:

a) import manually the certificate (.der, .pem etc.) in my client's java local keystore b) implement a trust manager that trusts all certificates

based on:

    // prepare Verifiers that will accept certificate from ANY host
    yesHostnameVerifier = new CYesHostnameVerifier();

    // initiate the SSL context and prepare trust manager that will accept ANY certificate
    TrustManager yesTrustManager    = new CYesTrustManager();
    TrustManager[] yesTrustManagers = new TrustManager[] {yesTrustManager};
    try {
        SSLContext yesSslcontext = SSLContext.getInstance("TLS");
        yesSslcontext.init(null, yesTrustManagers, null); // key manager, trust manager, secure random manager
        yesSslFactory = yesSslcontext.getSocketFactory();
    } catch (Exception e) {
    }   

    // configure the HTTPS connection
    HttpsURLConnection.setDefaultSSLSocketFactory(yesSslFactory);
    HttpsURLConnection.setDefaultHostnameVerifier(yesHostnameVerifier);

and

public class CYesTrustManager implements X509TrustManager {
    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
    }

    @Override
    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}

    @Override
    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}

}

and

public class CYesHostnameVerifier implements HostnameVerifier {

    @Override
    public boolean verify(String arg0, SSLSession arg1) {
        return true;
    }
}

In my opinion:

  • a) is impossible because I can't oblige all the clients to manually do that
  • b) is totally unsecure in production contexts and prone to man-in-the-midle attacks so, I don't want to accept ALL certificates.

Instead, what I would like is to do all this programmatically: When an exception is thrown because of invalid certificate: 1) get the server certificate from the SSL session 2) get the main useful information from this certificate and show this to the user (thru Swing) 3) ask the user if he accepts this certificate 4) if yes, import the certificate in the local keystore All this programmatically.

This way, if one day the certificate is tampered at least a new message will be displayed to the user hence making it a bit suspect. This would work pretty similarly to a standard ssh session at the end. Good idea? bad idea?

Would someone have some sample code for 1) 2) and 4)

DaveG
  • 127
  • 2
  • 10
  • what do you think of just modifying the CYesHostnameVerifier to return true only for the known server name? – DaveG May 07 '15 at 09:50
  • Why not use solution 3 - saving the certificate in a keystore and load it in your app. See answer of question: [How do I accept a self-signed certificate with a Java HttpsURLConnection?](http://stackoverflow.com/questions/859111) – Robert May 07 '15 at 09:51
  • You mean when an exception is thrown because of invalid certificate, my app would retrieve (i.e. in it's configuration) the certificate (i.e. .pem) and would import it automatically? is it possible only on a dedicated keystore? not possible in the stadard cacert? – DaveG May 07 '15 at 10:00
  • You are absolutely correct about (b), but you need to revise your opinion about (a). What you're proposing isn't in practice any more secure than (b). The client wants to use the service so he will accept whatever dialogs you put in front of him. PKI certificate trust is essentially an off-line process. You can't implement your way around that. – user207421 May 07 '15 at 10:38
  • @EJP I tend to agree with that but itsn't it what happens with a standard ssh connection (through putty or any other ssh client)? – DaveG May 07 '15 at 10:44
  • @DaveG: No when you apply the linked solution, the certificate from the keystore will be considered as trusted in your app if you use the created sslFactory. You can set the socketfactory as default one: `HttpsURLConnection.setDefaultSSLSocketFactory(factory);` – Robert May 07 '15 at 10:51
  • Sorry Robert, I don't understand. Can you ellaborate? let's imagine I have a virgin computer with Java & a default cacert. Now, I start my app that connects through https to an external service (let's call it JIRA). Currently I get an exception because the certificate on the JIRA server is not authentified on my local keystore (maybe self-signed). Let's assume I can't change the certificate on the JIRA server because it's not mine... what do you think is the best way to handle it knowing that I want to ovoid my users to import manually a certificate in their keystore? – DaveG May 07 '15 at 14:52
  • Just to let all know what I finally did: in my app's server settings, we can set the certificate X.509. When the client app starts, it create its own private truststore in a temp folder, the certificate is imported and this truststore is used to connect to any SSL service. – DaveG Oct 15 '15 at 22:29
  • SOLUTION: Actually I think this was really the best way to go. Provide a mechanisme so that the admin of the application can import the certificates of the services he trust. – DaveG Jan 20 '16 at 11:04

0 Answers0