6

As far as I understand in the majority of cases this exception states, that the certificate owner CN (common name) does not match the host name in the url. However in my case they do match, but the exception still raises..

The remote server certificate hierachy is:

  1. a self signed certificate with CN=sms.main.ru
  2. a certificate signed with the first one and CN=client.sms.main.ru

My java client is launched under apache-tomcat 6 and tries to connect to https://client.sms.main.ru/ and the following exception is thrown:

No name matching client.sms.main.ru found

Both certificates are added to $JAVA_HOME/jre/lib/security/cacerts via $JAVA_HOME/bin/keytool as shown in How do you configure Apache/Tomcat to trust internal Certificate Authorities for server-to-server https requests in the answer by unixtippse.

The Java code is quite trivial:

URL url = new URL(strurl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Connection", "close");
con.setDoOutput(true);
con.connect();

What am I missing?


Interesting thing is that when I try to access this url with a browser on a Windows PC, it says that the certificate is not trusted, I add it to the browser exception list and it works fine. So it looks like I added these certificates to cacerts incorrectly, so that java cannot use them. But I can easily find them by alias or by CN with:

$JAVA_HOME/bin/keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts | less
Community
  • 1
  • 1
horgh
  • 17,918
  • 22
  • 68
  • 123
  • if it is self-signed then it will not be trusted as the hierarchy will not go all the way up to a TA – Scary Wombat Nov 07 '14 at 02:10
  • @Scary Wombat But I added them both to the `cacerts`..and marked them there as trusted..Is that not enough? Is this not the way one should do to make a self-signed certificate be trusted? – horgh Nov 07 '14 at 02:13
  • You are using the wrong class to access https – CharlieS Nov 07 '14 at 02:20

3 Answers3

7

In the end all I had to do was:

  1. Disable hostname verification:

    // Create all-trusting host name verifier
    HostnameVerifier allHostsValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };
    // Install the all-trusting host verifier
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    
  2. Make sure that the default cacerts file is used, as while researching I tried to disable ssl certificate verification and used code which can be found in a great number of threads on SO (implementing own X509TrustManager), i.e. in Java: Overriding function to disable SSL certificate check. This code replaced default SSLSocketFactory, which I needed. So I had to delete all this stuff and use code disabling hostname verification only.

I left HttpURLConnection. No need to replace it with HttpsURLConnection.

Definitely it would be much better if I managed to avoid disabling hostname verification as well, but I couldn't. Probably something wrong with the certificates..

Community
  • 1
  • 1
horgh
  • 17,918
  • 22
  • 68
  • 123
  • didn't downvote, but disabling hostname verification is almost always the wrong thing to do. It shouldn't be recommended as a solution. – eis May 08 '17 at 06:35
1

This is previously how I have trusted self signed certs on tomcat

static private SSLSocketFactory newSslSocketFactory(String jksFile, char[] password) {

    try {
        KeyStore trusted = KeyStore.getInstance("JKS");
        InputStream in = StaticHttpsClient.class.getClassLoader().getResourceAsStream(jksFile);
        try {
            trusted.load(in, password);
        } finally {
            in.close();
        }

        SSLSocketFactory socketFactory = new SSLSocketFactory(trusted);
        HostnameVerifier hostnameVerifier =    org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
        socketFactory .setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
        return socketFactory;
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

static protected ClientConnectionManager createClientConnectionManager(String jksFile, char[] password) {
    SchemeRegistry registry = new SchemeRegistry();

    registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
    registry.register(new Scheme("https", 443, newSslSocketFactory(jksFile, password)));
    return new SingleClientConnManager(registry);

}   
Scary Wombat
  • 44,617
  • 6
  • 35
  • 64
-5

Use an HttpsURLConnection and SSLSocketFactory to perform the SSL handshake.

CharlieS
  • 1,432
  • 1
  • 9
  • 10
  • How is this supposed to help me? – horgh Nov 07 '14 at 02:13
  • Try researching `HttpsURLConnection`. You are using the wrong class to perform SSL, which is how the certificate is presented to the server. Your code does not do anything with a certificate. It will if you use the classes I suggested. – CharlieS Nov 07 '14 at 02:19
  • Thanks for marking it down, not a good way to encourage help! – CharlieS Nov 07 '14 at 02:19
  • Probably it's wrong, but it does work when a real trusted certificate is used by the target server...However, thanks, I'll think it over as well – horgh Nov 07 '14 at 02:23